1 /* Request processing thread.
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: process_rq.c,v 1.25 2003/03/01 12:11:33 rich Exp $
25 #ifdef HAVE_SYS_SOCKET_H
26 #include <sys/socket.h>
37 #ifdef HAVE_SYS_STAT_H
50 #include <pthr_pseudothread.h>
51 #include <pthr_iolib.h>
52 #include <pthr_http.h>
60 #include "process_rq.h"
62 /* Maximum number of requests to service in one thread. This just acts
63 * as a check on the size of the thread pool, preventing it from growing
66 #define MAX_REQUESTS_IN_THREAD 30
68 #define PR_DEBUG 0 /* Set this to enable debugging. */
70 static void run (void *vp);
73 new_process_rq (int sock)
79 p = pmalloc (pool, sizeof *p);
81 memset (p, 0, sizeof *p);
83 /* Set the FD_CLOEXEC flag so that when we fork off CGI scripts, they
84 * won't inherit the socket.
86 if (fcntl (sock, F_SETFD, FD_CLOEXEC) < 0) { perror ("fcntl"); exit (1); }
89 p->pth = new_pseudothread (pool, run, p, "process_rq");
96 #define THREAD_NAME "rws process request thread"
101 process_rq p = (process_rq) vp;
104 vector path_comps, v;
105 int i, is_dir, nr_requests = 1;
106 const char *location;
108 p->pool = pth_get_pool (p->pth);
109 p->io = io_fdopen (p->sock);
111 request_timeout = cfg_get_int (0, 0, "request timeout", 60);
113 /* Sit in a loop reading HTTP requests. */
114 while (!close && nr_requests <= MAX_REQUESTS_IN_THREAD)
116 /* Generic name for this thread. */
117 pth_set_name (THREAD_NAME " (idle)");
119 /* Count the number of requests serviced in this thread. */
122 /* Timeout requests. */
123 pth_timeout (request_timeout);
125 /* Read the request. */
126 p->http_request = new_http_request (p->pool, p->io);
127 if (p->http_request == 0) /* Normal end of file. */
133 /* Choose the correct configuration file based on the Host: header. */
134 p->host_header = http_request_get_header (p->http_request, "Host");
137 p->host_header = pstrlwr (pstrdup (p->pool, p->host_header));
139 if ((p->host = cfg_get_host (p->host_header)) != 0)
141 fprintf (stderr, "unknown virtual host: %s, trying default\n",
145 p->host_header = "default";
146 if ((p->host = cfg_get_host (p->host_header)) == 0)
148 close = bad_request_error (p, "no \"default\" virtual host!");
153 /* Get the originally requested path. */
154 p->requested_path = http_request_path (p->http_request);
155 if (!p->requested_path || p->requested_path[0] != '/')
157 close = bad_request_error (p, "bad pathname");
161 /* Path may contain % sequences. Unescape them. */
162 p->requested_path = cgi_unescape (p->pool, p->requested_path);
164 /* If the path ends in a /, then it's a request for a directory.
165 * Record this fact now, because pstrcsplit will forget about the
166 * trailing slash otherwise.
168 is_dir = p->requested_path[strlen (p->requested_path)-1] == '/';
170 /* Split up the path into individual components. */
171 path_comps = pstrcsplit (p->pool, p->requested_path, '/');
173 /* Remove "", "." and ".." components. */
174 for (i = 0; i < vector_size (path_comps); ++i)
178 vector_get (path_comps, i, comp);
180 if (strcmp (comp, "") == 0 || strcmp (comp, ".") == 0)
182 vector_erase (path_comps, i);
185 else if (strcmp (comp, "..") == 0)
189 vector_erase_range (path_comps, i-1, i+1);
194 vector_erase (path_comps, i);
200 /* Construct the canonical path. Add a trailing slash if the
201 * original request was for a directory.
203 p->canonical_path = psprintf (p->pool, "/%s",
204 pjoin (p->pool, path_comps, "/"));
205 if (strlen (p->canonical_path) > 1 && is_dir)
206 p->canonical_path = psprintf (p->pool, "%s/", p->canonical_path);
209 fprintf (stderr, "canonical path is %s\n", p->canonical_path);
212 /* Update the name of the thread with the full request URL. */
213 pth_set_name (psprintf (p->pool, THREAD_NAME " http://%s%s",
217 /* Apply internal and external rewrite rules. */
218 i = apply_rewrites (p, p->canonical_path, &location);
219 if (i == 1) /* External rewrite. */
222 fprintf (stderr, "external rewrite rule to %s\n", location);
224 close = moved_permanently (p, location);
227 else if (i == 2) /* Internal rewrite. */
230 fprintf (stderr, "internal rewrite rule to %s\n", location);
233 /* Update the http_request object with the new path. This also
234 * changes the query string held in this object so that the cgi
235 * library works correctly.
237 http_request_set_url (p->http_request, location);
239 /* Get the path, minus query string. */
240 p->rewritten_path = http_request_path (p->http_request);
242 /* Resplit the path. */
243 path_comps = pstrcsplit (p->pool, p->rewritten_path, '/');
246 /* Look for longest matching alias. */
247 for (i = vector_size (path_comps); i >= 0; --i)
251 v = new_subvector (p->pool, path_comps, 0, i);
253 psprintf (p->pool, "/%s/", pjoin (p->pool, v, "/"));
259 fprintf (stderr, "try to find alias matching %s\n", p->aliasname);
262 if ((p->alias = cfg_get_alias (p->host, p->aliasname)) != 0)
267 fprintf (stderr, "no matching alias found\n");
271 close = file_not_found_error (p);
275 /* Build up the remainder of the path and the file. */
276 v = new_subvector (p->pool, path_comps, i, vector_size (path_comps));
277 p->remainder = pjoin (p->pool, v, "/");
279 /* Find the root path for this alias. */
280 p->root = cfg_get_string (p->host, p->alias, "path", 0);
283 close = file_not_found_error (p);
287 /* Construct the file path. */
288 p->file_path = psprintf (p->pool, "%s/%s", p->root, p->remainder);
292 "rp = %s, cp = %s, rew = %s, "
293 "an = %s, rem = %s, root = %s, fp = %s, qs = %s\n",
294 p->requested_path, p->canonical_path, p->rewritten_path,
295 p->aliasname, p->remainder, p->root,
297 http_request_query_string (p->http_request) ? : "(null)");
300 /* Find the file to serve and stat it. */
301 if (stat (p->file_path, &p->statbuf) == -1)
303 close = file_not_found_error (p);
307 /* If it's a directory, but the last component of the name isn't
308 * a "/" character, then we need to add a "/" character and send
309 * a browser redirect back.
311 if (S_ISDIR (p->statbuf.st_mode) &&
312 p->canonical_path[strlen(p->canonical_path)-1] != '/')
314 location = psprintf (p->pool, "%s/", p->requested_path);
315 close = moved_permanently (p, location);
319 /* What type of file are we serving? */
320 if (S_ISREG (p->statbuf.st_mode))
322 close = file_serve (p);
325 else if (S_ISDIR (p->statbuf.st_mode))
327 close = dir_serve (p);
332 close = bad_request_error (p, "not a regular file or directory");