Add to git.
[pthrlib.git] / src / pthr_http.c
1 /* HTTP library.
2  * - by Richard W.M. Jones <rich@annexia.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  *
18  * $Id: pthr_http.c,v 1.16 2003/02/02 18:05:31 rich Exp $
19  */
20
21 #include "config.h"
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <stdarg.h>
26 #include <ctype.h>
27
28 #ifdef HAVE_SETJMP_H
29 #include <setjmp.h>
30 #endif
31
32 #ifdef HAVE_STRING_H
33 #include <string.h>
34 #endif
35
36 #ifdef HAVE_SYSLOG_H
37 #include <syslog.h>
38 #endif
39
40 #ifdef HAVE_TIME_H
41 #include <time.h>
42 #endif
43
44 #ifdef HAVE_SYS_SOCKET_H
45 #include <sys/socket.h>
46 #endif
47
48 #ifdef HAVE_NETINET_IN_H
49 #include <netinet/in.h>
50 #endif
51
52 #ifdef HAVE_ARPA_INET_H
53 #include <arpa/inet.h>
54 #endif
55
56 #ifdef HAVE_ALLOCA_H
57 #include <alloca.h>
58 #endif
59
60 #include <pool.h>
61 #include <hash.h>
62 #include <vector.h>
63 #include <pstring.h>
64 #include <pre.h>
65
66 #include "pthr_reactor.h"
67 #include "pthr_iolib.h"
68 #include "pthr_cgi.h"
69 #include "pthr_http.h"
70
71 #define MAX_LINE_LENGTH 4096
72 #define CRLF "\r\n"
73
74 static const char *servername = PACKAGE "-httpd/" VERSION;
75 static FILE *log_fp = 0;
76
77 struct http_request
78 {
79   pool pool;                    /* Pool for memory allocations. */
80   time_t t;                     /* Request time. */
81   int method;                   /* Method. */
82   const char *original_url;     /* Original request URL (used for logging). */
83   const char *url;              /* The URL. */
84   const char *path;             /* Path only. */
85   const char *query_string;     /* Query string only. */
86   int is_http09;                /* Is it an HTTP/0.9 request? */
87   int major, minor;             /* Major/minor version numbers. */
88   sash headers;                 /* Headers. */
89 };
90
91 struct http_response
92 {
93   pool pool;                    /* Pool. */
94   http_request request;         /* The original request. */
95   int code;                     /* Response code. */
96   io_handle io;                 /* IO handle. */
97   unsigned extra_headers;       /* Bitmap of extra headers to send. */
98 #define _HTTP_XH_SERVER           1
99 #define _HTTP_XH_DATE             2
100 #define _HTTP_XH_CONTENT_TYPE     4
101 #define _HTTP_XH_CONNECTION       8
102 #define _HTTP_XH_CONTENT_LENGTH  16
103 #define _HTTP_XH_TRANSFER_ENCODING_CHUNKED 32
104   int content_length;           /* Content length (if seen). */
105 };
106
107 #define _HTTP_XH_LENGTH_DEFINED (_HTTP_XH_CONTENT_LENGTH|_HTTP_XH_TRANSFER_ENCODING_CHUNKED)
108
109 static void parse_url (http_request h);
110 static void do_logging (http_response h);
111
112 const char *
113 http_get_servername (void)
114 {
115   return servername;
116 }
117
118 const char *
119 http_set_servername (const char *new_server_name)
120 {
121   return servername = new_server_name;
122 }
123
124 http_request
125 new_http_request (pool pool, io_handle io)
126 {
127   char line[MAX_LINE_LENGTH];
128   char *start_url, *end_url, *end_key;
129   char *key, *value;
130
131   http_request h = pmalloc (pool, sizeof *h);
132
133   memset (h, 0, sizeof *h);
134   h->pool = pool;
135   h->headers = new_sash (h->pool);
136   h->t = reactor_time / 1000;
137
138   /* Read the first line of the request. As a sop to Netscape 4, ignore
139    * blank lines (see note below about Netscape generating extra CRLFs
140    * after POST requests).
141    */
142  again:
143   if (!io_fgets (line, sizeof line, io, 0))
144     return 0;
145   if (line[0] == '\0') goto again;
146
147   /* Previous versions of the server supported only GET requests. We
148    * now support GET and HEAD (as required by RFC 2616 section 5.1.1)
149    * and POST (see: http://www.w3.org/MarkUp/html-spec/html-spec_8.html).
150    * See RFC 2616 section 14.7 for a description of the Allow header.
151    */
152   if (strncmp (line, "GET ", 4) == 0)
153     {
154       h->method = HTTP_METHOD_GET;
155       start_url = line + 4;
156     }
157   else if (strncmp (line, "HEAD ", 5) == 0)
158     {
159       h->method = HTTP_METHOD_HEAD;
160       start_url = line + 5;
161     }
162   else if (strncmp (line, "POST ", 5) == 0)
163     {
164       h->method = HTTP_METHOD_POST;
165       start_url = line + 5;
166     }
167   else
168     {
169       const char *msg = "bad method (not a GET, HEAD or POST request)";
170
171       syslog (LOG_INFO, msg);
172       io_fputs ("HTTP/1.1 405 Method not allowed" CRLF
173                 "Allow: GET, HEAD, POST" CRLF
174                 CRLF, io);
175       pth_die (msg);
176     }
177
178   /* Parse out the URL. According to RFC 2616 section 5.1.2 we ought
179    * to be able to support absolute URIs in requests. At the moment
180    * we can't. Luckily no HTTP/1.1 clients should generate them.
181    */
182   end_url = strchr (start_url, ' ');
183   if (end_url == 0)
184     {
185       /* It's an HTTP/0.9 request! */
186       h->original_url = h->url = pstrdup (h->pool, start_url);
187       parse_url (h);
188       h->is_http09 = 1;
189       h->major = 0;
190       h->minor = 9;
191
192       return h;
193     }
194
195   /* It's an HTTP > 0.9 request, so there must be headers following. */
196   *end_url = '\0';
197   h->original_url = h->url = pstrdup (h->pool, start_url);
198   parse_url (h);
199
200   /* Check HTTP version number. */
201   if (strncmp (end_url+1, "HTTP/", 5) != 0 ||
202       !isdigit ((int) *(end_url+6)) ||
203       *(end_url+7) != '.' ||
204       !isdigit ((int) *(end_url+8)))
205     {
206       const char *msg = "badly formed request -- no HTTP/x.y";
207
208       syslog (LOG_INFO, msg);
209       io_fputs ("HTTP/1.1 400 Badly formed request" CRLF, io);
210       pth_die (msg);
211     }
212
213   h->is_http09 = 0;
214   h->major = *(end_url+6) - '0';
215   h->minor = *(end_url+8) - '0';
216
217   /* Read the headers. */
218   for (;;)
219     {
220       if (!io_fgets (line, sizeof line, io, 0))
221         {
222           const char *msg = "unexpected EOF reading headers";
223
224           syslog (LOG_INFO, msg);
225           io_fputs ("HTTP/1.1 400 Unexpected EOF in request" CRLF, io);
226           pth_die (msg);
227         }
228
229       /* End of headers? */
230       if (line[0] == '\0')
231         break;
232
233       /* Check that the header has the form Key: Value. */
234       end_key = strchr (line, ':');
235       if (end_key == 0)
236         {
237           const char *msg = "badly formed header in request";
238
239           syslog (LOG_INFO, msg);
240           io_fputs ("HTTP/1.1 400 Badly formed header" CRLF, io);
241           pth_die (msg);
242         }
243
244       /* Split up the key and value and store them. */
245       *end_key = '\0';
246
247       /* Find the beginning of the value field.
248        * RFC822 / RFC2616 does not require a space after the colon
249        * and effectively says to ignore linear white space after the
250        * colon. So remove that here.
251        */
252       end_key++;
253       ptrim (end_key);
254
255       key = pstrdup (h->pool, line);
256       value = pstrdup (h->pool, end_key);
257
258       /* Canonicalize the key (HTTP header keys are case insensitive). */
259       pstrlwr (key);
260
261       sash_insert (h->headers, key, value);
262     }
263
264   return h;
265 }
266
267 /* This function is called just after h->url has been set. It
268  * parses out the path and query string parameters from the URL
269  * and stores them separately.
270  */
271 static void
272 parse_url (http_request h)
273 {
274   if (h->method == HTTP_METHOD_POST)
275     {
276       h->path = h->url;
277       h->query_string = 0;
278     }
279   else                          /* GET or HEAD requests. */
280     {
281       char *p, *t;
282
283       p = pstrdup (h->pool, h->url);
284       t = strchr (p, '?');
285
286       if (t == 0)               /* No query string. */
287         {
288           h->path = p;
289           h->query_string = 0;
290           return;
291         }
292
293       *t = '\0';
294       h->path = p;              /* Path is everything before '?' char. */
295       h->query_string = t+1;    /* Query string is everything after it. */
296     }
297 }
298
299 time_t
300 http_request_time (http_request h)
301 {
302   return h->t;
303 }
304
305 const char *
306 http_request_get_url (http_request h)
307 {
308   return h->url;
309 }
310
311 void
312 http_request_set_url (http_request h, const char *url)
313 {
314   h->url = url;
315   parse_url (h);
316 }
317
318 const char *
319 http_request_path (http_request h)
320 {
321   return h->path;
322 }
323
324 const char *
325 http_request_query_string (http_request h)
326 {
327   return h->query_string;
328 }
329
330 int
331 http_request_method (http_request h)
332 {
333   return h->method;
334 }
335
336 const char *
337 http_request_method_string (http_request h)
338 {
339   switch (h->method)
340     {
341     case HTTP_METHOD_GET: return "GET";
342     case HTTP_METHOD_HEAD: return "HEAD";
343     case HTTP_METHOD_POST: return "POST";
344     }
345   abort ();
346 }
347
348 int
349 http_request_is_HEAD (http_request h)
350 {
351   return h->method == HTTP_METHOD_HEAD;
352 }
353
354 void
355 http_request_version (http_request h, int *major, int *minor)
356 {
357   *major = h->major;
358   *minor = h->minor;
359 }
360
361 int
362 http_request_nr_headers (http_request h)
363 {
364   return sash_size (h->headers);
365 }
366
367 vector
368 http_request_get_headers (http_request h)
369 {
370   vector keys = sash_keys (h->headers);
371   vector r = new_vector (h->pool, struct http_header);
372   int i;
373
374   for (i = 0; i < vector_size (keys); ++i)
375     {
376       struct http_header header;
377       char *key;
378
379       vector_get (keys, i, key);
380       header.key = key;
381       sash_get (h->headers, key, header.value);
382
383       vector_push_back (r, header);
384     }
385
386   return r;
387 }
388
389 const char *
390 http_request_get_header (http_request h, const char *key)
391 {
392   char *k = alloca (strlen (key) + 1);
393   const char *v;
394
395   /* Canonicalize the key. */
396   strcpy (k, key);
397   pstrlwr (k);
398
399   sash_get (h->headers, k, v);
400
401   return v;
402 }
403
404 const char *
405 http_request_get_cookie (http_request h, const char *key)
406 {
407   const char *cookie_hdr;
408   static pcre *re = 0;
409   vector v;
410   int keylen = strlen (key);
411   int i;
412
413   cookie_hdr = http_request_get_header (h, "Cookie");
414   if (!cookie_hdr) return 0;
415
416   /* Split it into pieces at whitespace, commas or semi-colons. */
417   if (!re) re = precomp (global_pool, "[ \t\n,;]+", 0);
418   v = pstrresplit (h->pool, cookie_hdr, re);
419
420   for (i = 0; i < vector_size (v); ++i)
421     {
422       char *str;
423
424       vector_get (v, i, str);
425
426       /* It'll either be NAME=VALUE or $Path="?VALUE"?. We actually
427        * don't care too much about the Path or Domain, so ignore them.
428        */
429       if (str[0] != '$')
430         {
431           /* Matching name? */
432           if (strncasecmp (str, key, keylen) == 0
433               && str[keylen] == '=')
434             {
435               return cgi_unescape (h->pool, &str[keylen+1]);
436             }
437         }
438     }
439
440   return 0;
441 }
442
443 http_response
444 new_http_response (pool pool, http_request request,
445                    io_handle io,
446                    int code, const char *msg)
447 {
448   http_response h = pmalloc (pool, sizeof *h);
449
450   memset (h, 0, sizeof *h);
451
452   h->pool = pool;
453   h->request = request;
454   h->code = code;
455   h->io = io;
456   h->extra_headers = ~0;
457   h->content_length = 0;
458
459   /* Set the output mode to fully buffered for efficiency. */
460   io_setbufmode (h->io, IO_MODE_FULLY_BUFFERED);
461
462   /* HTTP/0.9? No response line or headers. */
463   if (request->is_http09) return h;
464
465   /* Write the response line. */
466   io_fprintf (io, "HTTP/1.1 %d %s" CRLF, code, msg);
467
468   return h;
469 }
470
471 void
472 http_response_send_header (http_response h,
473                            const char *key, const char *value)
474 {
475   /* HTTP/0.9? No response line or headers. */
476   if (h->request->is_http09) return;
477
478   io_fputs (key, h->io);
479   io_fputs (": ", h->io);
480   io_fputs (value, h->io);
481   io_fputs (CRLF, h->io);
482
483   /* Check for caller sending known header key and remove that
484    * from the bitmap so we don't overwrite caller's header
485    * in http_response_end_headers function.
486    */
487   if (strcasecmp (key, "Server") == 0)
488     h->extra_headers &= ~_HTTP_XH_SERVER;
489   if (strcasecmp (key, "Date") == 0)
490     h->extra_headers &= ~_HTTP_XH_DATE;
491   if (strcasecmp (key, "Content-Type") == 0)
492     h->extra_headers &= ~_HTTP_XH_CONTENT_TYPE;
493   if (strcasecmp (key, "Connection") == 0)
494     h->extra_headers &= ~_HTTP_XH_CONNECTION;
495   if (strcasecmp (key, "Content-Length") == 0)
496     {
497       h->extra_headers &= ~_HTTP_XH_CONTENT_LENGTH;
498       sscanf (value, "%d", &h->content_length);
499     }
500   if (strcasecmp (key, "Transfer-Encoding") == 0 &&
501       strcasecmp (value, "chunked") == 0)
502     h->extra_headers &= ~_HTTP_XH_TRANSFER_ENCODING_CHUNKED;
503 }
504
505 void
506 http_response_send_headers (http_response h, ...)
507 {
508   va_list args;
509   const char *k, *v;
510
511   /* HTTP/0.9? No response line or headers. */
512   if (h->request->is_http09) return;
513
514   va_start (args, h);
515   while ((k = va_arg (args, const char *)) != 0)
516     {
517       v = va_arg (args, const char *);
518       http_response_send_header (h, k, v);
519     }
520   va_end (args);
521 }
522
523 int
524 http_response_end_headers (http_response h)
525 {
526   int close = 1;
527
528   /* HTTP/0.9? No response line or headers. */
529   if (h->request->is_http09)
530     goto out;
531
532   /* Send any remaining headers. */
533   if (h->extra_headers & _HTTP_XH_SERVER)
534     http_response_send_header (h, "Server", servername);
535
536 #if HAVE_STRFTIME && HAVE_GMTIME
537   /* See RFC 2616 section 3.3.1. */
538   if (h->extra_headers & _HTTP_XH_DATE)
539     {
540       time_t t;
541       char s[64];
542
543       t = http_request_time (h->request);
544       strftime (s, sizeof s, "%a, %d %b %Y %H:%M:%S GMT", gmtime (&t));
545       http_response_send_header (h, "Date", s);
546     }
547 #endif
548
549   /* This is not correct: see RFC 2616 section 3.4.1 for more details. */
550   if (h->extra_headers & _HTTP_XH_CONTENT_TYPE)
551     http_response_send_header (h, "Content-Type", "text/plain");
552
553   if (h->extra_headers & _HTTP_XH_CONNECTION)
554     {
555       /* Deal with persistent connections (see RFC 2616 section 8.1). */
556
557       /* Server app must have sent a Content-Length header, otherwise
558        * the connection must close anyway. (RFC 2616 sections 4.4, 8.1.2.1)
559        */
560       if ((h->extra_headers & _HTTP_XH_LENGTH_DEFINED) ==
561           _HTTP_XH_LENGTH_DEFINED)
562         {
563           close = 1;
564           http_response_send_header (h, "Connection", "close");
565         }
566       else
567         {
568           /* Otherwise look for the Connection: header in the request. */
569           const char *conn_hdr
570             = http_request_get_header (h->request, "Connection");
571
572           if (conn_hdr)
573             {
574               if (strcasecmp (conn_hdr, "close") == 0)
575                 {
576                   close = 1;
577                   http_response_send_header (h, "Connection", "close");
578                 }
579               else if (strcasecmp (conn_hdr, "keep-alive") == 0)
580                 {
581                   close = 0;
582                   http_response_send_header (h, "Connection", "keep-alive");
583                 }
584               else
585                 {
586                   close = 1;
587                   http_response_send_header (h, "Connection", "close");
588                 }
589             }
590           else
591             {
592               /* Assume close for HTTP/1.0 clients, keep-alive for HTTP/1.1
593                * clients.
594                */
595               if (h->request->major == 1 && h->request->minor >= 1)
596                 {
597                   close = 0;
598                   http_response_send_header (h, "Connection", "keep-alive");
599                 }
600               else
601                 {
602                   close = 1;
603                   http_response_send_header (h, "Connection", "close");
604                 }
605             }
606         }
607     }
608
609   io_fputs (CRLF, h->io);
610
611  out:
612   if (log_fp) do_logging (h);
613
614   return close;
615 }
616
617 void
618 http_response_write_chunk (http_response h, const char *data, int length)
619 {
620   io_fprintf (h->io, "%X" CRLF, length);
621   io_fwrite (data, 1, length, h->io);
622   io_fprintf (h->io, CRLF);
623 }
624
625 void
626 http_response_write_chunk_string (http_response h, const char *string)
627 {
628   io_fprintf (h->io, "%X" CRLF "%s" CRLF, strlen (string), string);
629 }
630
631 void
632 http_response_write_chunk_end (http_response h)
633 {
634   io_fputs ("0" CRLF, h->io);
635 }
636
637 FILE *
638 http_set_log_file (FILE *fp)
639 {
640   return log_fp = fp;
641 }
642
643 FILE *
644 http_get_log_file (void)
645 {
646   return log_fp;
647 }
648
649 static void
650 do_logging (http_response h)
651 {
652 #if HAVE_STRFTIME && HAVE_GMTIME
653   char time_str[64];
654   struct tm *tm;
655   time_t t;
656 #else
657   const char *time_str;
658 #endif
659   const char *referer;
660   const char *method;
661   const char *url;
662   const char *user_agent;
663   int major, minor;
664   struct sockaddr_in addr;
665   socklen_t addrlen;
666   const char *addr_str;
667   int port;
668
669   /* Get the request time. */
670 #if HAVE_STRFTIME && HAVE_GMTIME
671   t = http_request_time (h->request);
672   tm = gmtime (&t);
673   strftime (time_str, sizeof time_str, "%Y/%m/%d %H:%M:%S", tm);
674 #else
675   time_str = "- -";
676 #endif
677
678   /* Get the referer (sic) header. */
679   if ((referer = http_request_get_header (h->request, "Referer")) == 0)
680     referer = "-";
681
682   /* Get the user agent header. */
683   if ((user_agent = http_request_get_header (h->request, "User-Agent")) == 0)
684     user_agent = "-";
685
686   /* Get the URL. */
687   method = http_request_method_string (h->request);
688   url = h->request->original_url;
689   http_request_version (h->request, &major, &minor);
690
691   /* Get the address and port number of the peer (client). */
692   addrlen = sizeof addr;
693   getpeername (io_fileno (h->io), (struct sockaddr *) &addr, &addrlen);
694   addr_str = inet_ntoa (addr.sin_addr);
695   port = ntohs (addr.sin_port);
696
697   fprintf (log_fp,
698            "%s %s:%d \"%s %s HTTP/%d.%d\" %d %d \"%s\" \"%s\"\n",
699            time_str, addr_str, port, method, url, major, minor,
700            h->code, h->content_length, referer, user_agent);
701   fflush (log_fp);
702 }