* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
#define ERANGE 34 /* errno.h value */
#endif
+static enum {
+ socket_domain_inet = AF_INET
#ifdef ENABLE_IPV6
-static bool use_ipv6 = FALSE;
+ , socket_domain_inet6 = AF_INET6
#endif
+#ifdef USE_UNIX_SOCKETS
+ , socket_domain_unix = AF_UNIX
+#endif
+} socket_domain = AF_INET;
static bool use_gopher = FALSE;
-static const char *ipv_inuse = "IPv4";
static int serverlogslocked = 0;
static bool is_proxy = FALSE;
bool pipelining; /* true if request is pipelined */
int callcount; /* times ProcessRequest() gets called */
bool connmon; /* monitor the state of the connection, log disconnects */
+ bool upgrade; /* test case allows upgrade to http2 */
+ bool upgrade_request; /* upgrade request found and allowed */
int done_processing;
};
proper point - like with NTLM */
#define CMD_CONNECTIONMONITOR "connection-monitor"
+/* upgrade to http2 */
+#define CMD_UPGRADE "upgrade"
+
#define END_OF_HEADERS "\r\n\r\n"
enum {
#endif
}
+/* returns true if the current socket is an IP one */
+static bool socket_domain_is_ip(void)
+{
+ switch(socket_domain) {
+ case AF_INET:
+#ifdef ENABLE_IPV6
+ case AF_INET6:
+#endif
+ return true;
+ default:
+ /* case AF_UNIX: */
+ return false;
+ }
+}
+
/* based on the testno, parse the correct server commands */
static int parse_servercmd(struct httprequest *req)
{
logmsg("enabled connection monitoring");
req->connmon = TRUE;
}
+ else if(!strncmp(CMD_UPGRADE, cmd, strlen(CMD_UPGRADE))) {
+ logmsg("enabled upgrade to http2");
+ req->upgrade = TRUE;
+ }
else if(1 == sscanf(cmd, "pipe: %d", &num)) {
logmsg("instructed to allow a pipe size of %d", num);
if(num < 0)
return 1; /* done */
}
+ if(req->upgrade && strstr(req->reqbuf, "Upgrade:")) {
+ /* we allow upgrade and there was one! */
+ logmsg("Found Upgrade: in request and allows it");
+ req->upgrade_request = TRUE;
+ }
+
if(req->cl > 0) {
if(req->cl <= req->offset - (end - req->reqbuf) - strlen(end_of_headers))
return 1; /* done */
srvr_sockaddr_union_t serveraddr;
curl_socket_t serverfd;
int error;
- int rc;
+ int rc = 0;
const char *op_br = "";
const char *cl_br = "";
-#ifdef TCP_NODELAY
- curl_socklen_t flag;
-#endif
#ifdef ENABLE_IPV6
- if(use_ipv6) {
+ if(socket_domain == AF_INET6) {
op_br = "[";
cl_br = "]";
}
logmsg("about to connect to %s%s%s:%hu",
op_br, ipaddr, cl_br, port);
-#ifdef ENABLE_IPV6
- if(!use_ipv6)
-#endif
- serverfd = socket(AF_INET, SOCK_STREAM, 0);
-#ifdef ENABLE_IPV6
- else
- serverfd = socket(AF_INET6, SOCK_STREAM, 0);
-#endif
+
+ serverfd = socket(socket_domain, SOCK_STREAM, 0);
if(CURL_SOCKET_BAD == serverfd) {
error = SOCKERRNO;
logmsg("Error creating socket for server conection: (%d) %s",
}
#ifdef TCP_NODELAY
- /* Disable the Nagle algorithm */
- flag = 1;
- if(0 != setsockopt(serverfd, IPPROTO_TCP, TCP_NODELAY,
- (void *)&flag, sizeof(flag)))
- logmsg("====> TCP_NODELAY for server conection failed");
- else
- logmsg("TCP_NODELAY set for server conection");
+ if(socket_domain_is_ip()) {
+ /* Disable the Nagle algorithm */
+ curl_socklen_t flag = 1;
+ if(0 != setsockopt(serverfd, IPPROTO_TCP, TCP_NODELAY,
+ (void *)&flag, sizeof(flag)))
+ logmsg("====> TCP_NODELAY for server conection failed");
+ else
+ logmsg("TCP_NODELAY set for server conection");
+ }
#endif
-#ifdef ENABLE_IPV6
- if(!use_ipv6) {
-#endif
+ switch(socket_domain) {
+ case AF_INET:
memset(&serveraddr.sa4, 0, sizeof(serveraddr.sa4));
serveraddr.sa4.sin_family = AF_INET;
serveraddr.sa4.sin_port = htons(port);
}
rc = connect(serverfd, &serveraddr.sa, sizeof(serveraddr.sa4));
+ break;
#ifdef ENABLE_IPV6
- }
- else {
+ case AF_INET6:
memset(&serveraddr.sa6, 0, sizeof(serveraddr.sa6));
serveraddr.sa6.sin6_family = AF_INET6;
serveraddr.sa6.sin6_port = htons(port);
}
rc = connect(serverfd, &serveraddr.sa, sizeof(serveraddr.sa6));
- }
+ break;
#endif /* ENABLE_IPV6 */
+#ifdef USE_UNIX_SOCKETS
+ case AF_UNIX:
+ logmsg("Proxying through Unix socket is not (yet?) supported.");
+ return CURL_SOCKET_BAD;
+#endif /* USE_UNIX_SOCKETS */
+ }
if(got_exit_signal) {
sclose(serverfd);
bool poll_server_rd[2] = { TRUE, TRUE };
bool poll_client_wr[2] = { TRUE, TRUE };
bool poll_server_wr[2] = { TRUE, TRUE };
-#ifdef TCP_NODELAY
- curl_socklen_t flag;
-#endif
bool primary = FALSE;
bool secondary = FALSE;
int max_tunnel_idx; /* CTRL or DATA */
memset(&req2, 0, sizeof(req2));
logmsg("====> Client connect DATA");
#ifdef TCP_NODELAY
- /* Disable the Nagle algorithm */
- flag = 1;
- if(0 != setsockopt(datafd, IPPROTO_TCP, TCP_NODELAY,
- (void *)&flag, sizeof(flag)))
- logmsg("====> TCP_NODELAY for client DATA conection failed");
- else
- logmsg("TCP_NODELAY set for client DATA conection");
+ if(socket_domain_is_ip()) {
+ /* Disable the Nagle algorithm */
+ curl_socklen_t flag = 1;
+ if(0 != setsockopt(datafd, IPPROTO_TCP, TCP_NODELAY,
+ (void *)&flag, sizeof(flag)))
+ logmsg("====> TCP_NODELAY for client DATA conection failed");
+ else
+ logmsg("TCP_NODELAY set for client DATA conection");
+ }
#endif
req2.pipelining = FALSE;
init_httprequest(&req2);
*infdp = CURL_SOCKET_BAD;
}
+static void http2(struct httprequest *req)
+{
+ (void)req;
+ logmsg("switched to http2");
+ /* left to implement */
+}
+
+
/* returns a socket handle, or 0 if there are no more waiting sockets,
or < 0 if there was an error */
static curl_socket_t accept_connection(curl_socket_t sock)
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.
- */
- if(0 != setsockopt(msgsock, IPPROTO_TCP, TCP_NODELAY,
- (void *)&flag, sizeof(flag)))
- logmsg("====> TCP_NODELAY failed");
- else
- logmsg("TCP_NODELAY set");
+ if(socket_domain_is_ip()) {
+ /*
+ * Disable the Nagle algorithm to make it easier to send out a large
+ * response in many small segments to torture the clients more.
+ */
+ if(0 != setsockopt(msgsock, IPPROTO_TCP, TCP_NODELAY,
+ (void *)&flag, sizeof(flag)))
+ logmsg("====> TCP_NODELAY failed");
+ else
+ logmsg("TCP_NODELAY set");
+ }
#endif
return msgsock;
}
}
+ if(req->upgrade_request) {
+ /* an upgrade request, switch to http2 here */
+ http2(req);
+ return -1;
+ }
+
/* if we got a CONNECT, loop and get another request as well! */
if(req->open) {
int wrotepidfile = 0;
int flag;
unsigned short port = DEFAULT_PORT;
+#ifdef USE_UNIX_SOCKETS
+ const char *unix_socket = NULL;
+ bool unlink_socket = false;
+#endif
char *pidname= (char *)".http.pid";
struct httprequest req;
- int rc;
+ int rc = 0;
int error;
int arg=1;
long pid;
const char *connecthost = "127.0.0.1";
+ const char *socket_type = "IPv4";
+ char port_str[11];
+ const char *location_str = port_str;
/* a default CONNECT port is basically pointless but still ... */
size_t socket_idx;
while(argc>arg) {
if(!strcmp("--version", argv[arg])) {
- printf("sws IPv4%s"
- "\n"
- ,
+ puts("sws IPv4"
#ifdef ENABLE_IPV6
"/IPv6"
-#else
- ""
#endif
- );
+#ifdef USE_UNIX_SOCKETS
+ "/unix"
+#endif
+ );
return 0;
}
else if(!strcmp("--pidfile", argv[arg])) {
end_of_headers = "\r\n"; /* gopher style is much simpler */
}
else if(!strcmp("--ipv4", argv[arg])) {
-#ifdef ENABLE_IPV6
- ipv_inuse = "IPv4";
- use_ipv6 = FALSE;
-#endif
+ socket_type = "IPv4";
+ socket_domain = AF_INET;
+ location_str = port_str;
arg++;
}
else if(!strcmp("--ipv6", argv[arg])) {
#ifdef ENABLE_IPV6
- ipv_inuse = "IPv6";
- use_ipv6 = TRUE;
+ socket_type = "IPv6";
+ socket_domain = AF_INET6;
+ location_str = port_str;
#endif
arg++;
}
+ else if(!strcmp("--unix-socket", argv[arg])) {
+ arg++;
+ if(argc>arg) {
+#ifdef USE_UNIX_SOCKETS
+ unix_socket = argv[arg];
+ if(strlen(unix_socket) >= sizeof(me.sau.sun_path)) {
+ fprintf(stderr, "sws: socket path must be shorter than %zu chars\n",
+ sizeof(me.sau.sun_path));
+ return 0;
+ }
+ socket_type = "unix";
+ socket_domain = AF_UNIX;
+ location_str = unix_socket;
+#endif
+ arg++;
+ }
+ }
else if(!strcmp("--port", argv[arg])) {
arg++;
if(argc>arg) {
" --pidfile [file]\n"
" --ipv4\n"
" --ipv6\n"
+ " --unix-socket [file]\n"
" --port [port]\n"
" --srcdir [path]\n"
" --connect [ip4-addr]\n"
}
}
+ snprintf(port_str, sizeof(port_str), "port %hu", port);
+
#ifdef WIN32
win32_init();
atexit(win32_cleanup);
pid = (long)getpid();
-#ifdef ENABLE_IPV6
- if(!use_ipv6)
-#endif
- sock = socket(AF_INET, SOCK_STREAM, 0);
-#ifdef ENABLE_IPV6
- else
- sock = socket(AF_INET6, SOCK_STREAM, 0);
-#endif
+ sock = socket(socket_domain, SOCK_STREAM, 0);
all_sockets[0] = sock;
num_sockets = 1;
goto sws_cleanup;
}
-#ifdef ENABLE_IPV6
- if(!use_ipv6) {
-#endif
+ switch(socket_domain) {
+ case AF_INET:
memset(&me.sa4, 0, sizeof(me.sa4));
me.sa4.sin_family = AF_INET;
me.sa4.sin_addr.s_addr = INADDR_ANY;
me.sa4.sin_port = htons(port);
rc = bind(sock, &me.sa, sizeof(me.sa4));
+ break;
#ifdef ENABLE_IPV6
- }
- else {
+ case AF_INET6:
memset(&me.sa6, 0, sizeof(me.sa6));
me.sa6.sin6_family = AF_INET6;
me.sa6.sin6_addr = in6addr_any;
me.sa6.sin6_port = htons(port);
rc = bind(sock, &me.sa, sizeof(me.sa6));
- }
+ break;
#endif /* ENABLE_IPV6 */
+#ifdef USE_UNIX_SOCKETS
+ case AF_UNIX:
+ memset(&me.sau, 0, sizeof(me.sau));
+ me.sau.sun_family = AF_UNIX;
+ strncpy(me.sau.sun_path, unix_socket, sizeof(me.sau.sun_path));
+ rc = bind(sock, &me.sa, sizeof(me.sau));
+ if(0 != rc && errno == EADDRINUSE) {
+ struct stat statbuf;
+ /* socket already exists. Perhaps it is stale? */
+ int unixfd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if(CURL_SOCKET_BAD == unixfd) {
+ error = SOCKERRNO;
+ logmsg("Error binding socket, failed to create socket at %s: (%d) %s",
+ unix_socket, error, strerror(error));
+ goto sws_cleanup;
+ }
+ /* check whether the server is alive */
+ rc = connect(unixfd, &me.sa, sizeof(me.sau));
+ error = errno;
+ close(unixfd);
+ if(ECONNREFUSED != error) {
+ logmsg("Error binding socket, failed to connect to %s: (%d) %s",
+ unix_socket, error, strerror(error));
+ goto sws_cleanup;
+ }
+ /* socket server is not alive, now check if it was actually a socket.
+ * Systems which have Unix sockets will also have lstat */
+ rc = lstat(unix_socket, &statbuf);
+ if (0 != rc) {
+ logmsg("Error binding socket, failed to stat %s: (%d) %s",
+ unix_socket, errno, strerror(errno));
+ goto sws_cleanup;
+ }
+ if((statbuf.st_mode & S_IFSOCK) != S_IFSOCK) {
+ logmsg("Error binding socket, failed to stat %s: (%d) %s",
+ unix_socket, error, strerror(error));
+ goto sws_cleanup;
+ }
+ /* dead socket, cleanup and retry bind */
+ rc = unlink(unix_socket);
+ if(0 != rc) {
+ logmsg("Error binding socket, failed to unlink %s: (%d) %s",
+ unix_socket, errno, strerror(errno));
+ goto sws_cleanup;
+ }
+ /* stale socket is gone, retry bind */
+ rc = bind(sock, &me.sa, sizeof(me.sau));
+ }
+ break;
+#endif /* USE_UNIX_SOCKETS */
+ }
if(0 != rc) {
error = SOCKERRNO;
- logmsg("Error binding socket on port %hu: (%d) %s",
- port, error, strerror(error));
+ logmsg("Error binding socket on %s: (%d) %s",
+ location_str, error, strerror(error));
goto sws_cleanup;
}
- logmsg("Running %s %s version on port %d",
- use_gopher?"GOPHER":"HTTP", ipv_inuse, (int)port);
+ logmsg("Running %s %s version on %s",
+ use_gopher?"GOPHER":"HTTP", socket_type, location_str);
/* start accepting connections */
rc = listen(sock, 5);
goto sws_cleanup;
}
+#ifdef USE_UNIX_SOCKETS
+ /* listen succeeds, so let's assume a valid listening Unix socket */
+ unlink_socket = true;
+#endif
+
/*
** As soon as this server writes its pid file the test harness will
** attempt to connect to this server and initiate its verification.
if(sock != CURL_SOCKET_BAD)
sclose(sock);
+#ifdef USE_UNIX_SOCKETS
+ if(unlink_socket && socket_domain == AF_UNIX) {
+ rc = unlink(unix_socket);
+ logmsg("unlink(%s) = %d (%s)", unix_socket, rc, strerror(rc));
+ }
+#endif
+
if(got_exit_signal)
logmsg("signalled to die");
restore_signal_handlers();
if(got_exit_signal) {
- logmsg("========> %s sws (port: %d pid: %ld) exits with signal (%d)",
- ipv_inuse, (int)port, pid, exit_signal);
+ logmsg("========> %s sws (%s pid: %ld) exits with signal (%d)",
+ socket_type, location_str, pid, exit_signal);
/*
* To properly set the return status of the process we
* must raise the same signal SIGINT or SIGTERM that we