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

Contents of /hydra/src/get.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.1.1.1 - (show annotations) (vendor branch)
Sat Sep 21 13:53:21 2002 UTC (21 years, 6 months ago) by nmav
Branch: boas
CVS Tags: start
Changes since 1.1: +0 -0 lines
File MIME type: text/plain
Imported sources

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

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26