/[hydra]/hydra/src/get.c
ViewVC logotype

Annotation of /hydra/src/get.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.3 - (hide annotations)
Sun Sep 22 07:10:57 2002 UTC (21 years, 7 months ago) by nmav
Branch: MAIN
Changes since 1.2: +2 -2 lines
File MIME type: text/plain
More improvements on the mmap caching code. Removed the MMAP_LIST_USE_MAX. Now the upper limit is the size of the mmap list.

1 nmav 1.1 /*
2     * Boa, an http server
3     * Copyright (C) 1995 Paul Phillips <paulp@go2net.com>
4     * Some changes Copyright (C) 1996,99 Larry Doolittle <ldoolitt@boa.org>
5     * Some changes Copyright (C) 1996-2002 Jon Nelson <jnelson@boa.org>
6     *
7     * This program is free software; you can redistribute it and/or modify
8     * it under the terms of the GNU General Public License as published by
9     * the Free Software Foundation; either version 1, or (at your option)
10     * any later version.
11     *
12     * This program is distributed in the hope that it will be useful,
13     * but WITHOUT ANY WARRANTY; without even the implied warranty of
14     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15     * GNU General Public License for more details.
16     *
17     * You should have received a copy of the GNU General Public License
18     * along with this program; if not, write to the Free Software
19     * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20     *
21     */
22    
23 nmav 1.3 /* $Id: get.c,v 1.2 2002/09/21 22:42:08 nmav Exp $*/
24 nmav 1.1
25     #include "boa.h"
26     #include "socket.h"
27    
28     /* local prototypes */
29     int get_cachedir_file(request * req, struct stat *statbuf);
30     int index_directory(request * req, char *dest_filename);
31     static int gzip_open(request * req);
32    
33     /*
34     * Name: init_get
35     * Description: Initializes a non-script GET or HEAD request.
36     *
37     * Return values:
38     * 0: finished or error, request will be freed
39     * 1: successfully initialized, added to ready queue
40     */
41    
42     int init_get(server_params * params, request * req)
43     {
44     int data_fd, saved_errno;
45     struct stat statbuf;
46     volatile int bytes;
47    
48     data_fd = open(req->pathname, O_RDONLY);
49     saved_errno = errno; /* might not get used */
50    
51     #ifdef GUNZIP
52     if (data_fd == -1 && errno == ENOENT) {
53     /* cannot open */
54     /* it's either a gunzipped file or a directory */
55     data_fd = gzip_open(req);
56    
57     /* since the gzip file exists then spawn the gunzip.
58     */
59     if (data_fd==1) return init_cgi(req);
60    
61     /* If the return value was not -1 then return it.
62     */
63     if (data_fd!=-1) return data_fd;
64     }
65     #endif
66    
67     if (data_fd == -1) {
68     log_error_doc(req);
69     errno = saved_errno;
70     perror("document open");
71    
72     if (saved_errno == ENOENT)
73     send_r_not_found(req);
74     else if (saved_errno == EACCES)
75     send_r_forbidden(req);
76     else
77     send_r_bad_request(req);
78     return 0;
79     }
80    
81     if (fstat( data_fd, &statbuf) == -1) {
82     /* this is quite impossible, since the file
83     * was opened before.
84     */
85     close(data_fd);
86     send_r_bad_request(req);
87     return 0;
88     }
89    
90     if (S_ISDIR(statbuf.st_mode)) { /* directory */
91     close(data_fd); /* close dir */
92    
93     if (req->pathname[strlen(req->pathname) - 1] != '/') {
94     char buffer[3 * MAX_PATH_LENGTH + 128];
95    
96     if (server_port != 80)
97     sprintf(buffer, "http://%s:%d%s/", server_name,
98     server_port, req->request_uri);
99     else
100     sprintf(buffer, "http://%s%s/", server_name, req->request_uri);
101     send_r_moved_perm(req, buffer);
102     return 0;
103     }
104     data_fd = get_dir(req, &statbuf); /* updates statbuf */
105    
106     if (data_fd == -1) /* couldn't do it */
107     return 0; /* errors reported by get_dir */
108     else if (data_fd <= 1)
109     /* data_fd == 0 -> close it down, 1 -> continue */
110     return data_fd;
111     /* else, data_fd contains the fd of the file... */
112     }
113     if (req->if_modified_since &&
114     !modified_since(&(statbuf.st_mtime), req->if_modified_since)) {
115     send_r_not_modified(req);
116     close(data_fd);
117     return 0;
118     }
119     req->filesize = statbuf.st_size;
120     req->last_modified = statbuf.st_mtime;
121    
122     if (req->method == M_HEAD || req->filesize == 0) {
123     send_r_request_ok(req);
124     close(data_fd);
125     return 0;
126     }
127    
128     if (req->filesize > MAX_FILE_MMAP) {
129     send_r_request_ok(req); /* All's well */
130     req->status = PIPE_READ;
131     req->cgi_status = CGI_BUFFER;
132     req->data_fd = data_fd;
133     req_flush(req); /* this should *always* complete due to
134     the size of the I/O buffers */
135     req->header_line = req->header_end = req->buffer;
136     return 1;
137     }
138    
139     if (req->filesize == 0) { /* done */
140     send_r_request_ok(req); /* All's well *so far* */
141     close(data_fd);
142     return 1;
143     }
144    
145     /* NOTE: I (Jon Nelson) tried performing a read(2)
146     * into the output buffer provided the file data would
147     * fit, before mmapping, and if successful, writing that
148     * and stopping there -- all to avoid the cost
149     * of a mmap. Oddly, it was *slower* in benchmarks.
150     */
151 nmav 1.2 #ifdef USE_MMAP_LIST
152 nmav 1.1 req->mmap_entry_var = find_mmap(data_fd, &statbuf);
153     if (req->mmap_entry_var == NULL) {
154     req->buffer_end = 0;
155     if (errno == ENOENT)
156     send_r_not_found(req);
157     else if (errno == EACCES)
158     send_r_forbidden(req);
159     else
160     send_r_bad_request(req);
161     close(data_fd);
162     return 0;
163     }
164     req->data_mem = req->mmap_entry_var->mmap;
165 nmav 1.2 #else
166     req->data_mem = mmap(0, statbuf.st_size, PROT_READ, MAP_OPTIONS, data_fd, 0);
167     #endif
168 nmav 1.1 close(data_fd); /* close data file */
169    
170 nmav 1.3 if ( req->data_mem == MAP_FAILED) {
171 nmav 1.1 boa_perror(req, "mmap");
172     return 0;
173     }
174    
175     send_r_request_ok(req); /* All's well */
176    
177     bytes = BUFFER_SIZE - req->buffer_end;
178    
179     /* bytes is now how much the buffer can hold
180     * after the headers
181     */
182    
183     if (bytes > 0) {
184     if (bytes > req->filesize)
185     bytes = req->filesize;
186    
187     if (setjmp(params->env) == 0) {
188     params->handle_sigbus = 1;
189     memcpy(req->buffer + req->buffer_end, req->data_mem, bytes);
190     params->handle_sigbus = 0;
191     /* OK, SIGBUS **after** this point is very bad! */
192     } else {
193     /* sigbus! */
194     log_error_doc(req);
195     reset_output_buffer(req);
196     send_r_error(req);
197     fprintf(stderr, "%sGot SIGBUS in memcpy!\n",
198     get_commonlog_time());
199     return 0;
200     }
201     req->buffer_end += bytes;
202     req->filepos += bytes;
203     if (req->filesize == req->filepos) {
204     req_flush(req);
205     req->status = DONE;
206     }
207     }
208    
209     /* We lose statbuf here, so make sure response has been sent */
210     return 1;
211     }
212    
213     #ifdef GUNZIP
214    
215     /* If a file could not be opened, then this function
216     * is called, which tries to open the gziped file.
217     * Return values:
218     * 0: finished or error, request will be freed
219     * 1: success
220     * -1: failed
221     */
222     static int gzip_open(request * req)
223     {
224     char gzip_pathname[MAX_PATH_LENGTH];
225     int len, data_fd;
226    
227     len = strlen(req->pathname);
228    
229     memcpy(gzip_pathname, req->pathname, len);
230     memcpy(gzip_pathname + len, ".gz", 3);
231     gzip_pathname[len + 3] = '\0';
232     data_fd = open(gzip_pathname, O_RDONLY);
233     if (data_fd != -1) {
234     close(data_fd);
235    
236     req->response_status = R_REQUEST_OK;
237     if (req->pathname)
238     free(req->pathname);
239     req->pathname = strdup(gzip_pathname);
240     if (!req->pathname) {
241     log_error_time();
242     perror("strdup");
243     send_r_error(req);
244     return 0;
245     }
246     if (!req->simple) {
247     req_write(req, "HTTP/1.0 200 OK-GUNZIP\r\n");
248     print_http_headers(req);
249     print_content_type(req);
250     print_last_modified(req);
251     req_write(req, "\r\n");
252     req_flush(req);
253     }
254     if (req->method == M_HEAD)
255     return 0;
256    
257     return 1;
258    
259     }
260    
261     return -1;
262     }
263     #endif
264    
265     /*
266     * Name: process_get
267     * Description: Writes a chunk of data to the socket.
268     *
269     * Return values:
270     * -1: request blocked, move to blocked queue
271     * 0: EOF or error, close it down
272     * 1: successful write, recycle in ready queue
273     */
274    
275     int process_get(server_params * params, request * req)
276     {
277     int bytes_written;
278     volatile int bytes_to_write;
279    
280     bytes_to_write = req->filesize - req->filepos;
281     if (bytes_to_write > SOCKETBUF_SIZE)
282     bytes_to_write = SOCKETBUF_SIZE;
283    
284    
285     if (setjmp(params->env) == 0) {
286     params->handle_sigbus = 1;
287    
288     bytes_written =
289     socket_send(req, req->data_mem + req->filepos, bytes_to_write);
290    
291     params->handle_sigbus = 0;
292     /* OK, SIGBUS **after** this point is very bad! */
293     } else {
294     /* sigbus! */
295     log_error_doc(req);
296     /* sending an error here is inappropriate
297     * if we are here, the file is mmapped, and thus,
298     * a content-length has been sent. If we send fewer bytes
299     * the client knows there has been a problem.
300     * We run the risk of accidentally sending the right number
301     * of bytes (or a few too many) and the client
302     * won't be the wiser.
303     */
304     req->status = DEAD;
305     fprintf(stderr, "%sGot SIGBUS in write(2)!\n", get_commonlog_time());
306     return 0;
307     }
308    
309     if (bytes_written < 0) {
310     if (errno == EWOULDBLOCK || errno == EAGAIN)
311     return -1;
312     /* request blocked at the pipe level, but keep going */
313     else {
314     if (errno != EPIPE) {
315     log_error_doc(req);
316     /* Can generate lots of log entries, */
317     perror("write");
318     /* OK to disable if your logs get too big */
319     }
320     req->status = DEAD;
321     return 0;
322     }
323     }
324     req->filepos += bytes_written;
325    
326     if (req->filepos == req->filesize) { /* EOF */
327     return 0;
328     } else
329     return 1; /* more to do */
330     }
331    
332     /*
333     * Name: get_dir
334     * Description: Called from process_get if the request is a directory.
335     * statbuf must describe directory on input, since we may need its
336     * device, inode, and mtime.
337     * statbuf is updated, since we may need to check mtimes of a cache.
338     * returns:
339     * -1 error
340     * 0 cgi (either gunzip or auto-generated)
341     * >0 file descriptor of file
342     */
343    
344     int get_dir(request * req, struct stat *statbuf)
345     {
346    
347     char pathname_with_index[MAX_PATH_LENGTH];
348     int data_fd;
349    
350     if (directory_index) { /* look for index.html first?? */
351     strcpy(pathname_with_index, req->pathname);
352     strcat(pathname_with_index, directory_index);
353     /*
354     sprintf(pathname_with_index, "%s%s", req->pathname, directory_index);
355     */
356    
357     data_fd = open(pathname_with_index, O_RDONLY);
358    
359     if (data_fd != -1) { /* user's index file */
360     strcpy(req->request_uri, directory_index); /* for mimetype */
361     fstat(data_fd, statbuf);
362     return data_fd;
363     }
364     if (errno == EACCES) {
365     send_r_forbidden(req);
366     return -1;
367     } else if (errno != ENOENT) {
368     /* if there is an error *other* than EACCES or ENOENT */
369     send_r_not_found(req);
370     return -1;
371     }
372     #ifdef GUNZIP
373     /* if we are here, trying index.html didn't work
374     * try index.html.gz
375     */
376     strcat(pathname_with_index, ".gz");
377     data_fd = open(pathname_with_index, O_RDONLY);
378     if (data_fd != -1) { /* user's index file */
379     close(data_fd);
380    
381     req->response_status = R_REQUEST_OK;
382     SQUASH_KA(req);
383     if (req->pathname)
384     free(req->pathname);
385     req->pathname = strdup(pathname_with_index);
386     if (!req->pathname) {
387     log_error_time();
388     perror("strdup");
389     send_r_error(req);
390     return 0;
391     }
392     if (!req->simple) {
393     req_write(req, "HTTP/1.0 200 OK-GUNZIP\r\n");
394     print_http_headers(req);
395     print_last_modified(req);
396     req_write(req, "Content-Type: ");
397     req_write(req, get_mime_type(directory_index));
398     req_write(req, "\r\n\r\n");
399     req_flush(req);
400     }
401     if (req->method == M_HEAD)
402     return 0;
403     return init_cgi(req);
404     }
405     #endif
406     }
407    
408     /* only here if index.html, index.html.gz don't exist */
409     if (dirmaker != NULL) { /* don't look for index.html... maybe automake? */
410     req->response_status = R_REQUEST_OK;
411     SQUASH_KA(req);
412    
413     /* the indexer should take care of all headers */
414     if (!req->simple) {
415     req_write(req, "HTTP/1.0 200 OK\r\n");
416     print_http_headers(req);
417     print_last_modified(req);
418     req_write(req, "Content-Type: text/html\r\n\r\n");
419     req_flush(req);
420     }
421     if (req->method == M_HEAD)
422     return 0;
423    
424     return init_cgi(req);
425     /* in this case, 0 means success */
426     } else if (cachedir) {
427     return get_cachedir_file(req, statbuf);
428     } else { /* neither index.html nor autogenerate are allowed */
429     send_r_forbidden(req);
430     return -1; /* nothing worked */
431     }
432     }
433    
434     int get_cachedir_file(request * req, struct stat *statbuf)
435     {
436    
437     char pathname_with_index[MAX_PATH_LENGTH];
438     int data_fd;
439     time_t real_dir_mtime;
440    
441     real_dir_mtime = statbuf->st_mtime;
442     sprintf(pathname_with_index, "%s/dir.%d.%ld",
443     cachedir, (int) statbuf->st_dev, statbuf->st_ino);
444     data_fd = open(pathname_with_index, O_RDONLY);
445    
446     if (data_fd != -1) { /* index cache */
447    
448     fstat(data_fd, statbuf);
449     if (statbuf->st_mtime > real_dir_mtime) {
450     statbuf->st_mtime = real_dir_mtime; /* lie */
451     strcpy(req->request_uri, directory_index); /* for mimetype */
452     return data_fd;
453     }
454     close(data_fd);
455     unlink(pathname_with_index); /* cache is stale, delete it */
456     }
457     if (index_directory(req, pathname_with_index) == -1)
458     return -1;
459    
460     data_fd = open(pathname_with_index, O_RDONLY); /* Last chance */
461     if (data_fd != -1) {
462     strcpy(req->request_uri, directory_index); /* for mimetype */
463     fstat(data_fd, statbuf);
464     statbuf->st_mtime = real_dir_mtime; /* lie */
465     return data_fd;
466     }
467    
468     boa_perror(req, "re-opening dircache");
469     return -1; /* Nothing worked. */
470    
471     }
472    
473     /*
474     * Name: index_directory
475     * Description: Called from get_cachedir_file if a directory html
476     * has to be generated on the fly
477     * returns -1 for problem, else 0
478     * This version is the fastest, ugliest, and most accurate yet.
479     * It solves the "stale size or type" problem by not ever giving
480     * the size or type. This also speeds it up since no per-file
481     * stat() is required.
482     */
483    
484     int index_directory(request * req, char *dest_filename)
485     {
486     DIR *request_dir;
487     FILE *fdstream;
488     struct dirent *dirbuf;
489     int bytes = 0;
490     char *escname = NULL;
491    
492     if (chdir(req->pathname) == -1) {
493     if (errno == EACCES || errno == EPERM) {
494     send_r_forbidden(req);
495     } else {
496     log_error_doc(req);
497     perror("chdir");
498     send_r_bad_request(req);
499     }
500     return -1;
501     }
502    
503     request_dir = opendir(".");
504     if (request_dir == NULL) {
505     int errno_save = errno;
506     send_r_error(req);
507     log_error_time();
508     fprintf(stderr, "directory \"%s\": ", req->pathname);
509     errno = errno_save;
510     perror("opendir");
511     return -1;
512     }
513    
514     fdstream = fopen(dest_filename, "w");
515     if (fdstream == NULL) {
516     boa_perror(req, "dircache fopen");
517     closedir(request_dir);
518     return -1;
519     }
520    
521     bytes += fprintf(fdstream,
522     "<HTML><HEAD>\n<TITLE>Index of %s</TITLE>\n</HEAD>\n\n",
523     req->request_uri);
524     bytes +=
525     fprintf(fdstream, "<BODY>\n\n<H2>Index of %s</H2>\n\n<PRE>\n",
526     req->request_uri);
527    
528     while ((dirbuf = readdir(request_dir))) {
529     if (!strcmp(dirbuf->d_name, "."))
530     continue;
531    
532     if (!strcmp(dirbuf->d_name, "..")) {
533     bytes += fprintf(fdstream,
534     " [DIR] <A HREF=\"../\">Parent Directory</A>\n");
535     continue;
536     }
537    
538     if ((escname = escape_string(dirbuf->d_name, NULL)) != NULL) {
539     bytes += fprintf(fdstream, " <A HREF=\"%s\">%s</A>\n",
540     escname, dirbuf->d_name);
541     free(escname);
542     escname = NULL;
543     }
544     }
545     closedir(request_dir);
546     bytes += fprintf(fdstream, "</PRE>\n\n</BODY>\n</HTML>\n");
547    
548     fclose(fdstream);
549    
550     chdir(server_root);
551    
552     req->filesize = bytes; /* for logging transfer size */
553     return 0; /* success */
554     }

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26