📄 request.c
字号:
/* * Boa, an http server * Copyright (C) 1995 Paul Phillips <psp@well.com> * Some changes Copyright (C) 1996,97 Larry Doolittle <ldoolitt@jlab.org> * Some changes Copyright (C) 1996-99 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.100 2001/07/18 03:40:10 jnelson Exp $*/#include "boa.h"#include <netinet/tcp.h>#include <arpa/inet.h> /* inet_ntoa */#include <stddef.h>int sockbufsize = SOCKETBUF_SIZE;int total_connections;/* * 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_mesg(__FILE__, __LINE__, "out of memory allocating for new request structure"); exit(errno); } } 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 */ int remote_addrlen = sizeof (struct sockaddr_in); request *conn; /* connection */ int 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) /* no requests */ log_error_mesg(__FILE__, __LINE__, "accept"); return; } if (fd >= FD_SETSIZE) { log_error_mesg(__FILE__, __LINE__, "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) { log_error_mesg(__FILE__, __LINE__, "setsockopt: unable to set SO_REUSEADDR"); exit(errno); }#endif conn = new_request(); conn->fd = fd; conn->status = READ_HEADER; conn->header_line = conn->client_stream; conn->time_last = current_time; conn->kacount = ka_max; /* fill in local_ip_addr if relevant */ if (virtualhost) { struct SOCKADDR salocal; int dummy = sizeof (salocal); if (getsockname(conn->fd, (struct sockaddr *) &salocal, &dummy) == -1) { log_error_mesg(__FILE__, __LINE__, "getsockname"); exit(errno); } ascii_sockaddr(&salocal, conn->local_ip_addr, NI_MAXHOST); } /* nonblocking socket */ if (fcntl(conn->fd, F_SETFL, NOBLOCK) == -1) log_error_mesg(__FILE__, __LINE__, "fcntl: unable to set new socket to non-block"); /* set close on exec to true */ if (fcntl(conn->fd, F_SETFD, 1) == -1) log_error_mesg(__FILE__, __LINE__, "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 { log_error_mesg(__FILE__, __LINE__, "getsockopt(SNDBUF)"); system_bufsize = 1; } } if (system_bufsize < sockbufsize) { if (setsockopt (conn->fd, SOL_SOCKET, SO_SNDBUF, (void *) &sockbufsize, sizeof (sockbufsize)) == -1) { log_error_mesg(__FILE__, __LINE__, "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 = ntohs(remote_addr.sin_port); 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) { log_error_mesg(__FILE__, __LINE__, "setsockopt: unable to set TCP_NODELAY"); exit(errno); } }#endif 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){ /* 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 */ FD_CLR(req->fd, &block_read_fdset); FD_CLR(req->fd, &block_write_fdset); FD_CLR(req->data_fd, &block_read_fdset); FD_CLR(req->post_data_fd, &block_write_fdset); 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++; if (req->cgi_env) { int i; for (i = COMMON_CGI_COUNT; i < req->cgi_env_index; ++i) if (req->cgi_env[i]) free(req->cgi_env[i]); free(req->cgi_env); } 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) { request *conn = new_request(); 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++; { int bytes_to_move; /* 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; FD_CLR(conn->fd, &block_read_fdset); enqueue(&request_ready, conn); } else { FD_SET(conn->fd, &block_read_fdset); enqueue(&request_block, conn); } } } else { /* 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 || req->method == M_PUT) { char buf[32768]; read(req->fd, buf, 32768); } close(req->fd); total_connections--; } enqueue(&request_free, req);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -