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

Annotation of /hydra/src/get.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.6 - (hide annotations)
Mon Sep 23 13:24:19 2002 UTC (21 years, 7 months ago) by nmav
Branch: MAIN
Changes since 1.5: +2 -1 lines
File MIME type: text/plain
*** empty log message ***

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

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26