client support http without ws

Server support for http[s] as well as ws[s] is implicit.
But until now client only supported ws[s].

This allows the user code to pass an explicit http method
like "GET" in the connect_info, disabling the ws upgrade logic.

Then you can also use lws client as http client, not just ws.

Signed-off-by: Andy Green <andy.green@linaro.org>
This commit is contained in:
Andy Green 2016-02-29 13:18:30 +08:00
parent 2d8d35a1be
commit a661ee5d53
11 changed files with 988 additions and 721 deletions

View file

@ -75,6 +75,11 @@ feature. Input sanitization added to the js.
7) client connections attempted when no ah is free no longer fail, they are
just deferred until an ah becomes available.
8) The test client pays attention to if you give it an http:/ or https://
protocol string to its argument in URL format. If so, it stays in http[s]
client mode and doesn't upgrade to ws[s], allowing you to do generic http client
operations.
User API additions
------------------
@ -125,6 +130,24 @@ this generates this kind of timestamp for use as logging preamble
lwsts[13116]: [2016/01/25 14:52:52:8386] NOTICE: Initial logging level 7
5) struct lws_client_connect_info has a new member
const char *method
If it's NULL, then everything happens as before, lws_client_connect_via_info()
makes a ws or wss connection to the address given.
If you set method to a valid http method like "GET", though, then this method
is used and the connection remains in http[s], it's not upgraded to ws[s].
So with this, you can perform http[s] client operations as well as ws[s] ones.
There are 4 new related callbacks
LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP = 44,
LWS_CALLBACK_CLOSED_CLIENT_HTTP = 45,
LWS_CALLBACK_RECEIVE_CLIENT_HTTP = 46,
LWS_CALLBACK_COMPLETED_CLIENT_HTTP = 47,
v1.7.0

View file

