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

Contents of /hydra/src/request.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.1 - (show annotations)
Sat Sep 21 13:53:43 2002 UTC (21 years, 6 months ago) by nmav
Branch: MAIN
Branch point for: boas
File MIME type: text/plain
Initial revision

1 /*
2 * Boa, an http server
3 * Copyright (C) 1995 Paul Phillips <paulp@go2net.com>
4 * Some changes Copyright (C) 1996,97 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: request.c,v 1.112.2.3 2002/07/24 03:03:59 jnelson Exp $*/
25
26 #include "boa.h"
27 #include <stddef.h> /* for offsetof */
28 #include "ssl.h"
29 #include "socket.h"
30
31 #ifdef ENABLE_SMP
32 # include <pthread.h>
33 #endif
34
35 extern int boa_ssl;
36
37 /* function prototypes located in this file only */
38 static void free_request( server_params* params, request ** list_head_addr,
39 request * req);
40
41 /*
42 * Name: new_request
43 * Description: Obtains a request struct off the free list, or if the
44 * free list is empty, allocates memory
45 *
46 * Return value: pointer to initialized request
47 */
48
49 request *new_request(server_params* params)
50 {
51 request *req;
52
53 if (params->request_free) {
54 req = params->request_free; /* first on free list */
55 dequeue(&params->request_free, params->request_free); /* dequeue the head */
56 } else {
57 req = (request *) malloc(sizeof (request));
58 if (!req) {
59 log_error_time();
60 perror("malloc for new request");
61 return NULL;
62 }
63 }
64
65 memset(req, 0, offsetof(request, buffer) + 1);
66 req->data_fd = -1;
67 req->post_data_fd = -1;
68
69 return req;
70 }
71
72 #ifdef ENABLE_SMP
73 static pthread_mutex_t accept_mutex = PTHREAD_MUTEX_INITIALIZER;
74 #endif
75
76 /*
77 * Name: get_request
78 *
79 * Description: Polls the server socket for a request. If one exists,
80 * does some basic initialization and adds it to the ready queue;.
81 */
82
83 void get_request(server_params* params, socket_type server_s)
84 {
85 int fd; /* socket */
86 struct SOCKADDR remote_addr; /* address */
87 struct SOCKADDR salocal;
88 int remote_addrlen = sizeof (struct SOCKADDR);
89 request *conn; /* connection */
90 size_t len;
91 static int system_bufsize = 0; /* Default size of SNDBUF given by system */
92 gnutls_session ssl_state = NULL;
93
94 remote_addr.S_FAMILY = 0xdead;
95
96 #ifdef ENABLE_SMP
97 pthread_mutex_lock( &accept_mutex);
98 #if 0 /* this is slower! */
99 if ( pthread_mutex_trylock( &accept_mutex) == EBUSY) {
100 /* no requests we could handle */
101 server_s.pending_requests = 0;
102 return;
103 }
104 #endif
105 #endif
106
107 fd = accept(server_s.socket, (struct sockaddr *) &remote_addr,
108 &remote_addrlen);
109
110 #ifdef ENABLE_SMP
111 pthread_mutex_unlock( &accept_mutex);
112 #endif
113
114 if (fd == -1) {
115 if (errno != EAGAIN && errno != EWOULDBLOCK)
116 /* abnormal error */
117 WARN("accept");
118 else
119 /* no requests */
120 server_s.pending_requests = 0;
121 return;
122 }
123 if (fd >= FD_SETSIZE) {
124 WARN("Got fd >= FD_SETSIZE.");
125 close(fd);
126 return;
127 }
128 #ifdef DEBUGNONINET
129 /* This shows up due to race conditions in some Linux kernels
130 when the client closes the socket sometime between
131 the select() and accept() syscalls.
132 Code and description by Larry Doolittle <ldoolitt@boa.org>
133 */
134 #define HEX(x) (((x)>9)?(('a'-10)+(x)):('0'+(x)))
135 if (remote_addr.sin_family != AF_INET) {
136 struct sockaddr *bogus = (struct sockaddr *) &remote_addr;
137 char *ap, ablock[44];
138 int i;
139 close(fd);
140 log_error_time();
141 for (ap = ablock, i = 0; i < remote_addrlen && i < 14; i++) {
142 *ap++ = ' ';
143 *ap++ = HEX((bogus->sa_data[i] >> 4) & 0x0f);
144 *ap++ = HEX(bogus->sa_data[i] & 0x0f);
145 }
146 *ap = '\0';
147 fprintf(stderr, "non-INET connection attempt: socket %d, "
148 "sa_family = %hu, sa_data[%d] = %s\n",
149 fd, bogus->sa_family, remote_addrlen, ablock);
150 return;
151 }
152 #endif
153
154 /* XXX Either delete this, or document why it's needed */
155 /* Pointed out 3-Oct-1999 by Paul Saab <paul@mu.org> */
156 #ifdef REUSE_EACH_CLIENT_CONNECTION_SOCKET
157 if ((setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *) &sock_opt,
158 sizeof (sock_opt))) == -1) {
159 DIE("setsockopt: unable to set SO_REUSEADDR");
160 }
161 #endif
162
163 #ifdef ENABLE_SSL
164 if ( server_s.secure) {
165 ssl_state = initialize_ssl_session();
166 if (ssl_state == NULL) {
167 WARN("Could not initialize ssl state.");
168 close(fd);
169 return;
170 }
171
172 gnutls_transport_set_ptr( ssl_state, fd);
173 }
174 #endif
175
176 len = sizeof(salocal);
177
178 if (getsockname(fd, (struct sockaddr *) &salocal, &len) != 0) {
179 WARN("getsockname");
180 close(fd);
181 return;
182 }
183
184 conn = new_request( params);
185 if (!conn) {
186 close(fd);
187 return;
188 }
189 conn->fd = fd;
190 conn->ssl_state = ssl_state;
191
192 if (server_s.secure != 0) conn->secure = 1;
193 else conn->secure = 0;
194
195 if ( server_s.secure != 0)
196 conn->status = FINISH_HANDSHAKE;
197 else conn->status = READ_HEADER;
198
199 conn->header_line = conn->client_stream;
200 conn->time_last = current_time;
201 conn->kacount = ka_max;
202
203 ascii_sockaddr(&salocal, conn->local_ip_addr, NI_MAXHOST);
204
205 /* nonblocking socket */
206 if (set_nonblock_fd(conn->fd) == -1)
207 WARN("fcntl: unable to set new socket to non-block");
208
209 /* set close on exec to true */
210 if (fcntl(conn->fd, F_SETFD, 1) == -1)
211 WARN("fctnl: unable to set close-on-exec for new socket");
212
213 /* Increase buffer size if we have to.
214 * Only ask the system the buffer size on the first request,
215 * and assume all subsequent sockets have the same size.
216 */
217 if (system_bufsize == 0) {
218 len = sizeof (system_bufsize);
219 if (getsockopt
220 (conn->fd, SOL_SOCKET, SO_SNDBUF, &system_bufsize, &len) == 0
221 && len == sizeof (system_bufsize)) {
222 /*
223 fprintf(stderr, "%sgetsockopt reports SNDBUF %d\n",
224 get_commonlog_time(), system_bufsize);
225 */
226 ;
227 } else {
228 WARN("getsockopt(SNDBUF)");
229 system_bufsize = 1;
230 }
231 }
232 if (system_bufsize < params->sockbufsize) {
233 if (setsockopt
234 (conn->fd, SOL_SOCKET, SO_SNDBUF, (void *) &params->sockbufsize,
235 sizeof (params->sockbufsize)) == -1) {
236 WARN("setsockopt: unable to set socket buffer size");
237 #ifdef DIE_ON_ERROR_TUNING_SNDBUF
238 exit(errno);
239 #endif
240 }
241 }
242
243 /* for log file and possible use by CGI programs */
244 ascii_sockaddr(&remote_addr, conn->remote_ip_addr, NI_MAXHOST);
245
246 /* for possible use by CGI programs */
247 conn->remote_port = net_port(&remote_addr);
248
249 params->status.requests++;
250
251 #ifdef USE_TCPNODELAY
252 /* Thanks to Jef Poskanzer <jef@acme.com> for this tweak */
253 {
254 int one = 1;
255 if (setsockopt(conn->fd, IPPROTO_TCP, TCP_NODELAY,
256 (void *) &one, sizeof (one)) == -1) {
257 DIE("setsockopt: unable to set TCP_NODELAY");
258 }
259
260 }
261 #endif
262
263 #ifndef NO_RATE_LIMIT
264 if (conn->fd > max_connections) {
265 send_r_service_unavailable(conn);
266 conn->status = DONE;
267 server_s.pending_requests = 0;
268 }
269 #endif /* NO_RATE_LIMIT */
270
271 params->total_connections++;
272
273 enqueue(&params->request_ready, conn);
274 }
275
276
277 /*
278 * Name: free_request
279 *
280 * Description: Deallocates memory for a finished request and closes
281 * down socket.
282 */
283
284 static void free_request( server_params *params, request ** list_head_addr, request * req)
285 {
286 int i;
287 /* free_request should *never* get called by anything but
288 process_requests */
289
290 if (req->buffer_end && req->status != DEAD) {
291 req->status = DONE;
292 return;
293 }
294 /* put request on the free list */
295 dequeue(list_head_addr, req); /* dequeue from ready or block list */
296
297 if (req->logline) /* access log */
298 log_access(req);
299
300 if (req->mmap_entry_var)
301 release_mmap( req->mmap_entry_var);
302 /* FIXME: Why is it needed? */ else if (req->data_mem)
303 munmap(req->data_mem, req->filesize);
304
305 if (req->data_fd != -1)
306 close(req->data_fd);
307
308
309 if (req->post_data_fd != -1)
310 close(req->post_data_fd);
311
312 if (req->response_status >= 400)
313 params->status.errors++;
314
315 for (i = COMMON_CGI_COUNT; i < req->cgi_env_index; ++i) {
316 if (req->cgi_env[i]) {
317 free(req->cgi_env[i]);
318 } else {
319 log_error_time();
320 fprintf(stderr, "Warning: CGI Environment contains NULL value" \
321 "(index %d of %d).\n", i, req->cgi_env_index);
322 }
323 }
324
325 if (req->pathname)
326 free(req->pathname);
327 if (req->path_info)
328 free(req->path_info);
329 if (req->path_translated)
330 free(req->path_translated);
331 if (req->script_name)
332 free(req->script_name);
333
334 if ((req->keepalive == KA_ACTIVE) &&
335 (req->response_status < 500) && req->kacount > 0) {
336 int bytes_to_move;
337
338 request *conn = new_request( params);
339 if (!conn) {
340 /* errors already reported */
341 enqueue(&params->request_free, req);
342 close(req->fd);
343 params->total_connections--;
344 return;
345 }
346 conn->fd = req->fd;
347
348 #ifdef ENABLE_SSL
349 if ( req->secure != 0) {
350 conn->ssl_state = initialize_ssl_session();
351 conn->secure = 1;
352
353 if (conn->ssl_state == NULL) {
354 enqueue(&params->request_free, req);
355 close(req->fd);
356 params->total_connections--;
357 return;
358 }
359
360 gnutls_transport_set_ptr( conn->ssl_state, conn->fd);
361 conn->status = FINISH_HANDSHAKE;
362 } else {
363 #endif
364 conn->secure = 0;
365 conn->ssl_state = NULL;
366 conn->status = READ_HEADER;
367 #ifdef ENABLE_SSL
368 }
369 #endif
370
371 conn->header_line = conn->client_stream;
372 conn->kacount = req->kacount - 1;
373
374 /* close enough and we avoid a call to time(NULL) */
375 conn->time_last = req->time_last;
376
377 /* for log file and possible use by CGI programs */
378 memcpy(conn->remote_ip_addr, req->remote_ip_addr, NI_MAXHOST);
379 memcpy(conn->local_ip_addr, req->local_ip_addr, NI_MAXHOST);
380
381 /* for possible use by CGI programs */
382 conn->remote_port = req->remote_port;
383
384 params->status.requests++;
385
386 /* we haven't parsed beyond req->parse_pos, so... */
387 bytes_to_move = req->client_stream_pos - req->parse_pos;
388
389 if (bytes_to_move) {
390 memcpy(conn->client_stream,
391 req->client_stream + req->parse_pos, bytes_to_move);
392 conn->client_stream_pos = bytes_to_move;
393 }
394 enqueue(&params->request_block, conn);
395
396 BOA_FD_SET(conn->fd, &params->block_read_fdset);
397
398 enqueue(&params->request_free, req);
399
400 return;
401 }
402
403 /*
404 While debugging some weird errors, Jon Nelson learned that
405 some versions of Netscape Navigator break the
406 HTTP specification.
407
408 Some research on the issue brought up:
409
410 http://www.apache.org/docs/misc/known_client_problems.html
411
412 As quoted here:
413
414 "
415 Trailing CRLF on POSTs
416
417 This is a legacy issue. The CERN webserver required POST
418 data to have an extra CRLF following it. Thus many
419 clients send an extra CRLF that is not included in the
420 Content-Length of the request. Apache works around this
421 problem by eating any empty lines which appear before a
422 request.
423 "
424
425 Boa will (for now) hack around this stupid bug in Netscape
426 (and Internet Exploder)
427 by reading up to 32k after the connection is all but closed.
428 This should eliminate any remaining spurious crlf sent
429 by the client.
430
431 Building bugs *into* software to be compatable is
432 just plain wrong
433 */
434
435 if (req->method == M_POST) {
436 char buf[32768];
437
438 socket_recv( req, buf, sizeof(buf));
439 }
440
441 #ifdef ENABLE_SSL
442 if ( req->secure) {
443 gnutls_bye(req->ssl_state, GNUTLS_SHUT_WR);
444 gnutls_deinit( req->ssl_state);
445 }
446 #endif
447
448 close(req->fd);
449
450 params->total_connections--;
451
452 enqueue(&params->request_free, req);
453
454 return;
455 }
456
457 /*
458 * Name: process_requests
459 *
460 * Description: Iterates through the ready queue, passing each request
461 * to the appropriate handler for processing. It monitors the
462 * return value from handler functions, all of which return -1
463 * to indicate a block, 0 on completion and 1 to remain on the
464 * ready list for more procesing.
465 */
466
467 void process_requests(server_params* params, socket_type server_s)
468 {
469 int retval = 0;
470 request *current, *trailer;
471
472 if (server_s.pending_requests) {
473 get_request(params, server_s);
474 #ifdef ORIGINAL_BEHAVIOR
475 server_s.pending_requests = 0;
476 #endif
477 }
478
479 current = params->request_ready;
480
481 while (current) {
482 time(&current_time);
483 if (current->buffer_end && /* there is data in the buffer */
484 current->status != DEAD && current->status != DONE) {
485 retval = req_flush(current);
486 /*
487 * retval can be -2=error, -1=blocked, or bytes left
488 */
489 if (retval == -2) { /* error */
490 current->status = DEAD;
491 retval = 0;
492 } else if (retval >= 0) {
493 /* notice the >= which is different from below?
494 Here, we may just be flushing headers.
495 We don't want to return 0 because we are not DONE
496 or DEAD */
497
498 retval = 1;
499 }
500 } else {
501 switch (current->status) {
502 #ifdef ENABLE_SSL
503 case FINISH_HANDSHAKE:
504 retval = finish_handshake( current);
505 break;
506 case SEND_ALERT:
507 retval = send_alert( current);
508 break;
509 #endif
510 case READ_HEADER:
511 case ONE_CR:
512 case ONE_LF:
513 case TWO_CR:
514 retval = read_header(params, current);
515 break;
516 case BODY_READ:
517 retval = read_body(current);
518 break;
519 case BODY_WRITE:
520 retval = write_body(current);
521 break;
522 case WRITE:
523 retval = process_get(params, current);
524 break;
525 case PIPE_READ:
526 retval = read_from_pipe(current);
527 break;
528 case PIPE_WRITE:
529 retval = write_from_pipe(current);
530 break;
531 case DONE:
532 /* a non-status that will terminate the request */
533 retval = req_flush(current);
534 /*
535 * retval can be -2=error, -1=blocked, or bytes left
536 */
537 if (retval == -2) { /* error */
538 current->status = DEAD;
539 retval = 0;
540 } else if (retval > 0) {
541 retval = 1;
542 }
543 break;
544 case DEAD:
545 retval = 0;
546 current->buffer_end = 0;
547 SQUASH_KA(current);
548 break;
549 default:
550 retval = 0;
551 fprintf(stderr, "Unknown status (%d), "
552 "closing!\n", current->status);
553 current->status = DEAD;
554 break;
555 }
556
557 }
558
559 if (params->sigterm_flag)
560 SQUASH_KA(current);
561
562 /* we put this here instead of after the switch so that
563 * if we are on the last request, and get_request is successful,
564 * current->next is valid!
565 */
566 if (server_s.pending_requests)
567 get_request(params, server_s);
568
569 switch (retval) {
570 case -1: /* request blocked */
571 trailer = current;
572 current = current->next;
573 block_request(params, trailer);
574 break;
575 case 0: /* request complete */
576 current->time_last = current_time;
577 trailer = current;
578 current = current->next;
579 free_request(params, &params->request_ready, trailer);
580 break;
581 case 1: /* more to do */
582 current->time_last = current_time;
583 current = current->next;
584 break;
585 default:
586 log_error_time();
587 fprintf(stderr, "Unknown retval in process.c - "
588 "Status: %d, retval: %d\n", current->status, retval);
589 current = current->next;
590 break;
591 }
592 }
593 }
594
595 /*
596 * Name: process_logline
597 *
598 * Description: This is called with the first req->header_line received
599 * by a request, called "logline" because it is logged to a file.
600 * It is parsed to determine request type and method, then passed to
601 * translate_uri for further parsing. Also sets up CGI environment if
602 * needed.
603 */
604
605 int process_logline(request * req)
606 {
607 char *stop, *stop2;
608 static char *SIMPLE_HTTP_VERSION = "HTTP/0.9";
609
610 req->logline = req->client_stream;
611 if (!memcmp(req->logline, "GET ", 4))
612 req->method = M_GET;
613 else if (!memcmp(req->logline, "HEAD ", 5))
614 /* head is just get w/no body */
615 req->method = M_HEAD;
616 else if (!memcmp(req->logline, "POST ", 5))
617 req->method = M_POST;
618 else {
619 log_error_time();
620 fprintf(stderr, "malformed request: \"%s\"\n", req->logline);
621 send_r_not_implemented(req);
622 return 0;
623 }
624
625 req->http_version = SIMPLE_HTTP_VERSION;
626 req->simple = 1;
627
628 /* Guaranteed to find ' ' since we matched a method above */
629 stop = req->logline + 3;
630 if (*stop != ' ')
631 ++stop;
632
633 /* scan to start of non-whitespace */
634 while (*(++stop) == ' ');
635
636 stop2 = stop;
637
638 /* scan to end of non-whitespace */
639 while (*stop2 != '\0' && *stop2 != ' ')
640 ++stop2;
641
642 if (stop2 - stop > MAX_HEADER_LENGTH) {
643 log_error_time();
644 fprintf(stderr, "URI too long %d: \"%s\"\n", MAX_HEADER_LENGTH,
645 req->logline);
646 send_r_bad_request(req);
647 return 0;
648 }
649 memcpy(req->request_uri, stop, stop2 - stop);
650 req->request_uri[stop2 - stop] = '\0';
651
652 if (*stop2 == ' ') {
653 /* if found, we should get an HTTP/x.x */
654 unsigned int p1, p2;
655
656 /* scan to end of whitespace */
657 ++stop2;
658 while (*stop2 == ' ' && *stop2 != '\0')
659 ++stop2;
660
661 /* scan in HTTP/major.minor */
662 if (sscanf(stop2, "HTTP/%u.%u", &p1, &p2) == 2) {
663 /* HTTP/{0.9,1.0,1.1} */
664 if (p1 == 1 && (p2 == 0 || p2 == 1)) {
665 req->http_version = stop2;
666 req->simple = 0;
667 } else if (p1 > 1 || (p1 != 0 && p2 > 1)) {
668 goto BAD_VERSION;
669 }
670 } else {
671 goto BAD_VERSION;
672 }
673 }
674
675 if (req->method == M_HEAD && req->simple) {
676 send_r_bad_request(req);
677 return 0;
678 }
679 req->cgi_env_index = COMMON_CGI_COUNT;
680
681 return 1;
682
683 BAD_VERSION:
684 log_error_time();
685 fprintf(stderr, "bogus HTTP version: \"%s\"\n", stop2);
686 send_r_bad_request(req);
687 return 0;
688 }
689
690 /*
691 * Name: process_header_end
692 *
693 * Description: takes a request and performs some final checking before
694 * init_cgi or init_get
695 * Returns 0 for error or NPH, or 1 for success
696 */
697
698 int process_header_end(server_params* params, request * req)
699 {
700 if (!req->logline) {
701 send_r_error(req);
702 return 0;
703 }
704
705 /* Percent-decode request */
706 if (unescape_uri(req->request_uri, &(req->query_string)) == 0) {
707 log_error_doc(req);
708 fputs("Problem unescaping uri\n", stderr);
709 send_r_bad_request(req);
710 return 0;
711 }
712
713 /* clean pathname */
714 clean_pathname(req->request_uri);
715
716 if (req->request_uri[0] != '/') {
717 send_r_bad_request(req);
718 return 0;
719 }
720
721 if (translate_uri(req) == 0) { /* unescape, parse uri */
722 SQUASH_KA(req);
723 return 0; /* failure, close down */
724 }
725
726 if (req->method == M_POST) {
727 req->post_data_fd = create_temporary_file(1, NULL, 0);
728 if (req->post_data_fd == 0)
729 return(0);
730 return(1); /* success */
731 }
732
733 if (req->is_cgi) {
734 return init_cgi(req);
735 }
736
737 req->status = WRITE;
738 return init_get(params, req); /* get and head */
739 }
740
741 /*
742 * Name: process_option_line
743 *
744 * Description: Parses the contents of req->header_line and takes
745 * appropriate action.
746 */
747
748 int process_option_line(request * req)
749 {
750 char c, *value, *line = req->header_line;
751
752 /* Start by aggressively hacking the in-place copy of the header line */
753
754 #ifdef FASCIST_LOGGING
755 log_error_time();
756 fprintf(stderr, "%s:%d - Parsing \"%s\"\n", __FILE__, __LINE__, line);
757 #endif
758
759 value = strchr(line, ':');
760 if (value == NULL)
761 return 0;
762 *value++ = '\0'; /* overwrite the : */
763 to_upper(line); /* header types are case-insensitive */
764 while ((c = *value) && (c == ' ' || c == '\t'))
765 value++;
766
767 if (!memcmp(line, "IF_MODIFIED_SINCE", 18) && !req->if_modified_since)
768 req->if_modified_since = value;
769
770 else if (!memcmp(line, "CONTENT_TYPE", 13) && !req->content_type)
771 req->content_type = value;
772
773 else if (!memcmp(line, "CONTENT_LENGTH", 15) && !req->content_length)
774 req->content_length = value;
775
776 else if (!memcmp(line, "CONNECTION", 11) &&
777 ka_max && req->keepalive != KA_STOPPED) {
778 req->keepalive = (!strncasecmp(value, "Keep-Alive", 10) ?
779 KA_ACTIVE : KA_STOPPED);
780 }
781 /* #ifdef ACCEPT_ON */
782 else if (!memcmp(line, "ACCEPT", 7))
783 add_accept_header(req, value);
784 /* #endif */
785
786 /* Need agent and referer for logs */
787 else if (!memcmp(line, "REFERER", 8)) {
788 req->header_referer = value;
789 if (!add_cgi_env(req, "REFERER", value, 1))
790 return 0;
791 } else if (!memcmp(line, "USER_AGENT", 11)) {
792 req->header_user_agent = value;
793 if (!add_cgi_env(req, "USER_AGENT", value, 1))
794 return 0;
795 } else {
796 if (!add_cgi_env(req, line, value, 1))
797 return 0;
798 }
799 return 1;
800 }
801
802 /*
803 * Name: add_accept_header
804 * Description: Adds a mime_type to a requests accept char buffer
805 * silently ignore any that don't fit -
806 * shouldn't happen because of relative buffer sizes
807 */
808
809 void add_accept_header(request * req, char *mime_type)
810 {
811 #ifdef ACCEPT_ON
812 int l = strlen(req->accept);
813 int l2 = strlen(mime_type);
814
815 if ((l + l2 + 2) >= MAX_HEADER_LENGTH)
816 return;
817
818 if (req->accept[0] == '\0')
819 strcpy(req->accept, mime_type);
820 else {
821 req->accept[l] = ',';
822 req->accept[l + 1] = ' ';
823 memcpy(req->accept + l + 2, mime_type, l2 + 1);
824 /* the +1 is for the '\0' */
825 /*
826 sprintf(req->accept + l, ", %s", mime_type);
827 */
828 }
829 #endif
830 }
831
832 void free_requests(server_params* params)
833 {
834 request *ptr, *next;
835
836 ptr = params->request_free;
837 while (ptr != NULL) {
838 next = ptr->next;
839 free(ptr);
840 ptr = next;
841 }
842 params->request_free = NULL;
843 }

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26