2011-01-22 12:51:57 +00:00
|
|
|
#include "private-libwebsockets.h"
|
|
|
|
|
2015-12-06 08:40:00 +08:00
|
|
|
struct lws *
|
2015-12-15 21:15:58 +08:00
|
|
|
lws_client_connect_2(struct lws *wsi)
|
2015-12-06 08:40:00 +08:00
|
|
|
{
|
2014-03-24 16:09:25 +08:00
|
|
|
#ifdef LWS_USE_IPV6
|
2014-03-24 16:09:25 +08:00
|
|
|
struct sockaddr_in6 server_addr6;
|
|
|
|
struct sockaddr_in6 client_addr6;
|
|
|
|
struct addrinfo hints, *result;
|
|
|
|
#endif
|
2015-12-15 21:15:58 +08:00
|
|
|
struct lws_context *context = wsi->context;
|
2016-01-19 03:34:24 +08:00
|
|
|
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
2014-03-24 16:09:25 +08:00
|
|
|
struct sockaddr_in server_addr4;
|
|
|
|
struct sockaddr_in client_addr4;
|
2015-12-06 08:40:00 +08:00
|
|
|
struct lws_pollfd pfd;
|
2014-03-24 16:09:25 +08:00
|
|
|
struct sockaddr *v;
|
2015-12-06 08:40:00 +08:00
|
|
|
int n, plen = 0;
|
2013-02-11 13:04:45 +08:00
|
|
|
const char *ads;
|
2011-05-23 10:00:03 +01:00
|
|
|
|
2015-12-15 21:15:58 +08:00
|
|
|
lwsl_client("%s\n", __func__);
|
2011-05-23 10:00:03 +01:00
|
|
|
|
2016-11-15 17:01:19 +08:00
|
|
|
if (!wsi->u.hdr.ah) {
|
|
|
|
lwsl_err("ah was NULL at cc2\n");
|
|
|
|
goto oom4;
|
|
|
|
}
|
|
|
|
|
2015-12-06 08:40:00 +08:00
|
|
|
/* proxy? */
|
2011-05-23 10:00:03 +01:00
|
|
|
|
|
|
|
if (context->http_proxy_port) {
|
2016-01-19 03:34:24 +08:00
|
|
|
plen = sprintf((char *)pt->serv_buf,
|
2013-02-10 15:19:39 +08:00
|
|
|
"CONNECT %s:%u HTTP/1.0\x0d\x0a"
|
2015-11-08 10:15:01 +08:00
|
|
|
"User-agent: libwebsockets\x0d\x0a",
|
2013-02-11 13:04:45 +08:00
|
|
|
lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS),
|
2013-02-18 10:38:45 +08:00
|
|
|
wsi->u.hdr.ah->c_port);
|
2015-11-08 10:15:01 +08:00
|
|
|
|
2015-11-18 19:32:01 +08:00
|
|
|
if (context->proxy_basic_auth_token[0])
|
2016-01-19 03:34:24 +08:00
|
|
|
plen += sprintf((char *)pt->serv_buf + plen,
|
2015-11-08 10:15:01 +08:00
|
|
|
"Proxy-authorization: basic %s\x0d\x0a",
|
|
|
|
context->proxy_basic_auth_token);
|
|
|
|
|
2016-01-29 21:18:54 +08:00
|
|
|
plen += sprintf((char *)pt->serv_buf + plen, "\x0d\x0a");
|
2013-10-25 22:07:57 +08:00
|
|
|
ads = context->http_proxy_address;
|
2014-03-24 16:09:25 +08:00
|
|
|
|
2014-03-24 16:09:25 +08:00
|
|
|
#ifdef LWS_USE_IPV6
|
2015-12-08 19:57:19 +09:00
|
|
|
if (LWS_IPV6_ENABLED(context)) {
|
|
|
|
memset(&server_addr6, 0, sizeof(struct sockaddr_in6));
|
2014-03-24 16:09:25 +08:00
|
|
|
server_addr6.sin6_port = htons(context->http_proxy_port);
|
2015-12-08 19:57:19 +09:00
|
|
|
} else
|
2014-03-24 16:09:25 +08:00
|
|
|
#endif
|
|
|
|
server_addr4.sin_port = htons(context->http_proxy_port);
|
|
|
|
|
2013-10-25 22:07:57 +08:00
|
|
|
} else {
|
|
|
|
ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS);
|
2014-03-25 14:08:21 +08:00
|
|
|
#ifdef LWS_USE_IPV6
|
2015-12-08 19:57:19 +09:00
|
|
|
if (LWS_IPV6_ENABLED(context)) {
|
|
|
|
memset(&server_addr6, 0, sizeof(struct sockaddr_in6));
|
2014-03-24 16:09:25 +08:00
|
|
|
server_addr6.sin6_port = htons(wsi->u.hdr.ah->c_port);
|
2015-12-08 19:57:19 +09:00
|
|
|
} else
|
2014-03-24 16:09:25 +08:00
|
|
|
#endif
|
|
|
|
server_addr4.sin_port = htons(wsi->u.hdr.ah->c_port);
|
2011-05-23 10:00:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* prepare the actual connection (to the proxy, if any)
|
|
|
|
*/
|
2015-11-08 10:15:01 +08:00
|
|
|
lwsl_client("%s: address %s\n", __func__, ads);
|
2013-02-11 13:04:45 +08:00
|
|
|
|
2014-03-24 16:09:25 +08:00
|
|
|
#ifdef LWS_USE_IPV6
|
2014-03-24 16:09:25 +08:00
|
|
|
if (LWS_IPV6_ENABLED(context)) {
|
|
|
|
memset(&hints, 0, sizeof(struct addrinfo));
|
2015-12-11 18:10:02 +09:00
|
|
|
#if !defined(__ANDROID__)
|
2015-12-09 19:24:44 +09:00
|
|
|
hints.ai_family = AF_INET6;
|
|
|
|
hints.ai_flags = AI_V4MAPPED;
|
2015-12-11 18:10:02 +09:00
|
|
|
#endif
|
2014-03-24 16:09:25 +08:00
|
|
|
n = getaddrinfo(ads, NULL, &hints, &result);
|
|
|
|
if (n) {
|
2014-04-15 18:42:57 +02:00
|
|
|
#ifdef _WIN32
|
|
|
|
lwsl_err("getaddrinfo: %ls\n", gai_strerrorW(n));
|
|
|
|
#else
|
2014-03-24 16:09:25 +08:00
|
|
|
lwsl_err("getaddrinfo: %s\n", gai_strerror(n));
|
2014-04-15 18:42:57 +02:00
|
|
|
#endif
|
2014-03-24 16:09:25 +08:00
|
|
|
goto oom4;
|
|
|
|
}
|
|
|
|
|
|
|
|
server_addr6.sin6_family = AF_INET6;
|
|
|
|
switch (result->ai_family) {
|
2015-12-11 18:10:02 +09:00
|
|
|
#if defined(__ANDROID__)
|
|
|
|
case AF_INET:
|
|
|
|
/* map IPv4 to IPv6 */
|
|
|
|
bzero((char *)&server_addr6.sin6_addr,
|
|
|
|
sizeof(struct in6_addr));
|
|
|
|
server_addr6.sin6_addr.s6_addr[10] = 0xff;
|
|
|
|
server_addr6.sin6_addr.s6_addr[11] = 0xff;
|
|
|
|
memcpy(&server_addr6.sin6_addr.s6_addr[12],
|
|
|
|
&((struct sockaddr_in *)result->ai_addr)->sin_addr,
|
|
|
|
sizeof(struct in_addr));
|
|
|
|
break;
|
|
|
|
#endif
|
2014-03-24 16:09:25 +08:00
|
|
|
case AF_INET6:
|
|
|
|
memcpy(&server_addr6.sin6_addr,
|
|
|
|
&((struct sockaddr_in6 *)result->ai_addr)->sin6_addr,
|
|
|
|
sizeof(struct in6_addr));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
lwsl_err("Unknown address family\n");
|
|
|
|
freeaddrinfo(result);
|
|
|
|
goto oom4;
|
|
|
|
}
|
|
|
|
|
|
|
|
freeaddrinfo(result);
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
2015-04-11 07:00:51 +08:00
|
|
|
struct addrinfo ai, *res, *result;
|
2015-01-28 21:03:49 +08:00
|
|
|
void *p = NULL;
|
|
|
|
|
|
|
|
memset (&ai, 0, sizeof ai);
|
|
|
|
ai.ai_family = PF_UNSPEC;
|
|
|
|
ai.ai_socktype = SOCK_STREAM;
|
|
|
|
ai.ai_flags = AI_CANONNAME;
|
|
|
|
|
2015-04-11 07:00:51 +08:00
|
|
|
if (getaddrinfo(ads, NULL, &ai, &result))
|
2014-03-24 16:09:25 +08:00
|
|
|
goto oom4;
|
2015-01-28 21:03:49 +08:00
|
|
|
|
2015-04-11 07:00:51 +08:00
|
|
|
res = result;
|
2015-01-28 21:03:49 +08:00
|
|
|
while (!p && res) {
|
|
|
|
switch (res->ai_family) {
|
|
|
|
case AF_INET:
|
|
|
|
p = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
res = res->ai_next;
|
2014-03-24 16:09:25 +08:00
|
|
|
}
|
2015-12-14 08:52:03 +08:00
|
|
|
|
2015-04-11 07:00:51 +08:00
|
|
|
if (!p) {
|
|
|
|
freeaddrinfo(result);
|
2015-01-28 21:03:49 +08:00
|
|
|
goto oom4;
|
2015-04-11 07:00:51 +08:00
|
|
|
}
|
2014-03-24 16:09:25 +08:00
|
|
|
|
|
|
|
server_addr4.sin_family = AF_INET;
|
2015-01-28 21:03:49 +08:00
|
|
|
server_addr4.sin_addr = *((struct in_addr *)p);
|
2014-03-24 16:09:25 +08:00
|
|
|
bzero(&server_addr4.sin_zero, 8);
|
2015-04-11 07:00:51 +08:00
|
|
|
freeaddrinfo(result);
|
2011-05-23 10:00:03 +01:00
|
|
|
}
|
|
|
|
|
2015-11-14 07:35:27 +08:00
|
|
|
if (!lws_socket_is_valid(wsi->sock)) {
|
2013-09-20 20:26:12 +08:00
|
|
|
|
2014-03-24 16:09:25 +08:00
|
|
|
#ifdef LWS_USE_IPV6
|
2014-03-24 16:09:25 +08:00
|
|
|
if (LWS_IPV6_ENABLED(context))
|
|
|
|
wsi->sock = socket(AF_INET6, SOCK_STREAM, 0);
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
wsi->sock = socket(AF_INET, SOCK_STREAM, 0);
|
2013-09-20 20:26:12 +08:00
|
|
|
|
2015-11-14 07:35:27 +08:00
|
|
|
if (!lws_socket_is_valid(wsi->sock)) {
|
2013-09-20 20:26:12 +08:00
|
|
|
lwsl_warn("Unable to open socket\n");
|
|
|
|
goto oom4;
|
|
|
|
}
|
|
|
|
|
2014-04-02 14:25:10 +08:00
|
|
|
if (lws_plat_set_socket_options(context, wsi->sock)) {
|
2013-09-20 20:26:12 +08:00
|
|
|
lwsl_err("Failed to set wsi socket options\n");
|
|
|
|
compatible_close(wsi->sock);
|
|
|
|
goto oom4;
|
|
|
|
}
|
|
|
|
|
2015-12-17 17:03:59 +08:00
|
|
|
wsi->mode = LWSCM_WSCL_WAITING_CONNECT;
|
2013-09-20 20:26:12 +08:00
|
|
|
|
2015-12-16 18:19:08 +08:00
|
|
|
lws_libev_accept(wsi, wsi->sock);
|
2015-11-14 13:48:58 +08:00
|
|
|
if (insert_wsi_socket_into_fds(context, wsi)) {
|
|
|
|
compatible_close(wsi->sock);
|
2015-10-15 07:39:33 +08:00
|
|
|
goto oom4;
|
2015-11-14 13:48:58 +08:00
|
|
|
}
|
2013-09-20 20:26:12 +08:00
|
|
|
|
2015-10-18 18:47:37 +08:00
|
|
|
/*
|
|
|
|
* past here, we can't simply free the structs as error
|
|
|
|
* handling as oom4 does. We have to run the whole close flow.
|
|
|
|
*/
|
2016-05-03 08:08:32 +08:00
|
|
|
if (!wsi->protocol)
|
|
|
|
wsi->protocol = &wsi->context->protocols[0];
|
|
|
|
|
2016-03-15 16:27:49 +08:00
|
|
|
wsi->protocol->callback(wsi, LWS_CALLBACK_WSI_CREATE,
|
|
|
|
wsi->user_space, NULL, 0);
|
2015-12-04 08:43:54 +08:00
|
|
|
lws_set_timeout(wsi,
|
2013-09-20 20:26:12 +08:00
|
|
|
PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE,
|
|
|
|
AWAITING_TIMEOUT);
|
2014-03-24 16:09:25 +08:00
|
|
|
#ifdef LWS_USE_IPV6
|
2014-03-24 16:09:25 +08:00
|
|
|
if (LWS_IPV6_ENABLED(context)) {
|
|
|
|
v = (struct sockaddr *)&client_addr6;
|
|
|
|
n = sizeof(client_addr6);
|
|
|
|
bzero((char *)v, n);
|
|
|
|
client_addr6.sin6_family = AF_INET6;
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
v = (struct sockaddr *)&client_addr4;
|
|
|
|
n = sizeof(client_addr4);
|
|
|
|
bzero((char *)v, n);
|
|
|
|
client_addr4.sin_family = AF_INET;
|
|
|
|
}
|
2014-02-18 10:06:57 +01:00
|
|
|
|
2014-03-24 16:09:25 +08:00
|
|
|
if (context->iface) {
|
|
|
|
if (interface_to_sa(context, context->iface,
|
|
|
|
(struct sockaddr_in *)v, n) < 0) {
|
|
|
|
lwsl_err("Unable to find interface %s\n",
|
|
|
|
context->iface);
|
2014-02-18 10:06:57 +01:00
|
|
|
goto failed;
|
|
|
|
}
|
|
|
|
|
2014-03-24 16:09:25 +08:00
|
|
|
if (bind(wsi->sock, v, n) < 0) {
|
|
|
|
lwsl_err("Error binding to interface %s",
|
|
|
|
context->iface);
|
2014-02-18 10:06:57 +01:00
|
|
|
goto failed;
|
|
|
|
}
|
|
|
|
}
|
2011-05-23 10:00:03 +01:00
|
|
|
}
|
|
|
|
|
2014-03-24 16:09:25 +08:00
|
|
|
#ifdef LWS_USE_IPV6
|
2014-03-24 16:09:25 +08:00
|
|
|
if (LWS_IPV6_ENABLED(context)) {
|
|
|
|
v = (struct sockaddr *)&server_addr6;
|
|
|
|
n = sizeof(struct sockaddr_in6);
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
v = (struct sockaddr *)&server_addr4;
|
|
|
|
n = sizeof(struct sockaddr);
|
|
|
|
}
|
2011-05-23 10:00:03 +01:00
|
|
|
|
2014-03-24 16:09:25 +08:00
|
|
|
if (connect(wsi->sock, v, n) == -1 || LWS_ERRNO == LWS_EISCONN) {
|
2015-12-06 08:40:00 +08:00
|
|
|
if (LWS_ERRNO == LWS_EALREADY ||
|
|
|
|
LWS_ERRNO == LWS_EINPROGRESS ||
|
|
|
|
LWS_ERRNO == LWS_EWOULDBLOCK
|
2015-08-26 11:31:28 -07:00
|
|
|
#ifdef _WIN32
|
|
|
|
|| LWS_ERRNO == WSAEINVAL
|
|
|
|
#endif
|
2015-12-06 08:40:00 +08:00
|
|
|
) {
|
2013-09-20 20:26:12 +08:00
|
|
|
lwsl_client("nonblocking connect retry\n");
|
2011-05-23 10:00:03 +01:00
|
|
|
|
2013-09-20 20:26:12 +08:00
|
|
|
/*
|
|
|
|
* must do specifically a POLLOUT poll to hear
|
|
|
|
* about the connect completion
|
|
|
|
*/
|
2014-04-02 14:25:10 +08:00
|
|
|
if (lws_change_pollfd(wsi, 0, LWS_POLLOUT))
|
2015-10-18 18:47:37 +08:00
|
|
|
goto failed;
|
2013-09-20 20:26:12 +08:00
|
|
|
|
|
|
|
return wsi;
|
|
|
|
}
|
|
|
|
|
2014-02-28 12:37:52 +01:00
|
|
|
if (LWS_ERRNO != LWS_EISCONN) {
|
|
|
|
lwsl_debug("Connect failed errno=%d\n", LWS_ERRNO);
|
2013-10-25 15:49:11 +02:00
|
|
|
goto failed;
|
|
|
|
}
|
2013-02-09 12:25:31 +08:00
|
|
|
}
|
|
|
|
|
2013-09-20 20:26:12 +08:00
|
|
|
lwsl_client("connected\n");
|
2011-05-23 10:00:03 +01:00
|
|
|
|
|
|
|
/* we are connected to server, or proxy */
|
|
|
|
|
|
|
|
if (context->http_proxy_port) {
|
|
|
|
|
2013-10-25 22:07:57 +08:00
|
|
|
/*
|
2015-12-06 08:40:00 +08:00
|
|
|
* OK from now on we talk via the proxy, so connect to that
|
|
|
|
*
|
2013-10-25 22:07:57 +08:00
|
|
|
* (will overwrite existing pointer,
|
|
|
|
* leaving old string/frag there but unreferenced)
|
|
|
|
*/
|
|
|
|
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS,
|
2015-12-06 08:40:00 +08:00
|
|
|
context->http_proxy_address))
|
2013-10-25 22:07:57 +08:00
|
|
|
goto failed;
|
|
|
|
wsi->u.hdr.ah->c_port = context->http_proxy_port;
|
|
|
|
|
2016-01-19 03:34:24 +08:00
|
|
|
n = send(wsi->sock, (char *)pt->serv_buf, plen,
|
2015-12-06 08:40:00 +08:00
|
|
|
MSG_NOSIGNAL);
|
2011-05-23 10:00:03 +01:00
|
|
|
if (n < 0) {
|
2013-01-10 19:50:35 +08:00
|
|
|
lwsl_debug("ERROR writing to proxy socket\n");
|
2013-09-20 20:26:12 +08:00
|
|
|
goto failed;
|
2011-05-23 10:00:03 +01:00
|
|
|
}
|
|
|
|
|
2015-12-06 08:40:00 +08:00
|
|
|
lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE,
|
|
|
|
AWAITING_TIMEOUT);
|
2011-05-23 10:00:03 +01:00
|
|
|
|
2015-12-17 17:03:59 +08:00
|
|
|
wsi->mode = LWSCM_WSCL_WAITING_PROXY_REPLY;
|
2011-05-23 10:00:03 +01:00
|
|
|
|
|
|
|
return wsi;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* provoke service to issue the handshake directly
|
|
|
|
* we need to do it this way because in the proxy case, this is the
|
|
|
|
* next state and executed only if and when we get a good proxy
|
2013-01-13 11:05:30 +08:00
|
|
|
* response inside the state machine... but notice in SSL case this
|
|
|
|
* may not have sent anything yet with 0 return, and won't until some
|
|
|
|
* many retries from main loop. To stop that becoming endless,
|
|
|
|
* cover with a timeout.
|
2011-05-23 10:00:03 +01:00
|
|
|
*/
|
|
|
|
|
2015-12-06 08:40:00 +08:00
|
|
|
lws_set_timeout(wsi, PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE,
|
|
|
|
AWAITING_TIMEOUT);
|
2013-01-13 11:05:30 +08:00
|
|
|
|
2015-12-17 17:03:59 +08:00
|
|
|
wsi->mode = LWSCM_WSCL_ISSUE_HANDSHAKE;
|
2011-05-23 10:00:03 +01:00
|
|
|
pfd.fd = wsi->sock;
|
2014-03-30 09:18:05 +02:00
|
|
|
pfd.revents = LWS_POLLIN;
|
2013-01-09 16:17:04 +08:00
|
|
|
|
2015-12-04 08:43:54 +08:00
|
|
|
n = lws_service_fd(context, &pfd);
|
2013-01-09 16:17:04 +08:00
|
|
|
if (n < 0)
|
2013-09-20 20:26:12 +08:00
|
|
|
goto failed;
|
2013-01-09 16:17:04 +08:00
|
|
|
if (n) /* returns 1 on failure after closing wsi */
|
|
|
|
return NULL;
|
|
|
|
|
2011-05-23 10:00:03 +01:00
|
|
|
return wsi;
|
|
|
|
|
|
|
|
oom4:
|
ah owns rxbuf
This is intended to solve a longstanding problem with the
relationship between http/1.1 keep-alive and the service
loop.
Ah now contain an rx buffer which is used during header
processing, and the ah may not be detached from the wsi
until the rx buffer is exhausted.
Having the rx buffer in the ah means we can delay using the
rx until a later service loop.
Ah which have pending rx force POLLIN service on the wsi
they are attached to automatically, so we can interleave
general service / connections with draining each ah rx
buffer.
The possible http/1.1 situations and their dispositions are:
1) exactly one set of http headers come. After processing,
the ah is detached since no pending rx left. If more
headers come later, a fresh ah is aqcuired when available
and the rx flow control blocks the read until then.
2) more that one whole set of headers come and we remain in
http mode (no upgrade). The ah is left attached and
returns to the service loop after the first set of headers.
We will get forced service due to the ah having pending
content (respecting flowcontrol) and process the pending
rx in the ah. If we use it all up, we will detach the
ah.
3) one set of http headers come with ws traffic appended.
We service the headers, do the upgrade, and keep the ah
until the remaining ws content is used. When we
exhausted the ws traffix in the ah rx buffer, we
detach the ah.
Since there can be any amount of http/1.1 pipelining on a
connection, and each may be expensive to service, it's now
enforced there is a return to the service loop after each
header set is serviced on a connection.
When I added the forced service for ah with pending buffering,
I added support for it to the windows plat code. However this
is untested.
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-15 12:37:04 +08:00
|
|
|
/* we're closing, losing some rx is OK */
|
2016-12-12 20:41:05 +08:00
|
|
|
if (wsi->u.hdr.ah)
|
|
|
|
wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen;
|
|
|
|
|
ah owns rxbuf
This is intended to solve a longstanding problem with the
relationship between http/1.1 keep-alive and the service
loop.
Ah now contain an rx buffer which is used during header
processing, and the ah may not be detached from the wsi
until the rx buffer is exhausted.
Having the rx buffer in the ah means we can delay using the
rx until a later service loop.
Ah which have pending rx force POLLIN service on the wsi
they are attached to automatically, so we can interleave
general service / connections with draining each ah rx
buffer.
The possible http/1.1 situations and their dispositions are:
1) exactly one set of http headers come. After processing,
the ah is detached since no pending rx left. If more
headers come later, a fresh ah is aqcuired when available
and the rx flow control blocks the read until then.
2) more that one whole set of headers come and we remain in
http mode (no upgrade). The ah is left attached and
returns to the service loop after the first set of headers.
We will get forced service due to the ah having pending
content (respecting flowcontrol) and process the pending
rx in the ah. If we use it all up, we will detach the
ah.
3) one set of http headers come with ws traffic appended.
We service the headers, do the upgrade, and keep the ah
until the remaining ws content is used. When we
exhausted the ws traffix in the ah rx buffer, we
detach the ah.
Since there can be any amount of http/1.1 pipelining on a
connection, and each may be expensive to service, it's now
enforced there is a return to the service loop after each
header set is serviced on a connection.
When I added the forced service for ah with pending buffering,
I added support for it to the windows plat code. However this
is untested.
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-15 12:37:04 +08:00
|
|
|
lws_header_table_detach(wsi);
|
2016-12-12 20:41:05 +08:00
|
|
|
|
2016-04-22 12:40:44 +08:00
|
|
|
/* take care that we might be inserted in fds already */
|
|
|
|
if (wsi->position_in_fds_table != -1)
|
|
|
|
goto failed;
|
2014-12-04 23:59:35 +01:00
|
|
|
lws_free(wsi);
|
2015-12-17 17:03:59 +08:00
|
|
|
|
2013-09-20 20:26:12 +08:00
|
|
|
return NULL;
|
2011-05-23 10:00:03 +01:00
|
|
|
|
2013-09-20 20:26:12 +08:00
|
|
|
failed:
|
2015-12-15 21:15:58 +08:00
|
|
|
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
|
2015-12-06 08:40:00 +08:00
|
|
|
|
2011-05-23 10:00:03 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
2016-01-14 11:37:56 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* lws_client_reset() - retarget a connected wsi to start over with a new connection (ie, redirect)
|
|
|
|
* this only works if still in HTTP, ie, not upgraded yet
|
|
|
|
* wsi: connection to reset
|
|
|
|
* address: network address of the new server
|
|
|
|
* port: port to connect to
|
|
|
|
* path: uri path to connect to on the new server
|
|
|
|
* host: host header to send to the new server
|
|
|
|
*/
|
|
|
|
LWS_VISIBLE struct lws *
|
|
|
|
lws_client_reset(struct lws *wsi, int ssl, const char *address, int port, const char *path, const char *host)
|
|
|
|
{
|
|
|
|
if (wsi->u.hdr.redirects == 3) {
|
|
|
|
lwsl_err("%s: Too many redirects\n", __func__);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
wsi->u.hdr.redirects++;
|
|
|
|
|
|
|
|
#ifdef LWS_OPENSSL_SUPPORT
|
|
|
|
wsi->use_ssl = ssl;
|
|
|
|
#else
|
|
|
|
if (ssl) {
|
|
|
|
lwsl_err("%s: not configured for ssl\n", __func__);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
lwsl_notice("redirect ads='%s', port=%d, path='%s'\n", address, port, path);
|
|
|
|
|
|
|
|
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, address))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_URI, path))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_HOST, host))
|
|
|
|
return NULL;
|
|
|
|
|
2016-01-16 11:14:46 +08:00
|
|
|
compatible_close(wsi->sock);
|
2016-01-14 11:37:56 +08:00
|
|
|
remove_wsi_socket_from_fds(wsi);
|
|
|
|
wsi->sock = LWS_SOCK_INVALID;
|
|
|
|
wsi->state = LWSS_CLIENT_UNCONNECTED;
|
|
|
|
wsi->protocol = NULL;
|
|
|
|
wsi->pending_timeout = NO_PENDING_TIMEOUT;
|
|
|
|
wsi->u.hdr.ah->c_port = port;
|
|
|
|
|
|
|
|
return lws_client_connect_2(wsi);
|
|
|
|
}
|
|
|
|
|
2011-01-27 06:26:52 +00:00
|
|
|
/**
|
2016-01-12 17:22:06 +08:00
|
|
|
* lws_client_connect_via_info() - Connect to another websocket server
|
|
|
|
* @i:pointer to lws_client_connect_info struct
|
2011-01-27 06:26:52 +00:00
|
|
|
*
|
|
|
|
* This function creates a connection to a remote server
|
|
|
|
*/
|
|
|
|
|
2016-01-12 17:22:06 +08:00
|
|
|
|
2015-12-04 11:08:32 +08:00
|
|
|
LWS_VISIBLE struct lws *
|
2016-01-12 17:22:06 +08:00
|
|
|
lws_client_connect_via_info(struct lws_client_connect_info *i)
|
2011-01-22 12:51:57 +00:00
|
|
|
{
|
2015-12-04 11:08:32 +08:00
|
|
|
struct lws *wsi;
|
2016-01-11 11:34:01 +08:00
|
|
|
int v = SPEC_LATEST_SUPPORTED;
|
2013-01-20 17:08:31 +08:00
|
|
|
|
2015-12-04 11:08:32 +08:00
|
|
|
wsi = lws_zalloc(sizeof(struct lws));
|
2011-02-14 20:25:43 +00:00
|
|
|
if (wsi == NULL)
|
2013-02-11 21:43:41 +08:00
|
|
|
goto bail;
|
2011-01-22 12:51:57 +00:00
|
|
|
|
2016-01-11 11:34:01 +08:00
|
|
|
wsi->context = i->context;
|
2015-11-14 13:48:58 +08:00
|
|
|
wsi->sock = LWS_SOCK_INVALID;
|
2011-02-14 17:52:39 +00:00
|
|
|
|
2011-02-09 08:49:14 +00:00
|
|
|
/* -1 means just use latest supported */
|
|
|
|
|
2016-01-11 11:34:01 +08:00
|
|
|
if (i->ietf_version_or_minus_one != -1 && i->ietf_version_or_minus_one)
|
|
|
|
v = i->ietf_version_or_minus_one;
|
2011-02-09 08:49:14 +00:00
|
|
|
|
2016-01-11 11:34:01 +08:00
|
|
|
wsi->ietf_spec_revision = v;
|
2011-01-22 12:51:57 +00:00
|
|
|
wsi->user_space = NULL;
|
2015-12-17 17:03:59 +08:00
|
|
|
wsi->state = LWSS_CLIENT_UNCONNECTED;
|
2011-02-01 08:52:55 +00:00
|
|
|
wsi->protocol = NULL;
|
2011-02-14 17:59:43 +00:00
|
|
|
wsi->pending_timeout = NO_PENDING_TIMEOUT;
|
2016-02-22 23:35:12 +08:00
|
|
|
wsi->position_in_fds_table = -1;
|
2014-04-02 19:45:42 +08:00
|
|
|
|
2011-02-14 20:25:43 +00:00
|
|
|
#ifdef LWS_OPENSSL_SUPPORT
|
2016-01-11 11:34:01 +08:00
|
|
|
wsi->use_ssl = i->ssl_connection;
|
2014-04-03 10:17:00 +08:00
|
|
|
#else
|
2016-01-11 11:34:01 +08:00
|
|
|
if (i->ssl_connection) {
|
2014-04-03 10:17:00 +08:00
|
|
|
lwsl_err("libwebsockets not configured for ssl\n");
|
|
|
|
goto bail;
|
|
|
|
}
|
2011-02-14 20:25:43 +00:00
|
|
|
#endif
|
|
|
|
|
ah owns rxbuf
This is intended to solve a longstanding problem with the
relationship between http/1.1 keep-alive and the service
loop.
Ah now contain an rx buffer which is used during header
processing, and the ah may not be detached from the wsi
until the rx buffer is exhausted.
Having the rx buffer in the ah means we can delay using the
rx until a later service loop.
Ah which have pending rx force POLLIN service on the wsi
they are attached to automatically, so we can interleave
general service / connections with draining each ah rx
buffer.
The possible http/1.1 situations and their dispositions are:
1) exactly one set of http headers come. After processing,
the ah is detached since no pending rx left. If more
headers come later, a fresh ah is aqcuired when available
and the rx flow control blocks the read until then.
2) more that one whole set of headers come and we remain in
http mode (no upgrade). The ah is left attached and
returns to the service loop after the first set of headers.
We will get forced service due to the ah having pending
content (respecting flowcontrol) and process the pending
rx in the ah. If we use it all up, we will detach the
ah.
3) one set of http headers come with ws traffic appended.
We service the headers, do the upgrade, and keep the ah
until the remaining ws content is used. When we
exhausted the ws traffix in the ah rx buffer, we
detach the ah.
Since there can be any amount of http/1.1 pipelining on a
connection, and each may be expensive to service, it's now
enforced there is a return to the service loop after each
header set is serviced on a connection.
When I added the forced service for ah with pending buffering,
I added support for it to the windows plat code. However this
is untested.
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-15 12:37:04 +08:00
|
|
|
if (lws_header_table_attach(wsi))
|
2013-02-11 13:04:45 +08:00
|
|
|
goto bail;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* we're not necessarily in a position to action these right away,
|
|
|
|
* stash them... we only need during connect phase so u.hdr is fine
|
|
|
|
*/
|
2016-01-11 11:34:01 +08:00
|
|
|
wsi->u.hdr.ah->c_port = i->port;
|
|
|
|
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, i->address))
|
2013-02-11 13:04:45 +08:00
|
|
|
goto bail1;
|
2011-05-23 10:00:03 +01:00
|
|
|
|
2013-02-11 13:04:45 +08:00
|
|
|
/* these only need u.hdr lifetime as well */
|
2011-02-14 20:25:43 +00:00
|
|
|
|
2016-01-11 11:34:01 +08:00
|
|
|
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_URI, i->path))
|
2011-02-14 20:25:43 +00:00
|
|
|
goto bail1;
|
|
|
|
|
2016-01-11 11:34:01 +08:00
|
|
|
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_HOST, i->host))
|
2013-02-11 13:04:45 +08:00
|
|
|
goto bail1;
|
2012-04-20 12:16:52 +08:00
|
|
|
|
2016-01-11 11:34:01 +08:00
|
|
|
if (i->origin)
|
|
|
|
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_ORIGIN, i->origin))
|
2013-02-11 13:04:45 +08:00
|
|
|
goto bail1;
|
|
|
|
/*
|
|
|
|
* this is a list of protocols we tell the server we're okay with
|
|
|
|
* stash it for later when we compare server response with it
|
|
|
|
*/
|
2016-01-11 11:34:01 +08:00
|
|
|
if (i->protocol)
|
2015-12-06 08:40:00 +08:00
|
|
|
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS,
|
2016-01-11 11:34:01 +08:00
|
|
|
i->protocol))
|
2013-02-11 13:04:45 +08:00
|
|
|
goto bail1;
|
|
|
|
|
2016-01-11 11:34:01 +08:00
|
|
|
wsi->protocol = &i->context->protocols[0];
|
|
|
|
if (wsi && !wsi->user_space && i->userdata) {
|
|
|
|
wsi->user_space_externally_allocated = 1;
|
|
|
|
wsi->user_space = i->userdata;
|
|
|
|
}
|
2011-01-22 12:51:57 +00:00
|
|
|
|
|
|
|
/*
|
2011-05-23 10:00:03 +01:00
|
|
|
* Check with each extension if it is able to route and proxy this
|
|
|
|
* connection for us. For example, an extension like x-google-mux
|
|
|
|
* can handle this and then we don't need an actual socket for this
|
|
|
|
* connection.
|
2011-01-27 22:01:43 +00:00
|
|
|
*/
|
2015-12-14 08:52:03 +08:00
|
|
|
|
2016-01-11 11:34:01 +08:00
|
|
|
if (lws_ext_cb_all_exts(i->context, wsi,
|
|
|
|
LWS_EXT_CB_CAN_PROXY_CLIENT_CONNECTION,
|
|
|
|
(void *)i->address, i->port) > 0) {
|
2015-12-04 08:43:54 +08:00
|
|
|
lwsl_client("lws_client_connect: ext handling conn\n");
|
2011-01-28 09:39:29 +00:00
|
|
|
|
2015-12-04 08:43:54 +08:00
|
|
|
lws_set_timeout(wsi,
|
2013-02-11 17:13:32 +08:00
|
|
|
PENDING_TIMEOUT_AWAITING_EXTENSION_CONNECT_RESPONSE,
|
2015-12-06 08:40:00 +08:00
|
|
|
AWAITING_TIMEOUT);
|
2011-01-27 06:26:52 +00:00
|
|
|
|
2015-12-17 17:03:59 +08:00
|
|
|
wsi->mode = LWSCM_WSCL_WAITING_EXTENSION_CONNECT;
|
2011-02-14 20:25:43 +00:00
|
|
|
return wsi;
|
2011-01-27 22:01:43 +00:00
|
|
|
}
|
2015-12-04 08:43:54 +08:00
|
|
|
lwsl_client("lws_client_connect: direct conn\n");
|
2011-01-22 12:51:57 +00:00
|
|
|
|
2016-02-14 09:27:41 +08:00
|
|
|
wsi->context->count_wsi_allocated++;
|
|
|
|
|
2015-12-25 12:44:12 +08:00
|
|
|
return lws_client_connect_2(wsi);
|
2011-01-22 12:51:57 +00:00
|
|
|
|
|
|
|
bail1:
|
ah owns rxbuf
This is intended to solve a longstanding problem with the
relationship between http/1.1 keep-alive and the service
loop.
Ah now contain an rx buffer which is used during header
processing, and the ah may not be detached from the wsi
until the rx buffer is exhausted.
Having the rx buffer in the ah means we can delay using the
rx until a later service loop.
Ah which have pending rx force POLLIN service on the wsi
they are attached to automatically, so we can interleave
general service / connections with draining each ah rx
buffer.
The possible http/1.1 situations and their dispositions are:
1) exactly one set of http headers come. After processing,
the ah is detached since no pending rx left. If more
headers come later, a fresh ah is aqcuired when available
and the rx flow control blocks the read until then.
2) more that one whole set of headers come and we remain in
http mode (no upgrade). The ah is left attached and
returns to the service loop after the first set of headers.
We will get forced service due to the ah having pending
content (respecting flowcontrol) and process the pending
rx in the ah. If we use it all up, we will detach the
ah.
3) one set of http headers come with ws traffic appended.
We service the headers, do the upgrade, and keep the ah
until the remaining ws content is used. When we
exhausted the ws traffix in the ah rx buffer, we
detach the ah.
Since there can be any amount of http/1.1 pipelining on a
connection, and each may be expensive to service, it's now
enforced there is a return to the service loop after each
header set is serviced on a connection.
When I added the forced service for ah with pending buffering,
I added support for it to the windows plat code. However this
is untested.
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-15 12:37:04 +08:00
|
|
|
/* we're closing, losing some rx is OK */
|
|
|
|
wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen;
|
|
|
|
lws_header_table_detach(wsi);
|
2013-02-11 13:04:45 +08:00
|
|
|
bail:
|
2014-12-04 23:59:35 +01:00
|
|
|
lws_free(wsi);
|
2011-01-22 12:51:57 +00:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
2012-04-20 12:19:01 +08:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
2015-12-04 08:43:54 +08:00
|
|
|
* lws_client_connect_extended() - Connect to another websocket server
|
2016-01-12 17:22:06 +08:00
|
|
|
* DEPRECATED use lws_client_connect_via_info
|
2012-04-20 12:19:01 +08:00
|
|
|
* @context: Websocket context
|
|
|
|
* @address: Remote server address, eg, "myserver.com"
|
|
|
|
* @port: Port to connect to on the remote server, eg, 80
|
|
|
|
* @ssl_connection: 0 = ws://, 1 = wss:// encrypted, 2 = wss:// allow self
|
|
|
|
* signed certs
|
|
|
|
* @path: Websocket path on server
|
|
|
|
* @host: Hostname on server
|
|
|
|
* @origin: Socket origin name
|
|
|
|
* @protocol: Comma-separated list of protocols being asked for from
|
|
|
|
* the server, or just one. The server will pick the one it
|
|
|
|
* likes best.
|
|
|
|
* @ietf_version_or_minus_one: -1 to ask to connect using the default, latest
|
2013-02-11 17:13:32 +08:00
|
|
|
* protocol supported, or the specific protocol ordinal
|
2012-04-20 12:19:01 +08:00
|
|
|
* @userdata: Pre-allocated user data
|
|
|
|
*
|
|
|
|
* This function creates a connection to a remote server
|
|
|
|
*/
|
|
|
|
|
2015-12-04 11:08:32 +08:00
|
|
|
LWS_VISIBLE struct lws *
|
2015-12-06 08:40:00 +08:00
|
|
|
lws_client_connect_extended(struct lws_context *context, const char *address,
|
|
|
|
int port, int ssl_connection, const char *path,
|
|
|
|
const char *host, const char *origin,
|
|
|
|
const char *protocol, int ietf_version_or_minus_one,
|
|
|
|
void *userdata)
|
2012-04-20 12:19:01 +08:00
|
|
|
{
|
2016-01-11 11:34:01 +08:00
|
|
|
struct lws_client_connect_info i;
|
|
|
|
|
|
|
|
memset(&i, 0, sizeof(i));
|
|
|
|
|
|
|
|
i.context = context;
|
|
|
|
i.address = address;
|
|
|
|
i.port = port;
|
|
|
|
i.ssl_connection = ssl_connection;
|
|
|
|
i.path = path;
|
|
|
|
i.host = host;
|
|
|
|
i.origin = origin;
|
|
|
|
i.protocol = protocol;
|
|
|
|
i.ietf_version_or_minus_one = ietf_version_or_minus_one;
|
|
|
|
i.userdata = userdata;
|
|
|
|
|
2016-01-12 17:22:06 +08:00
|
|
|
return lws_client_connect_via_info(&i);
|
2016-01-11 11:34:01 +08:00
|
|
|
}
|
2016-01-12 17:22:06 +08:00
|
|
|
/**
|
|
|
|
* lws_client_connect_info() - Connect to another websocket server
|
|
|
|
* DEPRECATED use lws_client_connect_via_info
|
|
|
|
* @context: Websocket context
|
|
|
|
* @address: Remote server address, eg, "myserver.com"
|
|
|
|
* @port: Port to connect to on the remote server, eg, 80
|
|
|
|
* @ssl_connection: 0 = ws://, 1 = wss:// encrypted, 2 = wss:// allow self
|
|
|
|
* signed certs
|
|
|
|
* @path: Websocket path on server
|
|
|
|
* @host: Hostname on server
|
|
|
|
* @origin: Socket origin name
|
|
|
|
* @protocol: Comma-separated list of protocols being asked for from
|
|
|
|
* the server, or just one. The server will pick the one it
|
|
|
|
* likes best. If you don't want to specify a protocol, which is
|
|
|
|
* legal, use NULL here.
|
|
|
|
* @ietf_version_or_minus_one: -1 to ask to connect using the default, latest
|
|
|
|
* protocol supported, or the specific protocol ordinal
|
|
|
|
*
|
|
|
|
* This function creates a connection to a remote server
|
|
|
|
*/
|
|
|
|
|
2015-12-06 08:40:00 +08:00
|
|
|
|
2016-01-11 11:34:01 +08:00
|
|
|
LWS_VISIBLE struct lws *
|
|
|
|
lws_client_connect(struct lws_context *context, const char *address,
|
|
|
|
int port, int ssl_connection, const char *path,
|
|
|
|
const char *host, const char *origin,
|
|
|
|
const char *protocol, int ietf_version_or_minus_one)
|
|
|
|
{
|
2016-02-02 09:02:24 +08:00
|
|
|
struct lws_client_connect_info i;
|
|
|
|
|
|
|
|
memset(&i, 0, sizeof(i));
|
|
|
|
|
|
|
|
i.context = context;
|
|
|
|
i.address = address;
|
|
|
|
i.port = port;
|
|
|
|
i.ssl_connection = ssl_connection;
|
|
|
|
i.path = path;
|
|
|
|
i.host = host;
|
|
|
|
i.origin = origin;
|
|
|
|
i.protocol = protocol;
|
|
|
|
i.ietf_version_or_minus_one = ietf_version_or_minus_one;
|
|
|
|
i.userdata = NULL;
|
|
|
|
|
|
|
|
return lws_client_connect_via_info(&i);
|
2013-02-11 17:13:32 +08:00
|
|
|
}
|
2016-01-11 11:34:01 +08:00
|
|
|
|