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

Contents of /hydra/src/get.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.14 - (show annotations)
Sun Sep 29 08:02:56 2002 UTC (21 years, 6 months ago) by nmav
Branch: MAIN
Changes since 1.13: +3 -10 lines
File MIME type: text/plain
Added create_url() function.

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

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26