Fixed a few problems, added a debugging configuration variable
[theodwalha.git] / source / request.cpp
blob1559f114a0af5c377019e01438505e0c6fb73c05
1 #include <ail/string.hpp>
2 #include <boost/foreach.hpp>
3 #include <theodwalha/request.hpp>
4 #include <iostream>
6 namespace
8 std::string const delimiter = "\r\n";
9 std::string end_of_header = delimiter + delimiter;
12 quality_entry::quality_entry()
16 quality_entry::quality_entry(std::string const & name):
17 name(name),
18 quality_value(1.0f)
22 quality_entry::quality_entry(std::string const & name, float quality_value):
23 name(name),
24 quality_value(quality_value)
28 http_request::http_request():
29 has_content_type(false),
30 has_content_length(false),
31 keep_alive(true),
32 gzip(false),
33 content_length(0)
37 bool decode_string(std::string const & input, std::string & output)
39 for(std::size_t i = 0, end = input.size(); i < end; i++)
41 char byte = input[i];
42 if(byte == '%')
44 i++;
46 std::size_t const hex_length = 2;
48 std::size_t remaining_bytes = end - i;
49 if(remaining_bytes < hex_length)
50 return false;
52 std::string hex_string = input.substr(i, hex_length);
54 char hex_byte;
55 if(!ail::string_to_number<char>(hex_string, hex_byte, std::ios_base::hex))
56 return false;
58 output.push_back(hex_byte);
60 else
61 output.push_back(byte);
63 return true;
66 bool parse_quality_entries(std::string const & input, quality_entries & output, std::string & error_message)
68 string_vector argument_tokens = ail::tokenise(input, ",");
69 BOOST_FOREACH(std::string token, argument_tokens)
71 string_vector quality_tokens = ail::tokenise(ail::trim(token), ";q=");
72 switch(quality_tokens.size())
74 case 1:
75 output.push_back(quality_entry(quality_tokens[0]));
76 break;
78 case 2:
80 float quality_value;
81 if(!ail::string_to_number(quality_tokens[1], quality_value))
83 error_message = "Failed to parse quality value";
84 return false;
86 if(quality_value < 0.0f || quality_value > 1.0f)
88 error_message = "Quality value out of range";
89 return false;
91 output.push_back(quality_entry(quality_tokens[1]));
92 break;
95 default:
96 error_message = "Invalid quality value token count";
97 return false;
100 return true;
103 process_header_result::type process_header(std::string const & input, http_request & output, std::string & error_message)
105 using namespace process_header_result;
106 using namespace http_request_method;
107 using namespace http_protocol;
108 using namespace http_form_content;
110 std::size_t offset = input.find(end_of_header);
111 if(offset == std::string::npos)
112 return no_delimiter;
114 output.header_size = offset + end_of_header.size();
116 std::string header = input.substr(0, offset);
117 string_vector lines = ail::tokenise(header, delimiter);
118 if(lines.empty())
120 error_message = "Invalid line count in the header";
121 return error;
124 string_vector tokens = ail::tokenise(lines[0], " ");
125 std::string const
126 & method_string = tokens[0],
127 & encoded_path = tokens[1],
128 & version_string = tokens[2];
130 if(method_string == "GET")
131 output.method = get;
132 else if(method_string == "POST")
133 output.method = post;
134 else
136 error_message = "Invalid method specified";
137 return error;
140 if(version_string == "HTTP/1.1")
141 output.protocol_version = version_1_1;
142 else if(version_string == "HTTP/1.0")
143 output.protocol_version = version_1_0;
144 else
146 error_message = "Invalid protocol specified";
147 return error;
150 if(!decode_string(encoded_path, output.path))
152 error_message = "Failed to decode path";
153 return error;
156 for(std::size_t i = 1, end = lines.size(); i < end; i++)
158 std::string const target = ": ";
159 std::string const & current_line = lines[i];
160 std::size_t offset = current_line.find(target);
161 if(offset == std::string::npos)
163 error_message = "Unable to detect colon in HTTP header line";
164 return error;
167 std::string name = current_line.substr(0, offset);
168 offset += target.size();
169 std::string argument = current_line.substr(offset);
171 if(name == "Content-Type")
173 if(argument == "application/x-www-form-urlencoded")
174 output.content_type = application_x_www_form_urlencoded;
175 else if(argument == "multipart/form-data")
176 output.content_type = multipart_form_data;
177 else
179 error_message = "Unknown content type specified";
180 return error;
182 output.has_content_type = true;
184 else if(name == "Content-Length")
186 if(!ail::string_to_number<std::size_t>(argument, output.content_length))
188 error_message = "Invalid content length specified";
189 return error;
191 output.has_content_length = true;
193 else if(name == "Cookie")
195 string_vector argument_tokens = ail::tokenise(argument, ";");
196 BOOST_FOREACH(std::string const & token, argument_tokens)
198 string_vector cookie_tokens = ail::tokenise(ail::trim(token), "=");
199 if(cookie_tokens.size() != 2)
201 error_message = "Failed to parse a cookie assignment";
202 return error;
205 std::string
206 cookie,
207 value;
210 !decode_string(cookie_tokens[0], cookie) ||
211 !decode_string(cookie_tokens[1], value)
214 error_message = "Failed to decode a cookie";
215 return error;
219 else if(name == "Accept-Encoding")
221 if(!parse_quality_entries(argument, output.accepted_encodings, error_message))
222 return error;
223 BOOST_FOREACH(quality_entry & entry, output.accepted_encodings)
224 if(entry.name == "gzip" && entry.quality_value > 0.0f)
225 output.gzip = true;
227 else if(name == "Connection")
229 string_vector argument_tokens = ail::tokenise(argument, ",");
230 BOOST_FOREACH(std::string token, argument_tokens)
232 token = ail::to_lower(ail::trim(token));
233 if(token == "close")
234 output.keep_alive = false;
239 return success;