1 /*
2 *
3 * C++ Portable Types Library (PTypes)
4 * Version 2.1.1 Released 27-Jun-2007
5 *
6 * Copyright (C) 2001-2007 Hovik Melikyan
7 *
8 * http://www.melikyan.com/ptypes/
9 *
10 */
11
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <limits.h>
15
16 #include "config.h"
17 #include "log.h"
18 #include "utils.h"
19 #include "sysutils.h"
20 #include "request.h"
21 #include "clients.h"
22 #include "modules.h"
23
24
25 USING_PTYPES
26
27
28 const char* http_version_str[HTTP_VER_MAX] = {"", "HTTP/1.0", "HTTP/1.1", "HTTP/1.1"};
29 // const char* http_method_str[HTTP_METHOD_MAX] = {"GET", "HEAD", ""};
30
31
request_rec(instm & isockin,outstm & isockout,ipaddress iclient_ip)32 request_rec::request_rec(instm& isockin, outstm& isockout, ipaddress iclient_ip)
33 : started(now()), rsp_code(0), stat(STAT_READ), sockin(&isockin), sockout(&isockout), client_ip(iclient_ip),
34 version(HTTP_VER_10), method(HTTP_GET), method_str(), keep_alive(false), if_modified(invdatetime),
35 req_line(), uri(), host(), referer(), partial(false), range_min(0), range_max(0), headers(),
36 url(), path_parts(), user(0),
37 location(), hdr_size(0)
38 {
39 }
40
41
reset_state()42 void request_rec::reset_state()
43 {
44 rsp_code = 0;
45 version = HTTP_VER_10;
46 method = HTTP_GET;
47 clear(method_str);
48 keep_alive = false;
49 if_modified = invdatetime;
50 clear(req_line);
51 clear(uri);
52 path_parts.clear();
53 clear(host);
54 clear(referer);
55 partial = false;
56 range_min = 0;
57 range_max = 0;
58 headers.clear();
59 urlclear(url);
60 delete user;
61 user = 0;
62 clear(location);
63 hdr_size = 0;
64 }
65
66
~request_rec()67 request_rec::~request_rec()
68 {
69 delete user;
70 }
71
72
put_header(const char * name,const char * value)73 void request_rec::put_header(const char* name, const char* value)
74 {
75 if (version > HTTP_VER_09)
76 {
77 sockout->put(name);
78 sockout->put(": ");
79 sockout->put(value);
80 sockout->put("\r\n");
81 }
82 }
83
84
put_header(const char * name,const string & value)85 void request_rec::put_header(const char* name, const string& value)
86 {
87 if (version > HTTP_VER_09)
88 {
89 sockout->put(name);
90 sockout->put(": ");
91 sockout->put(value);
92 sockout->put("\r\n");
93 }
94 }
95
96
put_content_type(const char * mime)97 void request_rec::put_content_type(const char* mime)
98 {
99 put_header("Content-Type", mime);
100 }
101
102
put_content_length(large length)103 void request_rec::put_content_length(large length)
104 {
105 if (method != HTTP_HEAD)
106 put_header("Content-Length", itostring(length));
107 }
108
109
end_headers()110 void request_rec::end_headers()
111 {
112 if (version > HTTP_VER_09)
113 sockout->put("\r\n");
114 hdr_size = sockout->tell();
115 if (method == HTTP_HEAD)
116 end_response();
117 }
118
119
begin_response(int code,const char * msg)120 void request_rec::begin_response(int code, const char* msg)
121 {
122 rsp_code = code;
123 stat = STAT_WRITE;
124
125 if (version > HTTP_VER_09)
126 {
127 sockout->putf("%s %d %s\r\n", http_version_str[version], code, msg);
128 put_header("Date", http_time_stamp(now(true)));
129 put_header("Server", SERVER_APP_NAME);
130 // put_header("Accept-Ranges", "bytes");
131
132 if (!isempty(location))
133 put_header("Location", location);
134
135 static const char* sconn[2] = {"close", "keep-alive"};
136 if (version < HTTP_VER_11)
137 put_header("Connection", sconn[keep_alive]);
138 else if (!keep_alive) // HTTP/1.1
139 put_header("Connection", "close");
140 }
141 }
142
143
std_response(bool conn_close,int code,const char * msg,const char * descr)144 void request_rec::std_response(bool conn_close, int code, const char* msg, const char* descr)
145 {
146 if (conn_close)
147 keep_alive = false;
148
149 // we need a memory stream to temporarily store the response
150 outmemory s(4096);
151 string smsg = msg;
152
153 // write out the standard response page in HTML format
154 // to the memory stream
155 s.open();
156 if (strlen(descr) != 0)
157 {
158 std_html_header(s, itostring(code) + ' ' + smsg);
159 s.put("<p>");
160 html_encode(s, descr);
161 s.put("</p>\n");
162 std_html_footer(s);
163 }
164
165 // send the response
166 begin_response(code, msg);
167 if (s.tell() > 0) // some responses do not return any content, e.g. 304
168 {
169 put_content_type("text/html");
170 put_content_length(s.tell());
171 }
172 end_headers();
173 if (s.tell() > 0)
174 sockout->put(s.get_strdata());
175
176 end_response();
177 }
178
179
std_response(bool conn_close,int code,const char * msg,const char * descr,const string & dparam)180 void request_rec::std_response(bool conn_close, int code, const char* msg, const char* descr, const string& dparam)
181 {
182 char buf[1024];
183 snprintf(buf, sizeof(buf), descr, pconst(dparam));
184 std_response(conn_close, code, msg, buf);
185 }
186
187
rsp_not_found()188 void request_rec::rsp_not_found()
189 {
190 std_response(false, 404, "Not found", "The requested object %s was not found on this server.", url.path);
191 }
192
193
rsp_bad_request()194 void request_rec::rsp_bad_request()
195 {
196 std_response(true, 400, "Bad request", "Your browser sent a request that this server could not understand.");
197 }
198
199
rsp_bad_method(const char * ok_methods)200 void request_rec::rsp_bad_method(const char* ok_methods)
201 {
202 put_header("Allow", ok_methods);
203 std_response(true, 405, "Method not allowed", "Method %s not allowed for this resource", method_str);
204 }
205
206
rsp_uri_too_long()207 void request_rec::rsp_uri_too_long()
208 {
209 std_response(true, 414, "Request-URI too long", "The request-URI string sent by your browser is too long.");
210 }
211
212
rsp_forbidden()213 void request_rec::rsp_forbidden()
214 {
215 std_response(false, 403, "Forbidden", "You don't have permission to access %s on this server", url.path);
216 }
217
218
rsp_dir_index_forbidden()219 void request_rec::rsp_dir_index_forbidden()
220 {
221 std_response(false, 403, "Directory index forbidden", "Directory index forbidden: %s", url.path);
222 }
223
224
rsp_redirect(const string & newurl)225 void request_rec::rsp_redirect(const string& newurl)
226 {
227 location = newurl;
228 std_response(false, 301, "Moved permanently", "The document has moved to %s", newurl);
229 }
230
231
rsp_overloaded()232 void request_rec::rsp_overloaded()
233 {
234 std_response(true, 503, "Service unavailable", "The server is overloaded. Please, try again later.");
235 }
236
237
rsp_not_modified()238 void request_rec::rsp_not_modified()
239 {
240 std_response(false, 304, "Not modified", "");
241 }
242
243
abort_request()244 void request_rec::abort_request()
245 {
246 #ifdef DEBUG
247 syslog_write(SYSLOG_WARNING, "Request from %s aborted", pconst(iptostring(client_ip)));
248 #endif
249 keep_alive = false;
250 throw ehttp(0);
251 }
252
253
end_response()254 void request_rec::end_response()
255 {
256 throw ehttp(rsp_code);
257 }
258
259
260 //
261 // request parsers
262 //
263
264 const cset method_chars = "A-Z";
265 const cset uri_chars = "~21-~FF";
266 const cset field_chars = uri_chars - cset(":");
267 const cset ws_chars = "~20";
268
269
get_token(const cset & chars)270 string request_rec::get_token(const cset& chars)
271 {
272 char buf[MAX_TOKEN];
273 int bufsize = sockin->token(chars, buf, sizeof(buf));
274 if (bufsize == 0 || bufsize >= MAX_TOKEN)
275 rsp_bad_request();
276 return string(buf, bufsize);
277 }
278
279
get_uri()280 string request_rec::get_uri()
281 {
282 char buf[MAX_REQUEST_URI];
283 int bufsize = sockin->token(uri_chars, buf, sizeof(buf));
284 if (bufsize == 0)
285 rsp_bad_request();
286 if (bufsize >= MAX_REQUEST_URI)
287 rsp_uri_too_long();
288 return string(buf, bufsize);
289 }
290
291
parse_method()292 void request_rec::parse_method()
293 {
294 while (!sockin->get_eof() && sockin->get_eol())
295 sockin->skipline();
296 method_str = get_token(method_chars);
297 req_line = method_str;
298
299 // try to pass this method to a registered method handler.
300 // the rest of the request line can be parsed using
301 // parse_request_line(), if it's HTTP/1.1-like.
302 handler_info* h = find_method_handler(method_str);
303 if (h != 0)
304 {
305 method_callback(h->callback)(*this);
306 // the handler must throw an ehttp exception
307 fatal(252, "Internal error 252");
308 }
309 // otherwise use the internal method handlers
310 else if (method_str == "GET")
311 method = HTTP_GET;
312 else if (method_str == "HEAD")
313 method = HTTP_HEAD;
314 else if (length(method_str) == 0)
315 abort_request();
316 else
317 rsp_bad_method("GET, HEAD");
318 }
319
320
parse_request_line()321 void request_rec::parse_request_line()
322 {
323 if (sockin->skiptoken(ws_chars) == 0)
324 abort_request();
325
326 // read the request URI
327 uri = get_uri();
328 req_line += ' ' + uri;
329
330 // read the version number, if present
331 if (sockin->get_eol())
332 version = HTTP_VER_09;
333 else
334 {
335 string s;
336
337 if (sockin->skiptoken(ws_chars) == 0)
338 abort_request();
339 s = get_token(uri_chars);
340 req_line += ' ' + s;
341 const char* p = s;
342 if (length(s) < 8 || strncmp(p, "HTTP/1.", 7) != 0)
343 rsp_bad_request();
344 if (p[7] == '0')
345 version = HTTP_VER_10;
346 else if (p[7] == '1')
347 version = HTTP_VER_11;
348 else
349 version = HTTP_VER_UNKNOWN; // 1.x is ok for us
350 }
351
352 // HTTP/1.1 requires to keep the connection alive by default;
353 // can be overridden by `Connection:' header
354 keep_alive = version >= HTTP_VER_11;
355
356 if (!sockin->get_eol())
357 rsp_bad_request();
358
359 if (version > HTTP_VER_09)
360 sockin->skipline();
361 }
362
363
parse_hdr(string & fname,string & fvalue)364 void request_rec::parse_hdr(string& fname, string& fvalue)
365 {
366 fname = get_token(field_chars); // read the field name
367 sockin->skiptoken(ws_chars);
368 if (sockin->get() != ':') // malformed header (no colon)
369 rsp_bad_request();
370
371 do {
372 sockin->skiptoken(ws_chars); // skip leading ws chars
373 do {
374 if (sockin->get_eol()) // the value may be empty (?)
375 break;
376 string t = get_token(uri_chars); // read field value
377 if (!isempty(fvalue))
378 fvalue += ' ';
379 fvalue += t;
380 if (length(fvalue) > MAX_TOKEN)
381 rsp_bad_request();
382 // according to RFC2616 all ws chars inside the field value
383 // can become a single space
384 } while (sockin->skiptoken(ws_chars) > 0);
385
386 if (!sockin->get_eol())
387 rsp_bad_request();
388 sockin->skipline();
389 } while (sockin->preview() & ws_chars); // see if field value continues on the next line
390 }
391
392
parse_headers()393 void request_rec::parse_headers()
394 {
395 while (!sockin->get_eol())
396 {
397 string fname, fvalue;
398 parse_hdr(fname, fvalue);
399 fname = lowercase(fname);
400
401 if (fname == "host")
402 host = fvalue;
403
404 else if (fname == "connection")
405 {
406 fvalue = lowercase(fvalue);
407 if (fvalue == "close")
408 keep_alive = false;
409 else if (fvalue == "keep-alive")
410 keep_alive = true;
411 }
412
413 else if (fname == "if-modified-since")
414 {
415 if_modified = parse_http_date(fvalue);
416 if (if_modified == invdatetime)
417 rsp_bad_request();
418 }
419
420 else if (fname == "referer")
421 referer = fvalue;
422
423 else if (fname == "range")
424 {
425 if (strncmp(fvalue, "bytes=", 6) == 0)
426 {
427 del(fvalue, 0, 6);
428 const char* p = fvalue;
429 char* e;
430 int rmin = strtol(p, &e, 10);
431 if (*e == '-')
432 {
433 p = e + 1;
434 int rmax = strtol(p, &e, 10);
435 if (e == p)
436 rmax = -1;
437 // we don't support multiple ranges, neither negative ranges
438 if (*e == 0 && rmin >= 0)
439 {
440 partial = true;
441 range_min = rmin;
442 range_max = rmax;
443 }
444 }
445 }
446 }
447
448 // other headers go to request_rec::headers for use in
449 // custom plugins/modules. so called "coalesce" headers
450 // are not supported yet
451 else
452 headers.put(fname, fvalue);
453 }
454
455 // convert the referer URI to relative if on the same host
456 if (!isempty(referer))
457 {
458 string s = "http://" + host;
459 if (strncmp(s, referer, length(s)) == 0)
460 del(referer, 0, length(s));
461 if (isempty(referer))
462 referer = "/";
463 }
464
465 sockin->skipline();
466 }
467
468
parse_uri()469 void request_rec::parse_uri()
470 {
471 string s = uri;
472 if (!isurl(uri))
473 {
474 // if request URI is just a path
475 if (*pconst(uri) != '/')
476 rsp_bad_request();
477 if (version > HTTP_VER_09 && isempty(host))
478 rsp_bad_request();
479 s = "http://" + host + uri;
480 }
481
482 urlcrack(s, url);
483
484 // split the path into components
485 split_path(url.path, path_parts);
486 }
487
488
analyze_uri()489 void request_rec::analyze_uri()
490 {
491 // analyze path components one by one and stop at the longest
492 // match that has a path handler. note that the request-uri
493 // may be longer and may contain extra components which are
494 // ignored, but left intact to be used by the handler.
495
496 string path = "/";
497 handler_info* handler = find_path_handler(path);
498
499 for (int i = 0; i < path_parts.get_count(); i++)
500 {
501 path += path_parts.getkey(i);
502 handler_info* h = find_path_handler(path);
503 if (h != 0)
504 handler = h;
505 path += "/";
506 }
507
508 if (handler == 0)
509 rsp_not_found();
510
511 path_callback(handler->callback)(*this);
512 // the handler must throw an ehttp exception
513 fatal(253, "Internal error 253");
514 }
515
516
respond()517 void request_rec::respond()
518 {
519 try
520 {
521 if (thread_count > cfg_max_clients)
522 rsp_overloaded();
523
524 parse_method();
525 parse_request_line();
526 parse_headers();
527 parse_uri();
528 analyze_uri();
529
530 // all branches must throw an ehttp exception
531 // before reaching this point
532 fatal(254, "Internal error 254");
533 }
534
535 catch(ehttp e)
536 {
537 if (keep_alive)
538 {
539 sockout->flush();
540 stat = STAT_WAIT;
541 }
542 else
543 {
544 sockin->close();
545 sockout->close();
546 }
547
548 htlog_write(client_ip, req_line, e.code, sockout->tellx() - hdr_size, referer);
549 }
550 }
551
552
553