Add a polling loop in main to read from more than one socket at once. Add the O_NONBLOCK and
SO_KEEPALIVE flag to all sockets. Note that several loops which used to continue on a return value of 0 (theoretical since 0 would never be returned without O_NONBLOCK) now break on 0 so that they won't continue reading until after poll is called again.
This commit is contained in:
parent
84490052d4
commit
674da8ae07
@ -50,6 +50,9 @@
|
||||
#include <netinet/tcp.h> /* for TCP_NODELAY */
|
||||
#endif
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
|
||||
#define ENABLE_CURLX_PRINTF
|
||||
/* make the curlx header define all printf() functions to use the curlx_*
|
||||
versions instead */
|
||||
@ -113,6 +116,11 @@ struct httprequest {
|
||||
int done_processing;
|
||||
};
|
||||
|
||||
#define MAX_SOCKETS 1024
|
||||
|
||||
static struct pollfd all_sockets[MAX_SOCKETS];
|
||||
static nfds_t num_sockets = 0;
|
||||
|
||||
static int ProcessRequest(struct httprequest *req);
|
||||
static void storerequest(char *reqbuf, size_t totalsize);
|
||||
|
||||
@ -1708,7 +1716,12 @@ static int accept_connection(int sock)
|
||||
{
|
||||
curl_socket_t msgsock = CURL_SOCKET_BAD;
|
||||
int error;
|
||||
int flag;
|
||||
int flag = 1;
|
||||
|
||||
if(MAX_SOCKETS == num_sockets) {
|
||||
logmsg("Too many open sockets!");
|
||||
return CURL_SOCKET_BAD;
|
||||
}
|
||||
|
||||
msgsock = accept(sock, NULL, NULL);
|
||||
|
||||
@ -1729,23 +1742,45 @@ static int accept_connection(int sock)
|
||||
return CURL_SOCKET_BAD;
|
||||
}
|
||||
|
||||
if(0 != fcntl(msgsock, F_SETFL, O_NONBLOCK)) {
|
||||
error = SOCKERRNO;
|
||||
logmsg("fcntl(O_NONBLOCK) failed with error: (%d) %s",
|
||||
error, strerror(error));
|
||||
sclose(msgsock);
|
||||
return CURL_SOCKET_BAD;
|
||||
}
|
||||
|
||||
if(0 != setsockopt(msgsock, SOL_SOCKET, SO_KEEPALIVE,
|
||||
(void *)&flag, sizeof(flag))) {
|
||||
error = SOCKERRNO;
|
||||
logmsg("setsockopt(SO_KEEPALIVE) failed with error: (%d) %s",
|
||||
error, strerror(error));
|
||||
sclose(msgsock);
|
||||
return CURL_SOCKET_BAD;
|
||||
}
|
||||
|
||||
/*
|
||||
** As soon as this server acepts a connection from the test harness it
|
||||
** As soon as this server accepts a connection from the test harness it
|
||||
** must set the server logs advisor read lock to indicate that server
|
||||
** logs should not be read until this lock is removed by this server.
|
||||
*/
|
||||
|
||||
set_advisor_read_lock(SERVERLOGS_LOCK);
|
||||
serverlogslocked = 1;
|
||||
if(!serverlogslocked)
|
||||
set_advisor_read_lock(SERVERLOGS_LOCK);
|
||||
serverlogslocked += 1;
|
||||
|
||||
logmsg("====> Client connect");
|
||||
|
||||
all_sockets[num_sockets].fd = msgsock;
|
||||
all_sockets[num_sockets].events = POLLIN;
|
||||
all_sockets[num_sockets].revents = 0;
|
||||
num_sockets += 1;
|
||||
|
||||
#ifdef TCP_NODELAY
|
||||
/*
|
||||
* Disable the Nagle algorithm to make it easier to send out a large
|
||||
* response in many small segments to torture the clients more.
|
||||
*/
|
||||
flag = 1;
|
||||
if(0 != setsockopt(msgsock, IPPROTO_TCP, TCP_NODELAY,
|
||||
(void *)&flag, sizeof(flag)))
|
||||
logmsg("====> TCP_NODELAY failed");
|
||||
@ -1764,9 +1799,9 @@ static int service_connection(int msgsock, struct httprequest *req,
|
||||
if(got_exit_signal)
|
||||
return -1;
|
||||
|
||||
init_httprequest(req);
|
||||
while(!req->done_processing) {
|
||||
int rc = get_request(msgsock, req);
|
||||
logmsg("get_request %d returned %d", msgsock, rc);
|
||||
if (rc <= 0) {
|
||||
/* Nothing further to read now (possibly because the socket was closed */
|
||||
return rc;
|
||||
@ -1827,7 +1862,6 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
srvr_sockaddr_union_t me;
|
||||
curl_socket_t sock = CURL_SOCKET_BAD;
|
||||
curl_socket_t msgsock = CURL_SOCKET_BAD;
|
||||
int wrotepidfile = 0;
|
||||
int flag;
|
||||
unsigned short port = DEFAULT_PORT;
|
||||
@ -1838,6 +1872,7 @@ int main(int argc, char *argv[])
|
||||
int arg=1;
|
||||
long pid;
|
||||
const char *hostport = "127.0.0.1";
|
||||
nfds_t socket_idx;
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
|
||||
@ -1949,6 +1984,11 @@ int main(int argc, char *argv[])
|
||||
sock = socket(AF_INET6, SOCK_STREAM, 0);
|
||||
#endif
|
||||
|
||||
all_sockets[0].fd = sock;
|
||||
all_sockets[0].events = POLLIN;
|
||||
all_sockets[0].revents = 0;
|
||||
num_sockets = 1;
|
||||
|
||||
if(CURL_SOCKET_BAD == sock) {
|
||||
error = SOCKERRNO;
|
||||
logmsg("Error creating socket: (%d) %s",
|
||||
@ -1964,6 +2004,12 @@ int main(int argc, char *argv[])
|
||||
error, strerror(error));
|
||||
goto sws_cleanup;
|
||||
}
|
||||
if(0 != fcntl(sock, F_SETFL, O_NONBLOCK)) {
|
||||
error = SOCKERRNO;
|
||||
logmsg("fcntl(O_NONBLOCK) failed with error: (%d) %s",
|
||||
error, strerror(error));
|
||||
goto sws_cleanup;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_IPV6
|
||||
if(!use_ipv6) {
|
||||
@ -2011,59 +2057,114 @@ int main(int argc, char *argv[])
|
||||
if(!wrotepidfile)
|
||||
goto sws_cleanup;
|
||||
|
||||
for (;;) {
|
||||
do {
|
||||
msgsock = accept_connection(sock);
|
||||
if (CURL_SOCKET_BAD == msgsock)
|
||||
goto sws_cleanup;
|
||||
} while (msgsock >= 0);
|
||||
/* initialization of httprequest struct is done before get_request(), but
|
||||
the pipelining struct field must be initialized previously to FALSE
|
||||
every time a new connection arrives. */
|
||||
|
||||
/* initialization of httprequest struct is done before get_request(), but
|
||||
the pipelining struct field must be initialized previously to FALSE
|
||||
every time a new connection arrives. */
|
||||
req.pipelining = FALSE;
|
||||
init_httprequest(&req);
|
||||
|
||||
req.pipelining = FALSE;
|
||||
|
||||
do {
|
||||
rc = service_connection(msgsock, &req, sock, hostport);
|
||||
if(got_exit_signal)
|
||||
goto sws_cleanup;
|
||||
|
||||
if (rc < 0) {
|
||||
logmsg("====> Client disconnect %d", req.connmon);
|
||||
|
||||
if(req.connmon) {
|
||||
const char *keepopen="[DISCONNECT]\n";
|
||||
storerequest((char *)keepopen, strlen(keepopen));
|
||||
}
|
||||
|
||||
if(!req.open)
|
||||
/* When instructed to close connection after server-reply we
|
||||
wait a very small amount of time before doing so. If this
|
||||
is not done client might get an ECONNRESET before reading
|
||||
a single byte of server-reply. */
|
||||
wait_ms(50);
|
||||
|
||||
if(msgsock != CURL_SOCKET_BAD) {
|
||||
sclose(msgsock);
|
||||
msgsock = CURL_SOCKET_BAD;
|
||||
}
|
||||
|
||||
if(serverlogslocked) {
|
||||
serverlogslocked = 0;
|
||||
clear_advisor_read_lock(SERVERLOGS_LOCK);
|
||||
}
|
||||
|
||||
if (req.testno == DOCNUMBER_QUIT)
|
||||
goto sws_cleanup;
|
||||
for(;;) {
|
||||
/* Clear out closed sockets */
|
||||
for (socket_idx = num_sockets - 1; socket_idx >= 1; --socket_idx) {
|
||||
if (CURL_SOCKET_BAD == all_sockets[socket_idx].fd) {
|
||||
char* dst = (char *) all_sockets + socket_idx;
|
||||
char* src = (char *) all_sockets + socket_idx + 1;
|
||||
char* end = (char *) all_sockets + num_sockets;
|
||||
memmove(dst, src, end - src);
|
||||
num_sockets -= 1;
|
||||
}
|
||||
} while (rc > = 0);
|
||||
}
|
||||
|
||||
rc = poll(all_sockets, num_sockets, -1);
|
||||
|
||||
if (rc < 0) {
|
||||
error = SOCKERRNO;
|
||||
logmsg("poll() failed with error: (%d) %s",
|
||||
error, strerror(error));
|
||||
goto sws_cleanup;
|
||||
}
|
||||
|
||||
/* Check if the listening socket is ready to accept */
|
||||
if ((all_sockets[0].revents & POLLIN) == POLLIN) {
|
||||
/* Service all queued connections */
|
||||
curl_socket_t msgsock;
|
||||
do {
|
||||
msgsock = accept_connection(sock);
|
||||
logmsg("accept_connection %d returned %d", sock, msgsock);
|
||||
if (CURL_SOCKET_BAD == msgsock)
|
||||
goto sws_cleanup;
|
||||
} while (msgsock > 0);
|
||||
}
|
||||
else if (all_sockets[0].revents != 0) {
|
||||
logmsg("unexpected poll event on listening socket: %d",
|
||||
all_sockets[0].revents);
|
||||
goto sws_cleanup;
|
||||
}
|
||||
|
||||
/* Service all connections that are ready */
|
||||
for (socket_idx = 1; socket_idx < num_sockets; ++socket_idx) {
|
||||
if ((all_sockets[socket_idx].revents & POLLIN) == POLLIN) {
|
||||
if(got_exit_signal)
|
||||
goto sws_cleanup;
|
||||
|
||||
/* Service this connection until it has nothing available */
|
||||
do {
|
||||
rc = service_connection(all_sockets[socket_idx].fd, &req, sock, hostport);
|
||||
logmsg("service_connection %d returned %d", all_sockets[socket_idx].fd, rc);
|
||||
if(got_exit_signal)
|
||||
goto sws_cleanup;
|
||||
|
||||
if (rc < 0) {
|
||||
logmsg("====> Client disconnect %d", req.connmon);
|
||||
|
||||
if(req.connmon) {
|
||||
const char *keepopen="[DISCONNECT]\n";
|
||||
storerequest((char *)keepopen, strlen(keepopen));
|
||||
}
|
||||
|
||||
if(!req.open)
|
||||
/* When instructed to close connection after server-reply we
|
||||
wait a very small amount of time before doing so. If this
|
||||
is not done client might get an ECONNRESET before reading
|
||||
a single byte of server-reply. */
|
||||
wait_ms(50);
|
||||
|
||||
if(all_sockets[socket_idx].fd != CURL_SOCKET_BAD) {
|
||||
sclose(all_sockets[socket_idx].fd);
|
||||
all_sockets[socket_idx].fd = CURL_SOCKET_BAD;
|
||||
}
|
||||
|
||||
serverlogslocked -= 1;
|
||||
if(!serverlogslocked)
|
||||
clear_advisor_read_lock(SERVERLOGS_LOCK);
|
||||
|
||||
if (req.testno == DOCNUMBER_QUIT)
|
||||
goto sws_cleanup;
|
||||
}
|
||||
|
||||
/* Reset the request, unless we're still in the middle of reading */
|
||||
if (rc != 0)
|
||||
init_httprequest(&req);
|
||||
} while (rc > 0);
|
||||
}
|
||||
else if (all_sockets[socket_idx].revents != 0) {
|
||||
logmsg("unexpected poll event on socket %d: %d",
|
||||
socket_idx, all_sockets[socket_idx].revents);
|
||||
goto sws_cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if(got_exit_signal)
|
||||
goto sws_cleanup;
|
||||
}
|
||||
|
||||
sws_cleanup:
|
||||
|
||||
if((msgsock != sock) && (msgsock != CURL_SOCKET_BAD))
|
||||
sclose(msgsock);
|
||||
for (socket_idx = 1; socket_idx < num_sockets; ++socket_idx)
|
||||
if((all_sockets[socket_idx].fd != sock) &&
|
||||
(all_sockets[socket_idx].fd != CURL_SOCKET_BAD))
|
||||
sclose(all_sockets[socket_idx].fd);
|
||||
|
||||
if(sock != CURL_SOCKET_BAD)
|
||||
sclose(sock);
|
||||
|
Loading…
Reference in New Issue
Block a user