2 * - by Richard W.M. Jones <rich@annexia.org>
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.
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.
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.
18 * $Id: pthr_http.c,v 1.16 2003/02/02 18:05:31 rich Exp $
44 #ifdef HAVE_SYS_SOCKET_H
45 #include <sys/socket.h>
48 #ifdef HAVE_NETINET_IN_H
49 #include <netinet/in.h>
52 #ifdef HAVE_ARPA_INET_H
53 #include <arpa/inet.h>
66 #include "pthr_reactor.h"
67 #include "pthr_iolib.h"
69 #include "pthr_http.h"
71 #define MAX_LINE_LENGTH 4096
74 static const char *servername = PACKAGE "-httpd/" VERSION;
75 static FILE *log_fp = 0;
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. */
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). */
107 #define _HTTP_XH_LENGTH_DEFINED (_HTTP_XH_CONTENT_LENGTH|_HTTP_XH_TRANSFER_ENCODING_CHUNKED)
109 static void parse_url (http_request h);
110 static void do_logging (http_response h);
113 http_get_servername (void)
119 http_set_servername (const char *new_server_name)
121 return servername = new_server_name;
125 new_http_request (pool pool, io_handle io)
127 char line[MAX_LINE_LENGTH];
128 char *start_url, *end_url, *end_key;
131 http_request h = pmalloc (pool, sizeof *h);
133 memset (h, 0, sizeof *h);
135 h->headers = new_sash (h->pool);
136 h->t = reactor_time / 1000;
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).
143 if (!io_fgets (line, sizeof line, io, 0))
145 if (line[0] == '\0') goto again;
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.
152 if (strncmp (line, "GET ", 4) == 0)
154 h->method = HTTP_METHOD_GET;
155 start_url = line + 4;
157 else if (strncmp (line, "HEAD ", 5) == 0)
159 h->method = HTTP_METHOD_HEAD;
160 start_url = line + 5;
162 else if (strncmp (line, "POST ", 5) == 0)
164 h->method = HTTP_METHOD_POST;
165 start_url = line + 5;
169 const char *msg = "bad method (not a GET, HEAD or POST request)";
171 syslog (LOG_INFO, msg);
172 io_fputs ("HTTP/1.1 405 Method not allowed" CRLF
173 "Allow: GET, HEAD, POST" CRLF
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.
182 end_url = strchr (start_url, ' ');
185 /* It's an HTTP/0.9 request! */
186 h->original_url = h->url = pstrdup (h->pool, start_url);
195 /* It's an HTTP > 0.9 request, so there must be headers following. */
197 h->original_url = h->url = pstrdup (h->pool, start_url);
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)))
206 const char *msg = "badly formed request -- no HTTP/x.y";
208 syslog (LOG_INFO, msg);
209 io_fputs ("HTTP/1.1 400 Badly formed request" CRLF, io);
214 h->major = *(end_url+6) - '0';
215 h->minor = *(end_url+8) - '0';
217 /* Read the headers. */
220 if (!io_fgets (line, sizeof line, io, 0))
222 const char *msg = "unexpected EOF reading headers";
224 syslog (LOG_INFO, msg);
225 io_fputs ("HTTP/1.1 400 Unexpected EOF in request" CRLF, io);
229 /* End of headers? */
233 /* Check that the header has the form Key: Value. */
234 end_key = strchr (line, ':');
237 const char *msg = "badly formed header in request";
239 syslog (LOG_INFO, msg);
240 io_fputs ("HTTP/1.1 400 Badly formed header" CRLF, io);
244 /* Split up the key and value and store them. */
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.
255 key = pstrdup (h->pool, line);
256 value = pstrdup (h->pool, end_key);
258 /* Canonicalize the key (HTTP header keys are case insensitive). */
261 sash_insert (h->headers, key, value);
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.
272 parse_url (http_request h)
274 if (h->method == HTTP_METHOD_POST)
279 else /* GET or HEAD requests. */
283 p = pstrdup (h->pool, h->url);
286 if (t == 0) /* No query string. */
294 h->path = p; /* Path is everything before '?' char. */
295 h->query_string = t+1; /* Query string is everything after it. */
300 http_request_time (http_request h)
306 http_request_get_url (http_request h)
312 http_request_set_url (http_request h, const char *url)
319 http_request_path (http_request h)
325 http_request_query_string (http_request h)
327 return h->query_string;
331 http_request_method (http_request h)
337 http_request_method_string (http_request h)
341 case HTTP_METHOD_GET: return "GET";
342 case HTTP_METHOD_HEAD: return "HEAD";
343 case HTTP_METHOD_POST: return "POST";
349 http_request_is_HEAD (http_request h)
351 return h->method == HTTP_METHOD_HEAD;
355 http_request_version (http_request h, int *major, int *minor)
362 http_request_nr_headers (http_request h)
364 return sash_size (h->headers);
368 http_request_get_headers (http_request h)
370 vector keys = sash_keys (h->headers);
371 vector r = new_vector (h->pool, struct http_header);
374 for (i = 0; i < vector_size (keys); ++i)
376 struct http_header header;
379 vector_get (keys, i, key);
381 sash_get (h->headers, key, header.value);
383 vector_push_back (r, header);
390 http_request_get_header (http_request h, const char *key)
392 char *k = alloca (strlen (key) + 1);
395 /* Canonicalize the key. */
399 sash_get (h->headers, k, v);
405 http_request_get_cookie (http_request h, const char *key)
407 const char *cookie_hdr;
410 int keylen = strlen (key);
413 cookie_hdr = http_request_get_header (h, "Cookie");
414 if (!cookie_hdr) return 0;
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);
420 for (i = 0; i < vector_size (v); ++i)
424 vector_get (v, i, str);
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.
432 if (strncasecmp (str, key, keylen) == 0
433 && str[keylen] == '=')
435 return cgi_unescape (h->pool, &str[keylen+1]);
444 new_http_response (pool pool, http_request request,
446 int code, const char *msg)
448 http_response h = pmalloc (pool, sizeof *h);
450 memset (h, 0, sizeof *h);
453 h->request = request;
456 h->extra_headers = ~0;
457 h->content_length = 0;
459 /* Set the output mode to fully buffered for efficiency. */
460 io_setbufmode (h->io, IO_MODE_FULLY_BUFFERED);
462 /* HTTP/0.9? No response line or headers. */
463 if (request->is_http09) return h;
465 /* Write the response line. */
466 io_fprintf (io, "HTTP/1.1 %d %s" CRLF, code, msg);
472 http_response_send_header (http_response h,
473 const char *key, const char *value)
475 /* HTTP/0.9? No response line or headers. */
476 if (h->request->is_http09) return;
478 io_fputs (key, h->io);
479 io_fputs (": ", h->io);
480 io_fputs (value, h->io);
481 io_fputs (CRLF, h->io);
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.
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)
497 h->extra_headers &= ~_HTTP_XH_CONTENT_LENGTH;
498 sscanf (value, "%d", &h->content_length);
500 if (strcasecmp (key, "Transfer-Encoding") == 0 &&
501 strcasecmp (value, "chunked") == 0)
502 h->extra_headers &= ~_HTTP_XH_TRANSFER_ENCODING_CHUNKED;
506 http_response_send_headers (http_response h, ...)
511 /* HTTP/0.9? No response line or headers. */
512 if (h->request->is_http09) return;
515 while ((k = va_arg (args, const char *)) != 0)
517 v = va_arg (args, const char *);
518 http_response_send_header (h, k, v);
524 http_response_end_headers (http_response h)
528 /* HTTP/0.9? No response line or headers. */
529 if (h->request->is_http09)
532 /* Send any remaining headers. */
533 if (h->extra_headers & _HTTP_XH_SERVER)
534 http_response_send_header (h, "Server", servername);
536 #if HAVE_STRFTIME && HAVE_GMTIME
537 /* See RFC 2616 section 3.3.1. */
538 if (h->extra_headers & _HTTP_XH_DATE)
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);
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");
553 if (h->extra_headers & _HTTP_XH_CONNECTION)
555 /* Deal with persistent connections (see RFC 2616 section 8.1). */
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)
560 if ((h->extra_headers & _HTTP_XH_LENGTH_DEFINED) ==
561 _HTTP_XH_LENGTH_DEFINED)
564 http_response_send_header (h, "Connection", "close");
568 /* Otherwise look for the Connection: header in the request. */
570 = http_request_get_header (h->request, "Connection");
574 if (strcasecmp (conn_hdr, "close") == 0)
577 http_response_send_header (h, "Connection", "close");
579 else if (strcasecmp (conn_hdr, "keep-alive") == 0)
582 http_response_send_header (h, "Connection", "keep-alive");
587 http_response_send_header (h, "Connection", "close");
592 /* Assume close for HTTP/1.0 clients, keep-alive for HTTP/1.1
595 if (h->request->major == 1 && h->request->minor >= 1)
598 http_response_send_header (h, "Connection", "keep-alive");
603 http_response_send_header (h, "Connection", "close");
609 io_fputs (CRLF, h->io);
612 if (log_fp) do_logging (h);
618 http_response_write_chunk (http_response h, const char *data, int length)
620 io_fprintf (h->io, "%X" CRLF, length);
621 io_fwrite (data, 1, length, h->io);
622 io_fprintf (h->io, CRLF);
626 http_response_write_chunk_string (http_response h, const char *string)
628 io_fprintf (h->io, "%X" CRLF "%s" CRLF, strlen (string), string);
632 http_response_write_chunk_end (http_response h)
634 io_fputs ("0" CRLF, h->io);
638 http_set_log_file (FILE *fp)
644 http_get_log_file (void)
650 do_logging (http_response h)
652 #if HAVE_STRFTIME && HAVE_GMTIME
657 const char *time_str;
662 const char *user_agent;
664 struct sockaddr_in addr;
666 const char *addr_str;
669 /* Get the request time. */
670 #if HAVE_STRFTIME && HAVE_GMTIME
671 t = http_request_time (h->request);
673 strftime (time_str, sizeof time_str, "%Y/%m/%d %H:%M:%S", tm);
678 /* Get the referer (sic) header. */
679 if ((referer = http_request_get_header (h->request, "Referer")) == 0)
682 /* Get the user agent header. */
683 if ((user_agent = http_request_get_header (h->request, "User-Agent")) == 0)
687 method = http_request_method_string (h->request);
688 url = h->request->original_url;
689 http_request_version (h->request, &major, &minor);
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);
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);