Revision 1.14 |
Revision 1.18 |
Line 70 |
Line 70 |
char r504[HEADS]; char r505[HEADS];  
|
char r504[HEADS]; char r505[HEADS];  
|
#ifdef RESPONSE_CONCURRENCY
/* Number of threads sending data right now */
int active_responders = 0;
#endif
|
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 SERVER_STRING(VER) "cow" #VER
|
#define SERVER_STRING "cow"
|
#define HTTPV "1.1" /* * 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. */
|
#define HTTPV "1.1" /* * 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(PTH_VERSION);
|
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;
|
/* 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" \
"\r\n" \
BODY, strlen(BODY), constant_headers);
|
#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()
|
/**** * response_init()
|
Line 108 |
Line 113 |
* buffer and can be written with just one write() system call. ****/ int response_init() {
|
* 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(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")
|
Line 134 |
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 199 |
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 223 |
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 266 |
Line 273 |
} return 0; }
|
} return 0; }
|
|
#endif
|
int response_get(msg* req) { char *pathname = req->p_pathname; struct stat ifstat;
|
int response_get(msg* req) { char *pathname = req->p_pathname; struct stat ifstat;
|
size_t header_len;
|
int rv = 0;
|
//bzero(req->r_buf, sizeof(req->r_buf));
|
|
#ifdef HAVE_LIBDMALLOC /*
|
#ifdef HAVE_LIBDMALLOC /*
|
Line 358 |
Line 364 |
// fix here s.t. proper header (ie. keepalive etc) would be generated. // request is GET, and uri found, so compose a header for 200
|
// 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.
|
send_header_200(req);
if (M_HEAD == req->p_method)
|
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;
|
return 0;
|
}
|
|
#ifdef USE_SENDFILE /*
|
#ifdef USE_SENDFILE /*
|
Line 387 |
Line 387 |
* 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,
|
* 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 if we're locked in anyway.
|
* so we're locked in anyway.
|
*/
|
*/
|
setsockopt(req->fdp, SOL_TCP, TCP_CORK, &one, sizeof(one));
if (write(req->fdp, req->r_buf, header_len) == -1) {
|
//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); }
|
perror("header write()"); exit(1); }
|
Line 398 |
Line 398 |
perror("sendfile"); exit(1); }
|
perror("sendfile"); exit(1); }
|
setsockopt(req->fdp, SOL_TCP, TCP_CORK, &zero, sizeof(zero));
|
assert(rv == req->r_filesize);
|
|
//setsockopt(req->fdp, SOL_TCP, TCP_CORK, &zero, sizeof(zero));
|
COW_CLOSE(req->fdd); return 0;
|
COW_CLOSE(req->fdd); return 0;
|
Line 408 |
Line 409 |
/* memory map the file and then send it to the socket */ {
|
/* memory map the file and then send it to the socket */ {
|
#ifdef USE_WRITEV
struct iovec vec[2];
int i;
#endif
|
|
ssize_t sent;
|
ssize_t sent;
|
ssize_t left, length;
|
ssize_t length;
|
ssize_t offset = 0; char *m;
|
ssize_t offset = 0; char *m;
|
length = left = req->r_filesize;
m = mmap(0, left, PROT_READ, MAP_FILE|MAP_SHARED, req->fdd, 0);
|
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); COW_CLOSE(req->fdd); return -1; }
|
if (NULL == m) { STATIC_RESPONSE(req->fdp, 500); COW_CLOSE(req->fdd); return -1; }
|
#ifdef RESPONSE_CONCURRENCY
/* Don't respond until there are few enough other responding threads */
while (active_responders >= RESPONSE_CONCURRENCY)
pth_yield(NULL);
active_responders++;
#endif
#ifdef USE_WRITEV
vec[0].iov_base = req->r_buf;
vec[0].iov_len = strlen(req->r_buf);
vec[1].iov_base = m;
vec[1].iov_len = req->r_filesize;
if (writev_all(req->fdp, vec, 2) < 0)
|
/* Send the body of the requested page */
sent = cow_write(req->fdp, m, req->r_filesize);
if (sent < req->r_filesize) {
/* Some kind of error */
rv = -1;
#ifdef LOGGING
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 */
|
fprintf(ELOG, "Failed to send part of %s\n", req->p_uri);
#else /* USE_WRITEV -> false */
// 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 */
while (left > 0) {
errno = 0;
sent = pth_send(req->fdp, m + offset, left, 0);
if (sent < left && errno && errno != EINTR) {
/* Some kind of error */
#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 /* LOGGING */
if (munmap(m, length))
perror("munmap");
COW_CLOSE(req->fdd);
return -1;
}
/* Write successful */
left -= sent;
offset += sent;
}
#endif /* USE_WRITEV */
#ifdef RESPONSE_CONCURRENCY
/* Done responding; update # of active responders */
active_responders--;
#endif
|
|
/* 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;
}
/****
* writev_all()
|
return rv;
}
void send_header_200(msg * req)
|
*
* Although building the iovec structures for writev() is simple, figuring
* out what to do when only part of the data was written is a real pain.
* This function will continue writev'ing until everything has been written
* or until an error occurs. This code is almost directly out of Apache's
* source code.
*
* @param fd File descriptor to write to
* @param vec iovec pointing to set of buffers to send
* @param nvec Number of buffers in iovec set
*
* @return 0 on success, -1 if writev fails with a fatal error (not EINTR)
****/
int writev_all(int fd, struct iovec* vec, int nvec) {
int i = 0;
int ret;
i = 0;
while (i < nvec) {
do
ret = pth_writev(fd, &vec[i], nvec - i);
while (ret == -1 && errno == EINTR);
if (ret == -1)
return -1;
/* recalculate vec to deal with partial writes */
while (ret > 0) {
if (ret < vec[i].iov_len) {
vec[i].iov_base = (char *) vec[i].iov_base + ret;
vec[i].iov_len -= ret;
ret = 0;
}
else {
ret -= vec[i].iov_len;
++i;
}
}
}
/* If we get here, we wrote it all */
return 0;
}
void compose_header_200(msg * req)
|
|
{
|
{
|
// TODO (mdr): modify for new writev code...
strcat(req->r_buf, "HTTP/1.0 200 OK\r\n");
|
cow_write(req->fdp, "HTTP/1.0 200 OK\r\n", 17);
send_http_headers(req);
|
add_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 553 |
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
|
}
|
}
|
Line 651 |
Line 555 |
return -1; }
|
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; \
}
|