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