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

Annotation of /hydra/src/get.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.14 - (hide annotations)
Sun Sep 29 08:02:56 2002 UTC (21 years, 6 months ago) by nmav
Branch: MAIN
Changes since 1.13: +3 -10 lines
File MIME type: text/plain
Added create_url() function.

1 nmav 1.1 /*
2 nmav 1.13 * Hydra, an http server
3 nmav 1.1 * 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.14 /* $Id: get.c,v 1.13 2002/09/28 16:32:37 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    
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     if (data_fd == -1) {
52     log_error_doc(req);
53     errno = saved_errno;
54     perror("document open");
55    
56     if (saved_errno == ENOENT)
57     send_r_not_found(req);
58     else if (saved_errno == EACCES)
59     send_r_forbidden(req);
60     else
61     send_r_bad_request(req);
62     return 0;
63     }
64    
65     if (fstat( data_fd, &statbuf) == -1) {
66     /* this is quite impossible, since the file
67     * was opened before.
68     */
69     close(data_fd);
70 nmav 1.5 send_r_not_found(req);
71 nmav 1.1 return 0;
72     }
73    
74     if (S_ISDIR(statbuf.st_mode)) { /* directory */
75     close(data_fd); /* close dir */
76    
77     if (req->pathname[strlen(req->pathname) - 1] != '/') {
78     char buffer[3 * MAX_PATH_LENGTH + 128];
79 nmav 1.7
80 nmav 1.14 create_url( buffer, sizeof(buffer), req->secure, req->hostname,
81     params->server_s[req->secure].port, req->request_uri);
82 nmav 1.1
83     send_r_moved_perm(req, buffer);
84     return 0;
85     }
86     data_fd = get_dir(req, &statbuf); /* updates statbuf */
87    
88     if (data_fd == -1) /* couldn't do it */
89     return 0; /* errors reported by get_dir */
90     else if (data_fd <= 1)
91     /* data_fd == 0 -> close it down, 1 -> continue */
92     return data_fd;
93     /* else, data_fd contains the fd of the file... */
94     }
95 nmav 1.5
96 nmav 1.1 if (req->if_modified_since &&
97     !modified_since(&(statbuf.st_mtime), req->if_modified_since)) {
98     send_r_not_modified(req);
99     close(data_fd);
100     return 0;
101     }
102     req->filesize = statbuf.st_size;
103     req->last_modified = statbuf.st_mtime;
104 nmav 1.5
105     if (req->range_stop == 0)
106     req->range_stop = statbuf.st_size;
107    
108     /* out of range! */
109     if (req->range_start > statbuf.st_size ||
110     req->range_stop > statbuf.st_size ||
111     req->range_stop < req->range_start) {
112     send_r_range_unsatisfiable(req);
113     close(data_fd);
114     return 0;
115     }
116 nmav 1.1
117     if (req->method == M_HEAD || req->filesize == 0) {
118     send_r_request_ok(req);
119     close(data_fd);
120     return 0;
121     }
122    
123 nmav 1.9 if (req->range_stop > max_file_size_cache) {
124 nmav 1.5
125     if ( req->range_start == 0 && req->range_stop == statbuf.st_size)
126     send_r_request_ok(req); /* All's well */
127     else {
128     /* if ranges were used, then lseek to the start given
129     */
130     if ( lseek( data_fd, req->range_start, SEEK_SET) == (off_t) -1) {
131     close(data_fd);
132     send_r_not_found(req);
133     return 0;
134     }
135     send_r_request_partial(req);/* All's well */
136     }
137    
138 nmav 1.1 req->status = PIPE_READ;
139     req->cgi_status = CGI_BUFFER;
140     req->data_fd = data_fd;
141     req_flush(req); /* this should *always* complete due to
142     the size of the I/O buffers */
143     req->header_line = req->header_end = req->buffer;
144 nmav 1.5 req->pipe_range_stop = req->range_stop;
145 nmav 1.1 return 1;
146     }
147    
148 nmav 1.5 if (req->range_stop == 0) { /* done */
149 nmav 1.1 send_r_request_ok(req); /* All's well *so far* */
150     close(data_fd);
151     return 1;
152     }
153    
154     /* NOTE: I (Jon Nelson) tried performing a read(2)
155     * into the output buffer provided the file data would
156     * fit, before mmapping, and if successful, writing that
157     * and stopping there -- all to avoid the cost
158     * of a mmap. Oddly, it was *slower* in benchmarks.
159     */
160 nmav 1.9 if (max_files_cache > 0) {
161     req->mmap_entry_var = find_mmap(data_fd, &statbuf);
162     if (req->mmap_entry_var == NULL) {
163     req->buffer_end = 0;
164     if (errno == ENOENT)
165     send_r_not_found(req);
166     else if (errno == EACCES)
167     send_r_forbidden(req);
168     else
169     send_r_bad_request(req);
170     close(data_fd);
171     return 0;
172     }
173     req->data_mem = req->mmap_entry_var->mmap;
174     } else { /* File caching is disabled.
175     */
176     req->data_mem = mmap(0, req->range_stop, PROT_READ, MAP_OPTIONS, data_fd, 0);
177 nmav 1.1 }
178 nmav 1.9
179 nmav 1.1 close(data_fd); /* close data file */
180    
181 nmav 1.3 if ( req->data_mem == MAP_FAILED) {
182 nmav 1.1 boa_perror(req, "mmap");
183     return 0;
184     }
185    
186 nmav 1.5 if ( req->range_start == 0 && req->range_stop == statbuf.st_size)
187     send_r_request_ok(req); /* All's well */
188     else
189     send_r_request_partial(req);/* All's well */
190 nmav 1.1
191     bytes = BUFFER_SIZE - req->buffer_end;
192    
193     /* bytes is now how much the buffer can hold
194     * after the headers
195     */
196 nmav 1.5 req->filepos = req->range_start;
197    
198 nmav 1.1 if (bytes > 0) {
199 nmav 1.5 if (bytes > req->range_stop - req->range_start)
200     bytes = req->range_stop - req->range_start;
201 nmav 1.1
202     if (setjmp(params->env) == 0) {
203     params->handle_sigbus = 1;
204 nmav 1.5 memcpy(req->buffer + req->buffer_end, &req->data_mem[req->filepos],
205     bytes);
206 nmav 1.1 params->handle_sigbus = 0;
207     /* OK, SIGBUS **after** this point is very bad! */
208     } else {
209 nmav 1.4 char buf[30];
210 nmav 1.1 /* sigbus! */
211     log_error_doc(req);
212     reset_output_buffer(req);
213     send_r_error(req);
214 nmav 1.4 get_commonlog_time( buf);
215 nmav 1.1 fprintf(stderr, "%sGot SIGBUS in memcpy!\n",
216 nmav 1.4 buf);
217 nmav 1.1 return 0;
218     }
219     req->buffer_end += bytes;
220     req->filepos += bytes;
221 nmav 1.5 if (req->range_stop == req->filepos) {
222 nmav 1.1 req_flush(req);
223     req->status = DONE;
224     }
225     }
226    
227     /* We lose statbuf here, so make sure response has been sent */
228     return 1;
229     }
230    
231    
232     /*
233     * Name: process_get
234     * Description: Writes a chunk of data to the socket.
235     *
236     * Return values:
237     * -1: request blocked, move to blocked queue
238     * 0: EOF or error, close it down
239     * 1: successful write, recycle in ready queue
240     */
241    
242     int process_get(server_params * params, request * req)
243     {
244     int bytes_written;
245     volatile int bytes_to_write;
246    
247 nmav 1.5 bytes_to_write = req->range_stop - req->filepos;
248 nmav 1.1 if (bytes_to_write > SOCKETBUF_SIZE)
249     bytes_to_write = SOCKETBUF_SIZE;
250    
251    
252     if (setjmp(params->env) == 0) {
253     params->handle_sigbus = 1;
254    
255     bytes_written =
256     socket_send(req, req->data_mem + req->filepos, bytes_to_write);
257    
258     params->handle_sigbus = 0;
259     /* OK, SIGBUS **after** this point is very bad! */
260     } else {
261 nmav 1.4 char buf[30];
262 nmav 1.1 /* sigbus! */
263     log_error_doc(req);
264     /* sending an error here is inappropriate
265     * if we are here, the file is mmapped, and thus,
266     * a content-length has been sent. If we send fewer bytes
267     * the client knows there has been a problem.
268     * We run the risk of accidentally sending the right number
269     * of bytes (or a few too many) and the client
270     * won't be the wiser.
271     */
272     req->status = DEAD;
273 nmav 1.4 get_commonlog_time( buf);
274     fprintf(stderr, "%sGot SIGBUS in write(2)!\n", buf);
275 nmav 1.1 return 0;
276     }
277    
278     if (bytes_written < 0) {
279     if (errno == EWOULDBLOCK || errno == EAGAIN)
280     return -1;
281     /* request blocked at the pipe level, but keep going */
282     else {
283     if (errno != EPIPE) {
284     log_error_doc(req);
285     /* Can generate lots of log entries, */
286     perror("write");
287     /* OK to disable if your logs get too big */
288     }
289     req->status = DEAD;
290     return 0;
291     }
292     }
293     req->filepos += bytes_written;
294    
295 nmav 1.5 if (req->filepos == req->range_stop) { /* EOF */
296 nmav 1.1 return 0;
297     } else
298     return 1; /* more to do */
299     }
300    
301     /*
302     * Name: get_dir
303     * Description: Called from process_get if the request is a directory.
304     * statbuf must describe directory on input, since we may need its
305     * device, inode, and mtime.
306     * statbuf is updated, since we may need to check mtimes of a cache.
307     * returns:
308     * -1 error
309     * 0 cgi (either gunzip or auto-generated)
310     * >0 file descriptor of file
311     */
312    
313     int get_dir(request * req, struct stat *statbuf)
314     {
315    
316 nmav 1.10 char* directory_index;
317 nmav 1.1 int data_fd;
318    
319 nmav 1.10 directory_index = find_and_open_directory_index( req->pathname, 0, &data_fd);
320    
321 nmav 1.1 if (directory_index) { /* look for index.html first?? */
322     if (data_fd != -1) { /* user's index file */
323 nmav 1.12 int ret;
324    
325     /* Check if we can execute the file
326     */
327    
328     strcat(req->request_uri, directory_index);
329    
330     ret = is_executable_cgi( req, directory_index);
331     if ( ret != 0) { /* it is a CGI */
332     close( data_fd); /* we don't need it */
333     if (ret == -1) {
334     send_r_error(req);
335     return -1;
336     }
337     return init_cgi(req);
338     }
339    
340     /* Not a cgi */
341    
342 nmav 1.1 fstat(data_fd, statbuf);
343     return data_fd;
344     }
345     if (errno == EACCES) {
346     send_r_forbidden(req);
347     return -1;
348     } else if (errno != ENOENT) {
349     /* if there is an error *other* than EACCES or ENOENT */
350     send_r_not_found(req);
351     return -1;
352     }
353     }
354    
355     /* only here if index.html, index.html.gz don't exist */
356     if (dirmaker != NULL) { /* don't look for index.html... maybe automake? */
357     req->response_status = R_REQUEST_OK;
358     SQUASH_KA(req);
359    
360     /* the indexer should take care of all headers */
361     if (!req->simple) {
362 nmav 1.11 req_write(req, HTTP_VERSION" 200 OK\r\n");
363 nmav 1.1 print_http_headers(req);
364     print_last_modified(req);
365 nmav 1.11 req_write(req, "Content-Type: " TEXT_HTML CRLF CRLF);
366 nmav 1.1 req_flush(req);
367     }
368     if (req->method == M_HEAD)
369     return 0;
370    
371 nmav 1.7 req->is_cgi = INDEXER_CGI;
372 nmav 1.1 return init_cgi(req);
373     /* in this case, 0 means success */
374     } else if (cachedir) {
375     return get_cachedir_file(req, statbuf);
376     } else { /* neither index.html nor autogenerate are allowed */
377     send_r_forbidden(req);
378     return -1; /* nothing worked */
379     }
380     }
381    
382     int get_cachedir_file(request * req, struct stat *statbuf)
383     {
384    
385     char pathname_with_index[MAX_PATH_LENGTH];
386     int data_fd;
387     time_t real_dir_mtime;
388    
389     real_dir_mtime = statbuf->st_mtime;
390     sprintf(pathname_with_index, "%s/dir.%d.%ld",
391     cachedir, (int) statbuf->st_dev, statbuf->st_ino);
392     data_fd = open(pathname_with_index, O_RDONLY);
393    
394     if (data_fd != -1) { /* index cache */
395    
396     fstat(data_fd, statbuf);
397     if (statbuf->st_mtime > real_dir_mtime) {
398     statbuf->st_mtime = real_dir_mtime; /* lie */
399 nmav 1.10 strcpy(req->request_uri, find_default_directory_index()); /* for mimetype */
400 nmav 1.1 return data_fd;
401     }
402     close(data_fd);
403     unlink(pathname_with_index); /* cache is stale, delete it */
404     }
405     if (index_directory(req, pathname_with_index) == -1)
406     return -1;
407    
408     data_fd = open(pathname_with_index, O_RDONLY); /* Last chance */
409     if (data_fd != -1) {
410 nmav 1.10 strcpy(req->request_uri, find_default_directory_index()); /* for mimetype */
411 nmav 1.1 fstat(data_fd, statbuf);
412     statbuf->st_mtime = real_dir_mtime; /* lie */
413     return data_fd;
414     }
415    
416     boa_perror(req, "re-opening dircache");
417     return -1; /* Nothing worked. */
418    
419     }
420    
421     /*
422     * Name: index_directory
423     * Description: Called from get_cachedir_file if a directory html
424     * has to be generated on the fly
425     * returns -1 for problem, else 0
426     * This version is the fastest, ugliest, and most accurate yet.
427     * It solves the "stale size or type" problem by not ever giving
428     * the size or type. This also speeds it up since no per-file
429     * stat() is required.
430     */
431    
432     int index_directory(request * req, char *dest_filename)
433     {
434     DIR *request_dir;
435     FILE *fdstream;
436     struct dirent *dirbuf;
437     int bytes = 0;
438     char *escname = NULL;
439    
440     if (chdir(req->pathname) == -1) {
441     if (errno == EACCES || errno == EPERM) {
442     send_r_forbidden(req);
443     } else {
444     log_error_doc(req);
445     perror("chdir");
446     send_r_bad_request(req);
447     }
448     return -1;
449     }
450    
451     request_dir = opendir(".");
452     if (request_dir == NULL) {
453     int errno_save = errno;
454     send_r_error(req);
455     log_error_time();
456     fprintf(stderr, "directory \"%s\": ", req->pathname);
457     errno = errno_save;
458     perror("opendir");
459     return -1;
460     }
461    
462     fdstream = fopen(dest_filename, "w");
463     if (fdstream == NULL) {
464     boa_perror(req, "dircache fopen");
465     closedir(request_dir);
466     return -1;
467     }
468    
469     bytes += fprintf(fdstream,
470     "<HTML><HEAD>\n<TITLE>Index of %s</TITLE>\n</HEAD>\n\n",
471     req->request_uri);
472     bytes +=
473     fprintf(fdstream, "<BODY>\n\n<H2>Index of %s</H2>\n\n<PRE>\n",
474     req->request_uri);
475    
476     while ((dirbuf = readdir(request_dir))) {
477     if (!strcmp(dirbuf->d_name, "."))
478     continue;
479    
480     if (!strcmp(dirbuf->d_name, "..")) {
481     bytes += fprintf(fdstream,
482     " [DIR] <A HREF=\"../\">Parent Directory</A>\n");
483     continue;
484     }
485    
486     if ((escname = escape_string(dirbuf->d_name, NULL)) != NULL) {
487     bytes += fprintf(fdstream, " <A HREF=\"%s\">%s</A>\n",
488     escname, dirbuf->d_name);
489     free(escname);
490     escname = NULL;
491     }
492     }
493     closedir(request_dir);
494     bytes += fprintf(fdstream, "</PRE>\n\n</BODY>\n</HTML>\n");
495    
496     fclose(fdstream);
497    
498     chdir(server_root);
499    
500     req->filesize = bytes; /* for logging transfer size */
501     return 0; /* success */
502     }

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26