/*
* Boa, an http server
* Copyright (C) 1995 Paul Phillips <
[email protected]>
* Some changes Copyright (C) 1996,97 Larry Doolittle <
[email protected]>
* Some changes Copyright (C) 1996-2002 Jon Nelson <
[email protected]>
*
* 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;
/*-->changed by dxw 2005-10-23*/
extern int gflag;
extern time_t cgi_last_time;
/*<--*/
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 <
[email protected]>
*/
#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 <
[email protected]> */
#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 <
[email protected]> 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
评论0