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

Contents of /hydra/src/get.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.3 - (show annotations)
Sun Sep 22 07:10:57 2002 UTC (21 years, 6 months ago) by nmav
Branch: MAIN
Changes since 1.2: +2 -2 lines
File MIME type: text/plain
More improvements on the mmap caching code. Removed the MMAP_LIST_USE_MAX. Now the upper limit is the size of the mmap list.

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

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26