@ -383,7 +383,7 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
wsi->context = i->context;
/* assert the mode and union status (hdr) clearly */
lws_union_transition(wsi, LWSCM_HTTP_SERVING);
lws_union_transition(wsi, LWSCM_HTTP_CLIENT);
wsi->sock = LWS_SOCK_INVALID;
/* 1) fill up the wsi with stuff from the connect_info as far as it
@ -417,6 +417,11 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
goto bail;
}
#endif
wsi->protocol = &i->context->protocols[0];
if (wsi && !wsi->user_space && i->userdata) {
wsi->user_space_externally_allocated = 1;
wsi->user_space = i->userdata;
}
/* 2) stash the things from connect_info that we can't process without
* an ah. Because if no ah, we will go on the ah waiting list and
@ -432,6 +437,7 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
wsi->u.hdr.stash->origin[0] = '\0';
wsi->u.hdr.stash->protocol[0] = '\0';
wsi->u.hdr.stash->method[0] = '\0';
strncpy(wsi->u.hdr.stash->address, i->address,
sizeof(wsi->u.hdr.stash->address) - 1);
@ -445,12 +451,16 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
if (i->protocol)
strncpy(wsi->u.hdr.stash->protocol, i->protocol,
sizeof(wsi->u.hdr.stash->protocol) - 1);
if (i->method)
strncpy(wsi->u.hdr.stash->method, i->method,
sizeof(wsi->u.hdr.stash->method) - 1);
wsi->u.hdr.stash->address[sizeof(wsi->u.hdr.stash->address) - 1] = '\0';
wsi->u.hdr.stash->path[sizeof(wsi->u.hdr.stash->path) - 1] = '\0';
wsi->u.hdr.stash->host[sizeof(wsi->u.hdr.stash->host) - 1] = '\0';
wsi->u.hdr.stash->origin[sizeof(wsi->u.hdr.stash->origin) - 1] = '\0';
wsi->u.hdr.stash->protocol[sizeof(wsi->u.hdr.stash->protocol) - 1] = '\0';
wsi->u.hdr.stash->method[sizeof(wsi->u.hdr.stash->method) - 1] = '\0';
/* if we went on the waiting list, no probs just return the wsi
* when we get the ah, now or later, he will call
@ -503,6 +513,10 @@ lws_client_connect_via_info2(struct lws *wsi)
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS,
stash->protocol))
goto bail1;
if (stash->method[0])
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_METHOD,
stash->method))
goto bail1;
lws_free_set_NULL(wsi->u.hdr.stash);

View file

@ -492,6 +492,51 @@ strtolower(char *s)
}
}
/**
* lws_http_transaction_completed() - wait for new http transaction or close
* @wsi: websocket connection
*
* Returns 1 if the HTTP connection must close now
* Returns 0 and resets connection to wait for new HTTP header /
* transaction if possible
*/
LWS_VISIBLE int LWS_WARN_UNUSED_RESULT
lws_http_transaction_completed_client(struct lws *wsi)
{
lwsl_debug("%s: wsi %p\n", __func__, wsi);
/* if we can't go back to accept new headers, drop the connection */
if (wsi->u.http.connection_type != HTTP_CONNECTION_KEEP_ALIVE) {
lwsl_info("%s: %p: close connection\n", __func__, wsi);
return 1;
}
/* otherwise set ourselves up ready to go again */
wsi->state = LWSS_CLIENT_HTTP_ESTABLISHED;
wsi->mode = LWSCM_HTTP_CLIENT_ACCEPTED;
wsi->u.http.content_length = 0;
wsi->hdr_parsing_completed = 0;
/* He asked for it to stay alive indefinitely */
lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
/*
* As client, nothing new is going to come until we ask for it
* we can drop the ah, if any
*/
if (wsi->u.hdr.ah) {
wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen;
lws_header_table_detach(wsi, 0);
}
/* If we're (re)starting on headers, need other implied init */
wsi->u.hdr.ues = URIES_IDLE;
lwsl_info("%s: %p: keep-alive await new transaction\n", __func__, wsi);
return 0;
}
int
lws_client_interpret_server_handshake(struct lws *wsi)
{
@ -499,6 +544,7 @@ lws_client_interpret_server_handshake(struct lws *wsi)
struct lws_context *context = wsi->context;
int close_reason = LWS_CLOSE_STATUS_PROTOCOL_ERR;
const char *pc, *prot, *ads = NULL, *path;
struct allocated_headers *ah;
char *p;
#ifndef LWS_NO_EXTENSIONS
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
@ -512,11 +558,41 @@ lws_client_interpret_server_handshake(struct lws *wsi)
void *v;
#endif
if (!wsi->do_ws) {
/* we are being an http client...
*/
ah = wsi->u.hdr.ah;
lws_union_transition(wsi, LWSCM_HTTP_CLIENT_ACCEPTED);
wsi->state = LWSS_CLIENT_HTTP_ESTABLISHED;
wsi->u.http.ah = ah;
}
/*
* well, what the server sent looked reasonable for syntax.
* Now let's confirm it sent all the necessary headers
*
* http (non-ws) client will expect something like this
*
* HTTP/1.0.200
* server:.libwebsockets
* content-type:.text/html
* content-length:.17703
* set-cookie:.test=LWS_1456736240_336776_COOKIE;Max-Age=360000
*
*
*
*/
wsi->u.http.connection_type = HTTP_CONNECTION_KEEP_ALIVE;
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP);
if (wsi->do_ws && !p) {
lwsl_info("no URI\n");
goto bail3;
}
if (!p) {
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP1_0);
wsi->u.http.connection_type = HTTP_CONNECTION_CLOSE;
}
if (!p) {
lwsl_info("no URI\n");
goto bail3;
@ -539,6 +615,56 @@ lws_client_interpret_server_handshake(struct lws *wsi)
}
return 0;
}
if (!wsi->do_ws) {
if (n != 200) {
lwsl_notice("Connection failed with code %d", n);
goto bail2;
}
/* allocate the per-connection user memory (if any) */
if (lws_ensure_user_space(wsi)) {
lwsl_err("Problem allocating wsi user mem\n");
goto bail2;
}
if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
wsi->u.http.content_length =
atoi(lws_hdr_simple_ptr(wsi,
WSI_TOKEN_HTTP_CONTENT_LENGTH));
lwsl_notice("%s: incoming content length %d\n", __func__,
wsi->u.http.content_length);
wsi->u.http.content_remain = wsi->u.http.content_length;
} else /* can't do 1.1 without a content length */
wsi->u.http.connection_type = HTTP_CONNECTION_CLOSE;
/*
* we seem to be good to go, give client last chance to check
* headers and OK it
*/
if (wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH,
wsi->user_space, NULL, 0))
goto bail2;
/* clear his proxy connection timeout */
lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;
/* call him back to inform him he is up */
if (wsi->protocol->callback(wsi,
LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP,
wsi->user_space, NULL, 0))
goto bail3;
/* free up his parsing allocations */
lws_header_table_detach(wsi, 0);
lwsl_notice("%s: client connection up\n", __func__);
return 0;
}
if (lws_hdr_total_length(wsi, WSI_TOKEN_ACCEPT) == 0) {
lwsl_info("no ACCEPT\n");
isErrorCodeReceived = 1;
@ -887,39 +1013,36 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt)
{
char buf[128], hash[20], key_b64[40], *p = pkt;
struct lws_context *context = wsi->context;
const char *meth;
int n;
#ifndef LWS_NO_EXTENSIONS
const struct lws_extension *ext;
int ext_count = 0;
#endif
/*
* create the random key
*/
n = lws_get_random(context, hash, 16);
if (n != 16) {
lwsl_err("Unable to read from random dev %s\n",
SYSTEM_RANDOM_FILEPATH);
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
return NULL;
meth = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD);
if (!meth) {
meth = "GET";
wsi->do_ws = 1;
} else
wsi->do_ws = 0;
if (wsi->do_ws) {
/*
* create the random key
*/
n = lws_get_random(context, hash, 16);
if (n != 16) {
lwsl_err("Unable to read from random dev %s\n",
SYSTEM_RANDOM_FILEPATH);
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
return NULL;
}
lws_b64_encode_string(hash, 16, key_b64, sizeof(key_b64));
}
lws_b64_encode_string(hash, 16, key_b64, sizeof(key_b64));
/*
* 00 example client handshake
*
* GET /socket.io/websocket HTTP/1.1
* Upgrade: WebSocket
* Connection: Upgrade
* Host: 127.0.0.1:9999
* Origin: http://127.0.0.1
* Sec-WebSocket-Key1: 1 0 2#0W 9 89 7 92 ^
* Sec-WebSocket-Key2: 7 7Y 4328 B2v[8(z1
* Cookie: socketio=websocket
*
* (稀0
*
* 04 example client handshake
*
* GET /chat HTTP/1.1
@ -932,7 +1055,7 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt)
* Sec-WebSocket-Version: 4
*/
p += sprintf(p, "GET %s HTTP/1.1\x0d\x0a",
p += sprintf(p, "%s %s HTTP/1.1\x0d\x0a", meth,
lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI));
p += sprintf(p, "Pragma: no-cache\x0d\x0a"
@ -940,65 +1063,79 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt)
p += sprintf(p, "Host: %s\x0d\x0a",
lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_HOST));
p += sprintf(p, "Upgrade: websocket\x0d\x0a"
"Connection: Upgrade\x0d\x0a"
"Sec-WebSocket-Key: ");
strcpy(p, key_b64);
p += strlen(key_b64);
p += sprintf(p, "\x0d\x0a");
if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN))
p += sprintf(p, "Origin: http://%s\x0d\x0a",
lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN));
if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS))
p += sprintf(p, "Sec-WebSocket-Protocol: %s\x0d\x0a",
lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS));
if (wsi->do_ws) {
p += sprintf(p, "Upgrade: websocket\x0d\x0a"
"Connection: Upgrade\x0d\x0a"
"Sec-WebSocket-Key: ");
strcpy(p, key_b64);
p += strlen(key_b64);
p += sprintf(p, "\x0d\x0a");
if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS))
p += sprintf(p, "Sec-WebSocket-Protocol: %s\x0d\x0a",
lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS));
/* tell the server what extensions we could support */
/* tell the server what extensions we could support */
p += sprintf(p, "Sec-WebSocket-Extensions: ");
p += sprintf(p, "Sec-WebSocket-Extensions: ");
#ifndef LWS_NO_EXTENSIONS
ext = context->extensions;
while (ext && ext->callback) {
n = lws_ext_cb_all_exts(context, wsi,
LWS_EXT_CB_CHECK_OK_TO_PROPOSE_EXTENSION,
(char *)ext->name, 0);
if (n) { /* an extension vetos us */
lwsl_ext("ext %s vetoed\n", (char *)ext->name);
ext = context->extensions;
while (ext && ext->callback) {
n = lws_ext_cb_all_exts(context, wsi,
LWS_EXT_CB_CHECK_OK_TO_PROPOSE_EXTENSION,
(char *)ext->name, 0);
if (n) { /* an extension vetos us */
lwsl_ext("ext %s vetoed\n", (char *)ext->name);
ext++;
continue;
}
n = context->protocols[0].callback(wsi,
LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED,
wsi->user_space, (char *)ext->name, 0);
/*
* zero return from callback means
* go ahead and allow the extension,
* it's what we get if the callback is
* unhandled
*/
if (n) {
ext++;
continue;
}
/* apply it */
if (ext_count)
*p++ = ',';
p += sprintf(p, "%s", ext->client_offer);
ext_count++;
ext++;
continue;
}
n = context->protocols[0].callback(wsi,
LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED,
wsi->user_space, (char *)ext->name, 0);
/*
* zero return from callback means
* go ahead and allow the extension,
* it's what we get if the callback is
* unhandled
*/
if (n) {
ext++;
continue;
}
/* apply it */
if (ext_count)
*p++ = ',';
p += sprintf(p, "%s", ext->client_offer);
ext_count++;
ext++;
}
#endif
p += sprintf(p, "\x0d\x0a");
p += sprintf(p, "\x0d\x0a");
if (wsi->ietf_spec_revision)
p += sprintf(p, "Sec-WebSocket-Version: %d\x0d\x0a",
wsi->ietf_spec_revision);
if (wsi->ietf_spec_revision)
p += sprintf(p, "Sec-WebSocket-Version: %d\x0d\x0a",
wsi->ietf_spec_revision);
/* prepare the expected server accept response */
key_b64[39] = '\0'; /* enforce composed length below buf sizeof */
n = sprintf(buf, "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11", key_b64);
lws_SHA1((unsigned char *)buf, n, (unsigned char *)hash);
lws_b64_encode_string(hash, 20,
wsi->u.hdr.ah->initial_handshake_hash_base64,
sizeof(wsi->u.hdr.ah->initial_handshake_hash_base64));
}
/* give userland a chance to append, eg, cookies */
@ -1007,17 +1144,6 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt)
p += sprintf(p, "\x0d\x0a");
/* prepare the expected server accept response */
key_b64[39] = '\0'; /* enforce composed length below buf sizeof */
n = sprintf(buf, "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11", key_b64);
lws_SHA1((unsigned char *)buf, n, (unsigned char *)hash);
lws_b64_encode_string(hash, 20,
wsi->u.hdr.ah->initial_handshake_hash_base64,
sizeof(wsi->u.hdr.ah->initial_handshake_hash_base64));
return p;
}

