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

Annotation of /hydra/src/get.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.1 - (hide annotations)
Sat Sep 21 13:53:21 2002 UTC (21 years, 7 months ago) by nmav
Branch: MAIN
Branch point for: boas
File MIME type: text/plain
Initial revision

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     /* $Id: get.c,v 1.76.2.5 2002/07/26 03:05:59 jnelson Exp $*/
24    
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     req->mmap_entry_var = find_mmap(data_fd, &statbuf);
152     if (req->mmap_entry_var == NULL) {
153     req->buffer_end = 0;
154     if (errno == ENOENT)
155     send_r_not_found(req);
156     else if (errno == EACCES)
157     send_r_forbidden(req);
158     else
159     send_r_bad_request(req);
160     close(data_fd);
161     return 0;
162     }
163     req->data_mem = req->mmap_entry_var->mmap;
164     //req->data_mem = mmap(0, statbuf.st_size, PROT_READ, MAP_OPTIONS, data_fd, 0);
165     close(data_fd); /* close data file */
166    
167     if ((long) req->data_mem == -1) {
168     boa_perror(req, "mmap");
169     return 0;
170     }
171    
172     send_r_request_ok(req); /* All's well */
173    
174     bytes = BUFFER_SIZE - req->buffer_end;
175    
176     /* bytes is now how much the buffer can hold
177     * after the headers
178     */
179    
180     if (bytes > 0) {
181     if (bytes > req->filesize)
182     bytes = req->filesize;
183    
184     if (setjmp(params->env) == 0) {
185     params->handle_sigbus = 1;
186     memcpy(req->buffer + req->buffer_end, req->data_mem, bytes);
187     params->handle_sigbus = 0;
188     /* OK, SIGBUS **after** this point is very bad! */
189     } else {
190     /* sigbus! */
191     log_error_doc(req);
192     reset_output_buffer(req);
193     send_r_error(req);
194     fprintf(stderr, "%sGot SIGBUS in memcpy!\n",
195     get_commonlog_time());
196     return 0;
197     }
198     req->buffer_end += bytes;
199     req->filepos += bytes;
200     if (req->filesize == req->filepos) {
201     req_flush(req);
202     req->status = DONE;
203     }
204     }
205    
206     /* We lose statbuf here, so make sure response has been sent */
207     return 1;
208     }
209    
210     #ifdef GUNZIP
211    
212     /* If a file could not be opened, then this function
213     * is called, which tries to open the gziped file.
214     * Return values:
215     * 0: finished or error, request will be freed
216     * 1: success
217     * -1: failed
218     */
219     static int gzip_open(request * req)
220     {
221     char gzip_pathname[MAX_PATH_LENGTH];
222     int len, data_fd;
223    
224     len = strlen(req->pathname);
225    
226     memcpy(gzip_pathname, req->pathname, len);
227     memcpy(gzip_pathname + len, ".gz", 3);
228     gzip_pathname[len + 3] = '\0';
229     data_fd = open(gzip_pathname, O_RDONLY);
230     if (data_fd != -1) {
231     close(data_fd);
232    
233     req->response_status = R_REQUEST_OK;
234     if (req->pathname)
235     free(req->pathname);
236     req->pathname = strdup(gzip_pathname);
237     if (!req->pathname) {
238     log_error_time();
239     perror("strdup");
240     send_r_error(req);
241     return 0;
242     }
243     if (!req->simple) {
244     req_write(req, "HTTP/1.0 200 OK-GUNZIP\r\n");
245     print_http_headers(req);
246     print_content_type(req);
247     print_last_modified(req);
248     req_write(req, "\r\n");
249     req_flush(req);
250     }
251     if (req->method == M_HEAD)
252     return 0;
253    
254     return 1;
255    
256     }
257    
258     return -1;
259     }
260     #endif
261    
262     /*
263     * Name: process_get
264     * Description: Writes a chunk of data to the socket.
265     *
266     * Return values:
267     * -1: request blocked, move to blocked queue
268     * 0: EOF or error, close it down
269     * 1: successful write, recycle in ready queue
270     */
271    
272     int process_get(server_params * params, request * req)
273     {
274     int bytes_written;
275     volatile int bytes_to_write;
276    
277     bytes_to_write = req->filesize - req->filepos;
278     if (bytes_to_write > SOCKETBUF_SIZE)
279     bytes_to_write = SOCKETBUF_SIZE;
280    
281    
282     if (setjmp(params->env) == 0) {
283     params->handle_sigbus = 1;
284    
285     bytes_written =
286     socket_send(req, req->data_mem + req->filepos, bytes_to_write);
287    
288     params->handle_sigbus = 0;
289     /* OK, SIGBUS **after** this point is very bad! */
290     } else {
291     /* sigbus! */
292     log_error_doc(req);
293     /* sending an error here is inappropriate
294     * if we are here, the file is mmapped, and thus,
295     * a content-length has been sent. If we send fewer bytes
296     * the client knows there has been a problem.
297     * We run the risk of accidentally sending the right number
298     * of bytes (or a few too many) and the client
299     * won't be the wiser.
300     */
301     req->status = DEAD;
302     fprintf(stderr, "%sGot SIGBUS in write(2)!\n", get_commonlog_time());
303     return 0;
304     }
305    
306     if (bytes_written < 0) {
307     if (errno == EWOULDBLOCK || errno == EAGAIN)
308     return -1;
309     /* request blocked at the pipe level, but keep going */
310     else {
311     if (errno != EPIPE) {
312     log_error_doc(req);
313     /* Can generate lots of log entries, */
314     perror("write");
315     /* OK to disable if your logs get too big */
316     }
317     req->status = DEAD;
318     return 0;
319     }
320     }
321     req->filepos += bytes_written;
322    
323     if (req->filepos == req->filesize) { /* EOF */
324     return 0;
325     } else
326     return 1; /* more to do */
327     }
328    
329     /*
330     * Name: get_dir
331     * Description: Called from process_get if the request is a directory.
332     * statbuf must describe directory on input, since we may need its
333     * device, inode, and mtime.
334     * statbuf is updated, since we may need to check mtimes of a cache.
335     * returns:
336     * -1 error
337     * 0 cgi (either gunzip or auto-generated)
338     * >0 file descriptor of file
339     */
340    
341     int get_dir(request * req, struct stat *statbuf)
342     {
343    
344     char pathname_with_index[MAX_PATH_LENGTH];
345     int data_fd;
346    
347     if (directory_index) { /* look for index.html first?? */
348     strcpy(pathname_with_index, req->pathname);
349     strcat(pathname_with_index, directory_index);
350     /*
351     sprintf(pathname_with_index, "%s%s", req->pathname, directory_index);
352     */
353    
354     data_fd = open(pathname_with_index, O_RDONLY);
355    
356     if (data_fd != -1) { /* user's index file */
357     strcpy(req->request_uri, directory_index); /* for mimetype */
358     fstat(data_fd, statbuf);
359     return data_fd;
360     }
361     if (errno == EACCES) {
362     send_r_forbidden(req);
363     return -1;
364     } else if (errno != ENOENT) {
365     /* if there is an error *other* than EACCES or ENOENT */
366     send_r_not_found(req);
367     return -1;
368     }
369     #ifdef GUNZIP
370     /* if we are here, trying index.html didn't work
371     * try index.html.gz
372     */
373     strcat(pathname_with_index, ".gz");
374     data_fd = open(pathname_with_index, O_RDONLY);
375     if (data_fd != -1) { /* user's index file */
376     close(data_fd);
377    
378     req->response_status = R_REQUEST_OK;
379     SQUASH_KA(req);
380     if (req->pathname)
381     free(req->pathname);
382     req->pathname = strdup(pathname_with_index);
383     if (!req->pathname) {
384     log_error_time();
385     perror("strdup");
386     send_r_error(req);
387     return 0;
388     }
389     if (!req->simple) {
390     req_write(req, "HTTP/1.0 200 OK-GUNZIP\r\n");
391     print_http_headers(req);
392     print_last_modified(req);
393     req_write(req, "Content-Type: ");
394     req_write(req, get_mime_type(directory_index));
395     req_write(req, "\r\n\r\n");
396     req_flush(req);
397     }
398     if (req->method == M_HEAD)
399     return 0;
400     return init_cgi(req);
401     }
402     #endif
403     }
404    
405     /* only here if index.html, index.html.gz don't exist */
406     if (dirmaker != NULL) { /* don't look for index.html... maybe automake? */
407     req->response_status = R_REQUEST_OK;
408     SQUASH_KA(req);
409    
410     /* the indexer should take care of all headers */
411     if (!req->simple) {
412     req_write(req, "HTTP/1.0 200 OK\r\n");
413     print_http_headers(req);
414     print_last_modified(req);
415     req_write(req, "Content-Type: text/html\r\n\r\n");
416     req_flush(req);
417     }
418     if (req->method == M_HEAD)
419     return 0;
420    
421     return init_cgi(req);
422     /* in this case, 0 means success */
423     } else if (cachedir) {
424     return get_cachedir_file(req, statbuf);
425     } else { /* neither index.html nor autogenerate are allowed */
426     send_r_forbidden(req);
427     return -1; /* nothing worked */
428     }
429     }
430    
431     int get_cachedir_file(request * req, struct stat *statbuf)
432     {
433    
434     char pathname_with_index[MAX_PATH_LENGTH];
435     int data_fd;
436     time_t real_dir_mtime;
437    
438     real_dir_mtime = statbuf->st_mtime;
439     sprintf(pathname_with_index, "%s/dir.%d.%ld",
440     cachedir, (int) statbuf->st_dev, statbuf->st_ino);
441     data_fd = open(pathname_with_index, O_RDONLY);
442    
443     if (data_fd != -1) { /* index cache */
444    
445     fstat(data_fd, statbuf);
446     if (statbuf->st_mtime > real_dir_mtime) {
447     statbuf->st_mtime = real_dir_mtime; /* lie */
448     strcpy(req->request_uri, directory_index); /* for mimetype */
449     return data_fd;
450     }
451     close(data_fd);
452     unlink(pathname_with_index); /* cache is stale, delete it */
453     }
454     if (index_directory(req, pathname_with_index) == -1)
455     return -1;
456    
457     data_fd = open(pathname_with_index, O_RDONLY); /* Last chance */
458     if (data_fd != -1) {
459     strcpy(req->request_uri, directory_index); /* for mimetype */
460     fstat(data_fd, statbuf);
461     statbuf->st_mtime = real_dir_mtime; /* lie */
462     return data_fd;
463     }
464    
465     boa_perror(req, "re-opening dircache");
466     return -1; /* Nothing worked. */
467    
468     }
469    
470     /*
471     * Name: index_directory
472     * Description: Called from get_cachedir_file if a directory html
473     * has to be generated on the fly
474     * returns -1 for problem, else 0
475     * This version is the fastest, ugliest, and most accurate yet.
476     * It solves the "stale size or type" problem by not ever giving
477     * the size or type. This also speeds it up since no per-file
478     * stat() is required.
479     */
480    
481     int index_directory(request * req, char *dest_filename)
482     {
483     DIR *request_dir;
484     FILE *fdstream;
485     struct dirent *dirbuf;
486     int bytes = 0;
487     char *escname = NULL;
488    
489     if (chdir(req->pathname) == -1) {
490     if (errno == EACCES || errno == EPERM) {
491     send_r_forbidden(req);
492     } else {
493     log_error_doc(req);
494     perror("chdir");
495     send_r_bad_request(req);
496     }
497     return -1;
498     }
499    
500     request_dir = opendir(".");
501     if (request_dir == NULL) {
502     int errno_save = errno;
503     send_r_error(req);
504     log_error_time();
505     fprintf(stderr, "directory \"%s\": ", req->pathname);
506     errno = errno_save;
507     perror("opendir");
508     return -1;
509     }
510    
511     fdstream = fopen(dest_filename, "w");
512     if (fdstream == NULL) {
513     boa_perror(req, "dircache fopen");
514     closedir(request_dir);
515     return -1;
516     }
517    
518     bytes += fprintf(fdstream,
519     "<HTML><HEAD>\n<TITLE>Index of %s</TITLE>\n</HEAD>\n\n",
520     req->request_uri);
521     bytes +=
522     fprintf(fdstream, "<BODY>\n\n<H2>Index of %s</H2>\n\n<PRE>\n",
523     req->request_uri);
524    
525     while ((dirbuf = readdir(request_dir))) {
526     if (!strcmp(dirbuf->d_name, "."))
527     continue;
528    
529     if (!strcmp(dirbuf->d_name, "..")) {
530     bytes += fprintf(fdstream,
531     " [DIR] <A HREF=\"../\">Parent Directory</A>\n");
532     continue;
533     }
534    
535     if ((escname = escape_string(dirbuf->d_name, NULL)) != NULL) {
536     bytes += fprintf(fdstream, " <A HREF=\"%s\">%s</A>\n",
537     escname, dirbuf->d_name);
538     free(escname);
539     escname = NULL;
540     }
541     }
542     closedir(request_dir);
543     bytes += fprintf(fdstream, "</PRE>\n\n</BODY>\n</HTML>\n");
544    
545     fclose(fdstream);
546    
547     chdir(server_root);
548    
549     req->filesize = bytes; /* for logging transfer size */
550     return 0; /* success */
551     }

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26