Revision 1.12 |
Revision 1.18 |
Line 4 |
Line 4 |
#include <sys/unistd.h> #include <sys/types.h> #include <sys/stat.h> 
|
#include <sys/unistd.h> #include <sys/types.h> #include <sys/stat.h> 
|
|
#include <sys/uio.h>
|
#include <fcntl.h> #include <errno.h> #include <string.h>
|
#include <fcntl.h> #include <errno.h> #include <string.h>
|
Line 27 |
Line 28 |
char r100[HEADS]; char r101[HEADS];
|
char r100[HEADS]; char r101[HEADS];
|
char r200[HEADS];
|
|
char r201[HEADS]; char r202[HEADS]; char r203[HEADS];
|
char r201[HEADS]; char r202[HEADS]; char r203[HEADS];
|
Line 70 |
Line 70 |
char r504[HEADS]; char r505[HEADS];
|
char r504[HEADS]; char r505[HEADS];
|
|
int r100_len, r101_len, r201_len, r202_len, r203_len, r204_len, r205_len;
int r206_len, r300_len, r301_len, r302_len, r303_len, r304_len, r305_len;
int r306_len, r307_len, r400_len, r401_len, r402_len, r403_len, r404_len;
int r405_len, r406_len, r407_len, r408_len, r409_len, r410_len, r411_len;
int r412_len, r413_len, r414_len, r415_len, r416_len, r417_len, r500_len;
int r501_len, r502_len, r503_len, r504_len, r505_len;
|
/* initialize static reply contents */ /* for now, we use pth version */
|
/* initialize static reply contents */ /* for now, we use pth version */
|
#define COW_VERSION PTH_VERSION
|
#define SERVER_STRING "cow"
|
#define HTTPV "1.1"
|
#define HTTPV "1.1"
|
int response_init()
{
#define GEN_HEAD(BUF,STARTLINE,BODY)\
sprintf(BUF,\
"HTTP/" HTTPV " " STARTLINE "\r\n"\
"Server: cow/%x\r\n"\
"Connection: close\r\n"\
"Content-type: text/plain\r\n"\
"Content-Length: %d\r\n"\
"\r\n"\
BODY, COW_VERSION, strlen(BODY));
GEN_HEAD(r200, "200 OK", "") // don't use this
GEN_HEAD(r200, "204 No Content", "204 : Requested file has no content")
GEN_HEAD(r400, "400 Bad Request", "400 : Bad Request")
GEN_HEAD(r401, "401 Unauthorized", "401 : Authorization failure")
GEN_HEAD(r403, "403 Forbidden", "403 : Forbidden")
GEN_HEAD(r404, "404 Not Found", "404 : Not Found")
GEN_HEAD(r414, "414 Request-URI Too Long",
"414 : Requested URI is too long")
GEN_HEAD(r500, "500 Internal Server Error",
"500 : Internal Server Error")
GEN_HEAD(r501, "501 Not Implemented",
"501 : Requested service is not implemented")
GEN_HEAD(r503, "503 Service Unavailable",
"503 : Requested service is not available") return 0;
}
inline int response_static(int fd, char *str) {
COW_SEND(fd, str) return 0;
}
inline int response_204(int fd) {
return response_static(fd, r204);
}
inline int response_400(int fd) {
return response_static(fd, r400);
}
inline int response_404(int fd) {
return response_static(fd, r404);
}
inline int response_414(int fd) {
return response_static(fd, r414);
}
inline int response_500(int fd) {
return response_static(fd, r500);
}
inline int response_501(int fd) {
|
/*
* Some things never change...keep these in a static memory buffer. Note that
* the final header in this string should not include the \r\n line terminator.
*/
static char constant_headers[] = "Server: " SERVER_STRING "/" VERSION ", Pth " PTH_VERSION_STR "\r\n";
static int constant_headers_len;
/* Variables used by setsockopt */
static int one = 1;
static int zero = 0;
#define GEN_HEAD(BUF,STARTLINE,BODY) { \
sprintf(BUF, \
"HTTP/" HTTPV " " STARTLINE "\r\n" \
"Connection: close\r\n" \
"Content-type: text/plain\r\n" \
"Content-Length: %d\r\n" \
"%s" \
"\r\n" \
BODY, strlen(BODY), constant_headers); \
BUF##_len = strlen(BUF); \
}
/****
* response_init()
*
* Generates the standard error pages that we may want to send back to the
* user. The entire response (including headers) is contained in a single
* buffer and can be written with just one write() system call.
****/
int response_init() {
constant_headers_len = strlen(constant_headers);
GEN_HEAD(r204, "204 No Content", "204 : Requested file has no content")
GEN_HEAD(r400, "400 Bad Request", "400 : Bad Request")
GEN_HEAD(r401, "401 Unauthorized", "401 : Authorization failure")
GEN_HEAD(r403, "403 Forbidden", "403 : Forbidden")
GEN_HEAD(r404, "404 Not Found", "404 : Not Found")
GEN_HEAD(r414, "414 Request-URI Too Long",
"414 : Requested URI is too long")
GEN_HEAD(r500, "500 Internal Server Error",
"500 : Internal Server Error")
GEN_HEAD(r501, "501 Not Implemented",
"501 : Requested service is not implemented")
GEN_HEAD(r503, "503 Service Unavailable",
"503 : Requested service is not available") return 0;
}
|
return response_static(fd, r501);
}
inline int response_503(int fd) {
return response_static(fd, r503);
}
inline int response_default(int fd) {
return response_static(fd, r503);
}
|
|
/* put => just put the entity body at specified uri (server takes care it all).
|
/* put => just put the entity body at specified uri (server takes care it all).
|
Line 142 |
Line 141 |
return (int)req; }
|
return (int)req; }
|
|
#ifdef ENABLE_PUT
|
int response_put(msg * req) { char *pathname = req->p_pathname;
|
int response_put(msg * req) { char *pathname = req->p_pathname;
|
Line 156 |
Line 156 |
if ((MAX_PATH_LENGTH - strlen(server_root) - 1) < (unsigned)(req->p_uri_len)) { /* 414 "Request-URI Too Large" */
|
if ((MAX_PATH_LENGTH - strlen(server_root) - 1) < (unsigned)(req->p_uri_len)) { /* 414 "Request-URI Too Large" */
|
response_414(req->fdp);
|
STATIC_RESPONSE(req->fdp, 414);
|
return -1; } pathname[0] = '\0';
|
return -1; } pathname[0] = '\0';
|
Line 170 |
Line 170 |
#ifdef LOGGING fprintf(ELOG, "PUT: Failed to open %s\n", pathname); #endif
|
#ifdef LOGGING fprintf(ELOG, "PUT: Failed to open %s\n", pathname); #endif
|
response_404(req->fdp);
|
STATIC_RESPONSE(req->fdp, 404);
|
return -1; } else { ++cow_noof;
|
return -1; } else { ++cow_noof;
|
Line 207 |
Line 207 |
int ReaD; int siz = sizeof(BUF); pth_event_t ev = NULL;
|
int ReaD; int siz = sizeof(BUF); pth_event_t ev = NULL;
|
ev = pth_event(PTH_EVENT_TIME, pth_timeout(xS, xU));
|
|
while (1) { bzero(BUF, siz); ReaD = -1;
|
while (1) { bzero(BUF, siz); ReaD = -1;
|
|
ev = pth_event(PTH_EVENT_TIME, pth_timeout(xS, xU));
|
ReaD = pth_read_ev(req->fdp, BUF, (size_t) siz, ev);
|
ReaD = pth_read_ev(req->fdp, BUF, (size_t) siz, ev);
|
|
pth_event_free(ev, PTH_FREE_THIS);
|
if (ReaD <= 0) {
|
if (ReaD <= 0) {
|
printf("\n !!! fuck end !!!\n");
|
printf("Error!\n");
|
break; } if (0 > pth_write(req->fdd, BUF, ReaD)
|
break; } if (0 > pth_write(req->fdd, BUF, ReaD)
|
Line 231 |
Line 232 |
} pth_yield(NULL); */
|
} pth_yield(NULL); */
|
ev = pth_event(PTH_EVENT_TIME, pth_timeout(xS, xU));
|
|
} // end of while
|
} // end of while
|
pth_event_free(ev, PTH_FREE_THIS);
|
|
COW_CLOSE(req->fdd) return 0; }
|
COW_CLOSE(req->fdd) return 0; }
|
Line 274 |
Line 273 |
} return 0; }
|
} return 0; }
|
int response_get(msg * req) {
|
#endif
|
|
int response_get(msg* req) {
|
char *pathname = req->p_pathname; struct stat ifstat;
|
char *pathname = req->p_pathname; struct stat ifstat;
|
size_t header_len;
|
int rv = 0;
|
bzero(req->r_buf, sizeof(req->r_buf));
if ((MAX_PATH_LENGTH - strlen(server_root) - 1) <
(unsigned)(req->p_uri_len)) {
/* 414 "Request-URI Too Large" */
//write(1,&" (414@response_get) ", 20);
response_414(req->fdp);
return -1;
}
pathname[0] = '\0';
strcat(pathname, server_root);
strcat(pathname, req->p_uri);
stat(pathname, &ifstat);
/* 1 tmp_cgi */
if (0 == strncmp(req->p_uri, "/cgi-bin", 8) ||
0 == strncmp(req->p_uri, "cgi-bin", 7)) {
req->is_cgi = 1; // revise to CGI or NPH
}
/* 1 end of tmp_cgi */
|
|
#ifdef HAVE_LIBDMALLOC
|
#ifdef HAVE_LIBDMALLOC
|
|
/*
* Signals don't seem to work properly when linked with dmalloc; we need
* some other way to shutdown. Since dmalloc is only going to be linked
* during debugging, this should be fine.
*/
|
if (0 == strcmp(req->p_uri, "/SHUTDOWN")) { fprintf(stderr, "SHUTTING DOWN\n"); exit(0); } #endif
|
if (0 == strcmp(req->p_uri, "/SHUTDOWN")) { fprintf(stderr, "SHUTTING DOWN\n"); exit(0); } #endif
|
if ('/' == pathname[strlen(pathname) - 1])
strcat(pathname, "index.html");
else if (S_ISDIR(ifstat.st_mode)) {
strcat(pathname, "/index.html");
}
/* 2 tmp_cgi */
/* init_cgi */
if (req->is_cgi) {
printf("init_cgi...\n");
if (-1 == (req->fdd = open(pathname, O_RDONLY | S_IROTH | S_IXOTH))) {
perror("open");
return -1;
}
++cow_noof;
return tmp_cgi(req); // see cgi.c
}
/* 2 end of tmp_cgi */
/* init_get */
req->fdd = open(pathname, O_RDONLY);
if (-1 == req->fdd) {
#ifdef LOGGING
fprintf(ELOG, "GET: 404 %s (%s)\n", pathname, strerror(errno));
|
/*
* Make sure the URI doesn't force us to build a path that's too long to
* fit in our buffers.
*/
if (server_root_len + req->p_uri_len + 1 > MAX_PATH_LENGTH) {
/* Request URI is too long; respond with 414 */
STATIC_RESPONSE(req->fdp, 414);
return -1;
}
/* Make sure the URI starts with a slash */
if (req->p_uri[0] != '/') {
STATIC_RESPONSE(req->fdp, 404);
return -1;
}
strcpy(pathname, server_root);
strcat(pathname, req->p_uri);
/* Does the URI refer to a directory? If so, search for an index file */
stat(pathname, &ifstat);
if (S_ISDIR(ifstat.st_mode)) {
/* Add a trailing slash if we don't already have one */
if (pathname[req->p_uri_len + server_root_len - 1] != '/')
pathname[req->p_uri_len + server_root_len] = '/';
|
|
req->fdd = find_index_file(pathname);
/* If no index found, just return a 404. TODO: Generate index */
if (req->fdd == -1) {
STATIC_RESPONSE(req->fdp, 404);
return -1;
}
/*
* We need to stat the actual file that was opened so that we know
* its size later.
*/
fstat(req->fdd, &ifstat);
}
/* Does the URI refer to a CGI script (if enabled)? If so, flag it. */
else if (0 == strncmp(req->p_uri, "/cgi-bin", 8) ||
0 == strncmp(req->p_uri, "cgi-bin", 7)) {
req->is_cgi = 1; // revise to CGI or NPH
return tmp_cgi(req); // see cgi.c
}
/* Regular file; just open it for reading */
else {
req->fdd = open(pathname, O_RDONLY);
if (req->fdd == -1) {
if (errno == EACCES) {
STATIC_RESPONSE(req->fdp, 403);
LOG1(ELOG, "GET: 403 %s", pathname);
} else {
STATIC_RESPONSE(req->fdp, 404);
LOG1(ELOG, "GET: 404 %s", pathname);
}
return -1;
}
++cow_noof;
}
/* Is the response file empty? */
if (0 == (req->r_filesize = ifstat.st_size)) {
STATIC_RESPONSE(req->fdp, 204);
}
req->r_mimes = get_mime_type(pathname);
// fix here s.t. proper header (ie. keepalive etc) would be generated.
// request is GET, and uri found, so compose a header for 200
send_header_200(req);
if (M_HEAD == req->p_method)
return 0;
#ifdef USE_SENDFILE
/*
* Using sendfile() should give us the best performance since it can use
* the "zero-copy" kernel feature to avoid any buffer copies. Although
* many OS's have sendfile(), they almost all have different options and
* semantics, so this is really Linux-specific. Also note that there is
* no non-blocking version of this call so there is no concurrency
* possible here. Depending on the size of the files, this may or may
* not be an issue.
*/
{
int offset = 0;
int rv;
/*
* TCP_CORK prevents the transmission of partial packets. I.e. it
* forces us to wait and combine the response body with the response
* headers. This is linux-specific, but sendfile() really is too,
* so we're locked in anyway.
*/
//setsockopt(req->fdp, SOL_TCP, TCP_CORK, &one, sizeof(one));
if (pth_write(req->fdp, req->r_buf, header_len) == -1) {
perror("header write()");
exit(1);
}
if ((rv = sendfile(req->fdp, req->fdd, &offset, req->r_filesize)) == -1) {
perror("sendfile");
exit(1);
}
assert(rv == req->r_filesize);
//setsockopt(req->fdp, SOL_TCP, TCP_CORK, &zero, sizeof(zero));
COW_CLOSE(req->fdd);
return 0;
}
|
#endif
|
#endif
|
//printf("cow_noof is %d\n", cow_noof);
|
|
//write(1,&" (RG 404@response_get) ", 22);
response_404(req->fdp);
return -1;
}
++cow_noof;
stat(pathname, &ifstat);
req->r_mimes = get_mime_type(pathname);
if (0 == (req->r_filesize = ifstat.st_size)) {
return response_204(req->fdp);
}
// fix here s.t. proper header (ie. keepalive etc) would be generated.
// request is GET, and uri found, so compose a header for 200
req->r_buf[0] = '\0';
compose_header_200(req); // add keepalive to this func.
header_len = strlen(req->r_buf);
if (M_HEAD == req->p_method) {
pth_send(req->fdp, req->r_buf, header_len, 0);
COW_CLOSE(req->fdd);
return 0;
}
|
|
/* memory map the file and then send it to the socket */ {
|
/* memory map the file and then send it to the socket */ {
|
ssize_t sent;
ssize_t left, length;
ssize_t offset = 0;
//#define MAP_OPTIONS MAP_FILE|MAP_PRIVATE /* linux */
#define MAP_OPTIONS MAP_FILE|MAP_SHARED
char *m;
length = left = req->r_filesize;
m = mmap(0, left, PROT_READ, MAP_OPTIONS, req->fdd, 0);
|
ssize_t sent;
ssize_t length;
ssize_t offset = 0;
char *m;
length = req->r_filesize;
m = mmap(0, length, PROT_READ, MAP_FILE|MAP_SHARED, req->fdd, 0);
if (NULL == m) {
STATIC_RESPONSE(req->fdp, 500);
|
if (NULL == m) {
//write(1,&" (500@response_get) ", 20);
response_500(req->fdp);
COW_CLOSE(req->fdd);
return -1;
}
// send header
COW_SEND_R(req->fdp, req->r_buf, sent);
if (0 > sent) {
|
|
COW_CLOSE(req->fdd); return -1; } /* Send the body of the requested page */
|
COW_CLOSE(req->fdd); return -1; } /* Send the body of the requested page */
|
while (left > 0) {
errno = 0;
sent = pth_send(req->fdp, m + offset, left, 0);
if (sent < left && errno && errno != EINTR) {
|
sent = cow_write(req->fdp, m, req->r_filesize);
if (sent < req->r_filesize) {
/* Some kind of error */
rv = -1;
|
/* Some kind of error */
|
|
#ifdef LOGGING
|
#ifdef LOGGING
|
fprintf(ELOG, "Failed to write part of %s to socket; "
"already sent %d bytes, remaining were "
"not sent.\n",
req->p_uri,
offset,
left);
#endif
if (munmap(m, length))
|
fprintf(ELOG, "Failed to write part of %s to socket; "
"already sent %d bytes, remaining were "
"not sent.\n",
req->p_uri,
sent,
left-sent);
#endif /* LOGGING */
}
|
perror("munmap");
COW_CLOSE(req->fdd);
return -1;
}
/* Write successful */
left -= sent;
offset += sent;
}
|
|
/* Unmap the file from memory */ if (munmap(m, length))
|
/* Unmap the file from memory */ if (munmap(m, length))
|
perror("munmap");
|
perror("munmap");
|
COW_CLOSE(req->fdd); }
|
COW_CLOSE(req->fdd); }
|
return 0;
}
void compose_header_200(msg * req)
|
return rv;
}
|
|
void send_header_200(msg * req)
|
{
|
{
|
strcat(req->r_buf, "HTTP/1.0 200 OK\r\n");
add_http_headers(req);
|
cow_write(req->fdp, "HTTP/1.0 200 OK\r\n", 17);
send_http_headers(req);
|
} // inline
|
} // inline
|
void add_http_headers(msg * req)
|
void send_http_headers(msg * req)
|
{ char rfc822_time_buf[32]; // ={'\0'}; char *s;
|
{ char rfc822_time_buf[32]; // ={'\0'}; char *s;
|
Line 439 |
Line 464 |
time(&mytime); mytm = (struct tm *)gmtime(&mytime);
|
time(&mytime); mytm = (struct tm *)gmtime(&mytime);
|
// strftime(s, 128, "%a, %d %b %Y %H:%M:%S %Z", mytm);
// strftime(s, 128, "%a, %d %b %Y %H:%M:%S %z", mytm);
strftime(s, 128, "%d %b %Y %H:%M %Z", mytm); // shortest possible
// printf( "%s\n", s);
strcat(req->r_buf, "Date: ");
strcat(req->r_buf, s);
strcat(req->r_buf, "\r\nServer: Cow/0.01\r\n");
// print_ka_pharse(req); // not yet
add_keepalive_lines(req);
|
strftime(s, 128, "%d %b %Y %H:%M %Z\r\n", mytm); // shortest possible
cow_write(req->fdp, "Date: ", 6);
cow_write(req->fdp, s, strlen(s));
cow_write(req->fdp, constant_headers, constant_headers_len);
send_keepalive_lines(req);
send_content_type(req);
send_content_length(req);
//send_last_modified(req);
cow_write(req->fdp, "\r\n", 2);
|
add_content_type(req);
add_content_length(req);
//add_last_modified(req);
// end of header.
strcat(req->r_buf, "\r\n");
|
|
} // inline
|
} // inline
|
void add_content_type(msg * req)
|
void send_content_type(msg * req)
|
{
|
{
|
strcat(req->r_buf, "Content-Type: ");
strcat(req->r_buf, req->r_mimes);
strcat(req->r_buf, "\r\n");
|
cow_write(req->fdp, "Content-type: ", 14);
cow_write(req->fdp, req->r_mimes, strlen(req->r_mimes));
cow_write(req->fdp, "\r\n", 2);
|
} // inline
|
} // inline
|
void add_content_length(msg * req) {
req = 0; /* get rid of compiler warning */
/*
char buf[32]={'\0'};
strcat(req->r_buf, "Content-Length: ");
//sprintf(buf, "%lu\r\n\0", (long unsigned)(req->r_filesize));
sprintf(buf, "%d\r\n\0", (int)(req->r_filesize));
|
void send_content_length(msg * req) {
static char buf[30] = {'\0'};
int buflen;
buflen = sprintf(buf, "%d\r\n", req->r_filesize);
cow_write(req->fdp, "Content-Length: ", 16);
cow_write(req->fdp, buf, buflen);
|
strcat(req->r_buf, buf);
*/
|
|
} // inline
|
} // inline
|
void add_keepalive_lines(msg * req)
|
void send_keepalive_lines(msg * req)
|
{
|
{
|
/*
if (req->ka_count >0
&& req->ka_status == KA_ACTIVE
)
//&& req->response_status < 500)
{
char buf_ka_timeout[32];
char buf_ka_count[32];
sprintf(buf_ka_timeout, "timeout=%lu, ", ka_max_dur);
sprintf(buf_ka_count, "max=%d\r\n", req->ka_count);
strcat(req->r_buf, "Connection: Keep-Alive\r\nKeep-Alive: timeout=15\r\n");
}
else
*/
|
#ifndef DISABLE_KEEPALIVE
if (req->ka_count >0 && req->ka_status == KA_ACTIVE) {
char buf_ka_timeout[32];
char buf_ka_count[32];
int bkt_len, bkc_len;
bkt_len = sprintf(buf_ka_timeout, "timeout=%lu, ", ka_max_dur);
bkc_len = sprintf(buf_ka_count, "max=%d\r\n", req->ka_count);
cow_write(req->fdp, "Connection: Keep-Alive\r\nKeep-Alive: ", 36);
cow_write(req->fdp, buf_ka_timeout, bkt_len);
cow_write(req->fdp, buf_ka_count, bkc_len);
} else {
req->ka_status = KA_INACTIVE;
cow_write(req->fdp, "Connection: close\r\n", 19);
}
#else
|
req->ka_status = KA_INACTIVE;
|
req->ka_status = KA_INACTIVE;
|
strcat(req->r_buf, "Connection: close\r\n");
}
|
cow_write(req->fdp, "Connection: close\r\n", 19);
#endif
}
|
|
/****
* find_index_file()
*
* Given a directory, searches for a suitable index file based on server
* settings and returns a file descriptor to it.
*
* This function reads the list of suitable index filenames from the
* index_files linked list.
*
* Important Note: this function modifies filename so that it includes
* the name of the actual index file selected.
*
* @param pathname The pathname of the directory to scan for an index file
*
* @return A file descriptor to a suitable index file, or -1 if no suitable
* index file was found.
****/
int find_index_file(char* pathname) {
idxfilename* i;
int fd;
int pathlen;
pathlen = strlen(pathname);
for (i = index_files; i != NULL; i = i->next) {
strcpy(&pathname[pathlen], i->name);
if (fd = open(pathname, O_RDONLY) != -1) {
cow_noof++;
return fd;
}
}
/* No suitable index files */
pathname[pathlen] = '\0';
return -1;
}
#ifdef USE_TCPCORK
#define SOCKET_FLUSH(S) setsockopt(S, SOL_TCP, TCP_CORK, &zero, sizeof(zero))
#define SOCKET_HALFCLOSE(S) shutdown(S, SHUT_WR)
#define SOCKET_FULLCLOSE(S) close(S)
#define SOCKET_READ pth_read_ev
#else /* not USE_TCPCORK */
#define SOCKET_FLUSH(S) ap_bflush(S)
#define SOCKET_HALFCLOSE(S) shutdown(S->fd, SHUT_WR)
#define SOCKET_FULLCLOSE(S) ap_bclose(S)
#define SOCKET_READ ap_bread
#endif
/****
* cow_close_socket()
*
* Cleanly closes a socket connection. If there's unsent data in the buffer,
* that will be sent first. We also implement the 'lingering close' required
* by HTTP/1.1.
*
* @param sock The socket to close
****/
void cow_close_socket(SOCKTYPE sock) {
int rv;
char junkbuf[2048];
SOCKET_FLUSH(sock);
#ifndef USE_TCPCORK
/* Set 'end of output' flag on buffer so we don't reflush later */
ap_bsetflag(sock, B_EOUT, 1);
#endif
if (SOCKET_HALFCLOSE(sock) == -1) {
/* Closing half the socket failed; give up and do a regular close */
SOCKET_FULLCLOSE(sock);
return;
}
do {
/*
* Read anything coming in over the network and discard it. Keep
* doing this until the client closes the connection or until we've
* been idle for two seconds.
*
* This 'lingering close' is important so that if we decide to close
* a persistent connection, the client won't send something which makes
* us generate a RST --- our RST could get back to the client before
* earlier, valid data and cause the client to close its connection
* before the valid data showed up. See section 8 of the
* draft-ietf-http-connection-00.txt document (available many places
* on the web) for more details.
*/
pth_event_t timeout = pth_event(PTH_EVENT_TIME, pth_timeout(2, 0));
rv = SOCKET_READ(sock, junkbuf, sizeof(junkbuf), timeout);
pth_event_free(timeout, PTH_FREE_THIS);
} while (rv > 0);
/*
* Okay, either the client closed the connection or the connection was
* idle too long. Do a real close now.
*/
SOCKET_FULLCLOSE(sock);
--cow_noof; \
}
|