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

Contents of /hydra/src/get.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.7 - (show annotations)
Mon Sep 23 19:28:41 2002 UTC (21 years, 6 months ago) by nmav
Branch: MAIN
CVS Tags: BOAS_WITH_RANGES_AND_CGI
Changes since 1.6: +15 -6 lines
File MIME type: text/plain
Cleaned up the CGI handling. Now gunzip, indexer are treated like real cgis. Several other cleanups.

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

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26