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

Contents of /hydra/src/get.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.5 - (show annotations)
Mon Sep 23 12:48:59 2002 UTC (21 years, 6 months ago) by nmav
Branch: MAIN
Changes since 1.4: +46 -14 lines
File MIME type: text/plain
Added limited support for HTTP/1.1 ranges.

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

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26