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

Contents of /hydra/src/get.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.10 - (show annotations)
Wed Sep 25 19:55:53 2002 UTC (21 years, 6 months ago) by nmav
Branch: MAIN
Changes since 1.9: +6 -117 lines
File MIME type: text/plain
Added support for multiple directory indexes. Droped support for uncompressing gziped files.

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

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26