View file

@ -88,6 +88,7 @@ static const char *set[] = {
"proxy ",
"x-real-ip:",
"http/1.0 ",
"", /* not matchable */

File diff suppressed because it is too large Load diff

View file

@ -219,6 +219,9 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
if (wsi->mode == LWSCM_HTTP_SERVING)
context->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_HTTP,
wsi->user_space, NULL, 0);
if (wsi->mode == LWSCM_HTTP_CLIENT)
context->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_CLIENT_HTTP,
wsi->user_space, NULL, 0);
/*
* are his extensions okay with him closing? Eg he might be a mux

View file

@ -349,6 +349,10 @@ enum lws_callback_reasons {
LWS_CALLBACK_CGI_TERMINATED = 41,
LWS_CALLBACK_CGI_STDIN_DATA = 42,
LWS_CALLBACK_CGI_STDIN_COMPLETED = 43,
LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP = 44,
LWS_CALLBACK_CLOSED_CLIENT_HTTP = 45,
LWS_CALLBACK_RECEIVE_CLIENT_HTTP = 46,
LWS_CALLBACK_COMPLETED_CLIENT_HTTP = 47,
/****** add new things just above ---^ ******/
@ -610,6 +614,7 @@ enum lws_token_indexes {
WSI_TOKEN_HTTP_URI_ARGS = 76,
WSI_TOKEN_PROXY = 77,
WSI_TOKEN_HTTP_X_REAL_IP = 78,
WSI_TOKEN_HTTP1_0 = 79,
/****** add new things just above ---^ ******/
@ -621,6 +626,7 @@ enum lws_token_indexes {
_WSI_TOKEN_CLIENT_URI,
_WSI_TOKEN_CLIENT_HOST,
_WSI_TOKEN_CLIENT_ORIGIN,
_WSI_TOKEN_CLIENT_METHOD,
/* always last real token index*/
WSI_TOKEN_COUNT,
@ -1393,6 +1399,8 @@ struct lws_context_creation_info {
* @ietf_version_or_minus_one: currently leave at 0 or -1
* @userdata: if non-NULL, use this as wsi user_data instead of malloc it
* @client_exts: array of extensions that may be used on connection
* @method: if non-NULL, do this http method instead of ws[s] upgrade.
* use "GET" to be a simple http client connection
*/
struct lws_client_connect_info {
@ -1407,6 +1415,7 @@ struct lws_client_connect_info {
int ietf_version_or_minus_one;
void *userdata;
const struct lws_extension *client_exts;
const char *method;
/* Add new things just above here ---^
* This is part of the ABI, don't needlessly break compatibility

View file

@ -349,6 +349,7 @@ enum lws_connection_states {
LWSS_HTTP_BODY,
LWSS_DEAD_SOCKET,
LWSS_ESTABLISHED,
LWSS_CLIENT_HTTP_ESTABLISHED,
LWSS_CLIENT_UNCONNECTED,
LWSS_RETURNED_CLOSE_ALREADY,
LWSS_AWAITING_CLOSE_ACK,
@ -410,7 +411,9 @@ enum lws_rx_parse_state {
enum connection_mode {
LWSCM_HTTP_SERVING,
LWSCM_HTTP_CLIENT, /* we are client to someone else's server */
LWSCM_HTTP_SERVING_ACCEPTED, /* actual HTTP service going on */
LWSCM_HTTP_CLIENT_ACCEPTED, /* actual HTTP service going on */
LWSCM_PRE_WS_SERVING_ACCEPT,
LWSCM_WS_SERVING,
@ -787,6 +790,18 @@ enum uri_esc_states {
* used interchangeably to access the same data
*/
#ifndef LWS_NO_CLIENT
struct client_info_stash {
char address[256];
char path[1024];
char host[256];
char origin[256];
char protocol[256];
char method[16];
};
#endif
struct _lws_header_related {
/* MUST be first in struct */
struct allocated_headers *ah;
@ -976,17 +991,6 @@ struct _lws_http2_related {
#endif
#ifndef LWS_NO_CLIENT
struct client_info_stash {
char address[256];
char path[1024];
char host[256];
char origin[256];
char protocol[256];
};
#endif
struct _lws_websocket_related {
/* cheapest way to deal with ah overlap with ws union transition */
struct _lws_header_related hdr;
@ -1112,6 +1116,9 @@ struct lws {
unsigned int socket_is_permanently_unusable:1;
unsigned int rxflow_change_to:2;
unsigned int more_rx_waiting:1; /* has to live here since ah may stick to end */
#ifndef LWS_NO_CLIENT
unsigned int do_ws:1; /* whether we are doing http or ws flow */
#endif
#ifndef LWS_NO_EXTENSIONS
unsigned int extension_data_pending:1;
#endif

View file

@ -602,9 +602,13 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int t
switch (wsi->mode) {
case LWSCM_HTTP_SERVING:
case LWSCM_HTTP_CLIENT:
case LWSCM_HTTP_SERVING_ACCEPTED:
case LWSCM_SERVER_LISTENER:
case LWSCM_SSL_ACK_PENDING:
if (wsi->state == LWSS_CLIENT_HTTP_ESTABLISHED) {
goto handled;
}
n = lws_server_socket_service(context, wsi, pollfd);
if (n) /* closed by above */
return 1;
@ -616,6 +620,7 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int t
case LWSCM_WS_SERVING:
case LWSCM_WS_CLIENT:
case LWSCM_HTTP2_SERVING:
case LWSCM_HTTP_CLIENT_ACCEPTED:
/* 1: something requested a callback when it was OK to write */
@ -739,6 +744,7 @@ read:
eff_buf.token = (char *)pt->serv_buf;
}
/*
* give any active extensions a chance to munge the buffer
* before parse. We pass in a pointer to an lws_tokens struct
@ -751,6 +757,34 @@ read:
* used then so it is efficient.
*/
drain:
if (wsi->mode == LWSCM_HTTP_CLIENT_ACCEPTED) {
lwsl_notice("%s: calling LWS_CALLBACK_RECEIVE_CLIENT_HTTP, "
"rem %d len %d\n", __func__,
wsi->u.http.content_remain, eff_buf.token_len);
if (wsi->u.http.content_remain < eff_buf.token_len)
n = wsi->u.http.content_remain;
else
n = eff_buf.token_len;
if (user_callback_handle_rxflow(wsi->protocol->callback,
wsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP,
wsi->user_space, (void *)eff_buf.token,
eff_buf.token_len))
goto close_and_handled;
wsi->u.http.content_remain -= n;
if (wsi->u.http.content_remain)
goto handled;
lwsl_notice("%s: client http receved all content\n",
__func__);
if (user_callback_handle_rxflow(wsi->protocol->callback,
wsi, LWS_CALLBACK_COMPLETED_CLIENT_HTTP,
wsi->user_space, NULL, 0))
goto close_and_handled;
if (wsi->u.http.connection_type == HTTP_CONNECTION_CLOSE)
goto close_and_handled;
goto handled;
}
do {
more = 0;

View file

@ -133,6 +133,22 @@ protocol supported, or the specific protocol ordinal
This function creates a connection to a remote server
</blockquote>
<hr>
<h2>lws_http_transaction_completed_client - wait for new http transaction or close</h2>
<i>int LWS_WARN_UNUSED_RESULT</i>
<b>lws_http_transaction_completed_client</b>
(<i>struct lws *</i> <b>wsi</b>)
<h3>Arguments</h3>
<dl>
<dt><b>wsi</b>
<dd>websocket connection
</dl>
<h3>Description</h3>
<blockquote>
Returns 1 if the HTTP connection must close now
Returns 0 and resets connection to wait for new HTTP header /
transaction if possible
</blockquote>
<hr>
<h2>lws_get_library_version - </h2>
<i>const char *</i>
<b>lws_get_library_version</b>
@ -1711,6 +1727,7 @@ Otherwise a default timeout is used.
&nbsp; &nbsp; <i>int</i> <b>ietf_version_or_minus_one</b>;<br>
&nbsp; &nbsp; <i>void *</i> <b>userdata</b>;<br>
&nbsp; &nbsp; <i>const struct lws_extension *</i> <b>client_exts</b>;<br>
&nbsp; &nbsp; <i>const char *</i> <b>method</b>;<br>
};<br>
<h3>Members</h3>
<dl>
@ -1736,6 +1753,9 @@ Otherwise a default timeout is used.
<dd>if non-NULL, use this as wsi user_data instead of malloc it
<dt><b>client_exts</b>
<dd>array of extensions that may be used on connection
<dt><b>method</b>
<dd>if non-NULL, do this http method instead of ws[s] upgrade.
use "GET" to be a simple http client connection
</dl>
<hr>
<h2>lws_close_reason - Set reason and aux data to send with Close packet If you are going to return nonzero from the callback requesting the connection to close, you can optionally call this to set the reason the peer will be told if possible.</h2>

View file

@ -74,6 +74,8 @@ static int
callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len)
{
char *buf = (char *)in;
switch (reason) {
case LWS_CALLBACK_CLIENT_ESTABLISHED:
@ -114,6 +116,16 @@ callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
return 1;
break;
case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
while (len--)
putchar(*buf++);
break;
case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
wsi_dumb = NULL;
force_exit = 1;
break;
default:
break;
}
@ -269,7 +281,7 @@ static int ratelimit_connects(unsigned int *last, unsigned int secs)
int main(int argc, char **argv)
{
int n = 0, ret = 0, port = 7681, use_ssl = 0, ietf_version = -1;
unsigned int rl_dumb = 0, rl_mirror = 0;
unsigned int rl_dumb = 0, rl_mirror = 0, do_ws = 1;
struct lws_context_creation_info info;
struct lws_client_connect_info i;
struct lws_context *context;
@ -362,6 +374,14 @@ int main(int argc, char **argv)
i.origin = i.address;
i.ietf_version_or_minus_one = ietf_version;
i.client_exts = exts;
if (!strcmp(prot, "http") || !strcmp(prot, "https")) {
lwsl_notice("using %s mode (non-ws)\n", prot);
i.method = "GET";
do_ws = 0;
} else
lwsl_notice("using %s mode (ws)\n", prot);
/*
* sit there servicing the websocket context to handle incoming
* packets, and drawing random circles on the mirror protocol websocket
@ -374,17 +394,23 @@ int main(int argc, char **argv)
while (!force_exit) {
if (!wsi_dumb && ratelimit_connects(&rl_dumb, 2u)) {
lwsl_notice("dumb: connecting\n");
i.protocol = protocols[PROTOCOL_DUMB_INCREMENT].name;
wsi_dumb = lws_client_connect_via_info(&i);
}
if (do_ws) {
if (!wsi_dumb && ratelimit_connects(&rl_dumb, 2u)) {
lwsl_notice("dumb: connecting\n");
i.protocol = protocols[PROTOCOL_DUMB_INCREMENT].name;
wsi_dumb = lws_client_connect_via_info(&i);
}
if (!wsi_mirror && ratelimit_connects(&rl_mirror, 2u)) {
lwsl_notice("mirror: connecting\n");
i.protocol = protocols[PROTOCOL_LWS_MIRROR].name;
wsi_mirror = lws_client_connect_via_info(&i);
}
if (!wsi_mirror && ratelimit_connects(&rl_mirror, 2u)) {
lwsl_notice("mirror: connecting\n");
i.protocol = protocols[PROTOCOL_LWS_MIRROR].name;
wsi_mirror = lws_client_connect_via_info(&i);
}
} else
if (!wsi_dumb && ratelimit_connects(&rl_dumb, 2u)) {
lwsl_notice("http: connecting\n");
wsi_dumb = lws_client_connect_via_info(&i);
}
lws_service(context, 500);
}