📄 request.c
字号:
/* * Boa, an http server * Copyright (C) 1995 Paul Phillips <paulp@go2net.com> * Some changes Copyright (C) 1996,97 Larry Doolittle <ldoolitt@boa.org> * Some changes Copyright (C) 1996-2002 Jon Nelson <jnelson@boa.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 1, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * *//* $Id: request.c,v 1.112.2.3 2002/07/24 03:03:59 jnelson Exp $*/#include "boa.h"#include <stddef.h> /* for offsetof */int total_connections;struct status status;static int sockbufsize = SOCKETBUF_SIZE;/* function prototypes located in this file only */static void free_request(request ** list_head_addr, request * req);/* * Name: new_request * Description: Obtains a request struct off the free list, or if the * free list is empty, allocates memory * * Return value: pointer to initialized request */request *new_request(void){ request *req; if (request_free) { req = request_free; /* first on free list */ dequeue(&request_free, request_free); /* dequeue the head */ } else { req = (request *) malloc(sizeof (request)); if (!req) { log_error_time(); perror("malloc for new request"); return NULL; } } memset(req, 0, offsetof(request, buffer) + 1); return req;}/* * Name: get_request * * Description: Polls the server socket for a request. If one exists, * does some basic initialization and adds it to the ready queue;. */void get_request(int server_s){ int fd; /* socket */ struct SOCKADDR remote_addr; /* address */ struct SOCKADDR salocal; int remote_addrlen = sizeof (struct SOCKADDR); request *conn; /* connection */ size_t len; static int system_bufsize = 0; /* Default size of SNDBUF given by system */ remote_addr.S_FAMILY = 0xdead; fd = accept(server_s, (struct sockaddr *) &remote_addr, &remote_addrlen); if (fd == -1) { if (errno != EAGAIN && errno != EWOULDBLOCK) /* abnormal error */ WARN("accept"); else /* no requests */ pending_requests = 0; return; } if (fd >= FD_SETSIZE) { WARN("Got fd >= FD_SETSIZE."); close(fd); return; }#ifdef DEBUGNONINET /* This shows up due to race conditions in some Linux kernels when the client closes the socket sometime between the select() and accept() syscalls. Code and description by Larry Doolittle <ldoolitt@boa.org> */#define HEX(x) (((x)>9)?(('a'-10)+(x)):('0'+(x))) if (remote_addr.sin_family != AF_INET) { struct sockaddr *bogus = (struct sockaddr *) &remote_addr; char *ap, ablock[44]; int i; close(fd); log_error_time(); for (ap = ablock, i = 0; i < remote_addrlen && i < 14; i++) { *ap++ = ' '; *ap++ = HEX((bogus->sa_data[i] >> 4) & 0x0f); *ap++ = HEX(bogus->sa_data[i] & 0x0f); } *ap = '\0'; fprintf(stderr, "non-INET connection attempt: socket %d, " "sa_family = %hu, sa_data[%d] = %s\n", fd, bogus->sa_family, remote_addrlen, ablock); return; }#endif/* XXX Either delete this, or document why it's needed *//* Pointed out 3-Oct-1999 by Paul Saab <paul@mu.org> */#ifdef REUSE_EACH_CLIENT_CONNECTION_SOCKET if ((setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *) &sock_opt, sizeof (sock_opt))) == -1) { DIE("setsockopt: unable to set SO_REUSEADDR"); }#endif len = sizeof(salocal); if (getsockname(fd, (struct sockaddr *) &salocal, &len) != 0) { WARN("getsockname"); close(fd); return; } conn = new_request(); if (!conn) { close(fd); return; } conn->fd = fd; conn->status = READ_HEADER; conn->header_line = conn->client_stream; conn->time_last = current_time; conn->kacount = ka_max; ascii_sockaddr(&salocal, conn->local_ip_addr, NI_MAXHOST); /* nonblocking socket */ if (set_nonblock_fd(conn->fd) == -1) WARN("fcntl: unable to set new socket to non-block"); /* set close on exec to true */ if (fcntl(conn->fd, F_SETFD, 1) == -1) WARN("fctnl: unable to set close-on-exec for new socket"); /* Increase buffer size if we have to. * Only ask the system the buffer size on the first request, * and assume all subsequent sockets have the same size. */ if (system_bufsize == 0) { len = sizeof (system_bufsize); if (getsockopt (conn->fd, SOL_SOCKET, SO_SNDBUF, &system_bufsize, &len) == 0 && len == sizeof (system_bufsize)) { /* fprintf(stderr, "%sgetsockopt reports SNDBUF %d\n", get_commonlog_time(), system_bufsize); */ ; } else { WARN("getsockopt(SNDBUF)"); system_bufsize = 1; } } if (system_bufsize < sockbufsize) { if (setsockopt (conn->fd, SOL_SOCKET, SO_SNDBUF, (void *) &sockbufsize, sizeof (sockbufsize)) == -1) { WARN("setsockopt: unable to set socket buffer size");#ifdef DIE_ON_ERROR_TUNING_SNDBUF exit(errno);#endif } } /* for log file and possible use by CGI programs */ ascii_sockaddr(&remote_addr, conn->remote_ip_addr, NI_MAXHOST); /* for possible use by CGI programs */ conn->remote_port = net_port(&remote_addr); status.requests++;#ifdef USE_TCPNODELAY /* Thanks to Jef Poskanzer <jef@acme.com> for this tweak */ { int one = 1; if (setsockopt(conn->fd, IPPROTO_TCP, TCP_NODELAY, (void *) &one, sizeof (one)) == -1) { DIE("setsockopt: unable to set TCP_NODELAY"); } }#endif#ifndef NO_RATE_LIMIT if (conn->fd > max_connections) { send_r_service_unavailable(conn); conn->status = DONE; pending_requests = 0; }#endif /* NO_RATE_LIMIT */ total_connections++; enqueue(&request_ready, conn);}/* * Name: free_request * * Description: Deallocates memory for a finished request and closes * down socket. */static void free_request(request ** list_head_addr, request * req){ int i; /* free_request should *never* get called by anything but process_requests */ if (req->buffer_end && req->status != DEAD) { req->status = DONE; return; } /* put request on the free list */ dequeue(list_head_addr, req); /* dequeue from ready or block list */ if (req->logline) /* access log */ log_access(req); if (req->mmap_entry_var) release_mmap(req->mmap_entry_var); else if (req->data_mem) munmap(req->data_mem, req->filesize); if (req->data_fd) close(req->data_fd); if (req->post_data_fd) close(req->post_data_fd); if (req->response_status >= 400) status.errors++; for (i = COMMON_CGI_COUNT; i < req->cgi_env_index; ++i) { if (req->cgi_env[i]) { free(req->cgi_env[i]); } else { log_error_time(); fprintf(stderr, "Warning: CGI Environment contains NULL value" \ "(index %d of %d).\n", i, req->cgi_env_index); } } if (req->pathname) free(req->pathname); if (req->path_info) free(req->path_info); if (req->path_translated) free(req->path_translated); if (req->script_name) free(req->script_name); if ((req->keepalive == KA_ACTIVE) && (req->response_status < 500) && req->kacount > 0) { int bytes_to_move; request *conn = new_request(); if (!conn) { /* errors already reported */ enqueue(&request_free, req); close(req->fd); total_connections--; return; } conn->fd = req->fd; conn->status = READ_HEADER; conn->header_line = conn->client_stream; conn->kacount = req->kacount - 1; /* close enough and we avoid a call to time(NULL) */ conn->time_last = req->time_last; /* for log file and possible use by CGI programs */ memcpy(conn->remote_ip_addr, req->remote_ip_addr, NI_MAXHOST); memcpy(conn->local_ip_addr, req->local_ip_addr, NI_MAXHOST); /* for possible use by CGI programs */ conn->remote_port = req->remote_port; status.requests++; /* we haven't parsed beyond req->parse_pos, so... */ bytes_to_move = req->client_stream_pos - req->parse_pos; if (bytes_to_move) { memcpy(conn->client_stream, req->client_stream + req->parse_pos, bytes_to_move); conn->client_stream_pos = bytes_to_move; } enqueue(&request_block, conn); BOA_FD_SET(conn->fd, &block_read_fdset); enqueue(&request_free, req); return; } /* While debugging some weird errors, Jon Nelson learned that some versions of Netscape Navigator break the HTTP specification. Some research on the issue brought up: http://www.apache.org/docs/misc/known_client_problems.html As quoted here: " Trailing CRLF on POSTs This is a legacy issue. The CERN webserver required POST data to have an extra CRLF following it. Thus many clients send an extra CRLF that is not included in the Content-Length of the request. Apache works around this problem by eating any empty lines which appear before a request. " Boa will (for now) hack around this stupid bug in Netscape (and Internet Exploder) by reading up to 32k after the connection is all but closed. This should eliminate any remaining spurious crlf sent by the client. Building bugs *into* software to be compatable is just plain wrong */ if (req->method == M_POST) { char buf[32768]; read(req->fd, buf, 32768); } close(req->fd); total_connections--; enqueue(&request_free, req); return;}/* * Name: process_requests * * Description: Iterates through the ready queue, passing each request * to the appropriate handler for processing. It monitors the
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -