mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-09 00:00:04 +01:00
http2 alpn npn pollout
This adds npn / alpn support if your openssl can handle it. Then, browsers that understand alpn will by default negotiate http/1.1 and work as normal. Clients that understand http2.0 can negotiate h2-14 and use the basic but working http2.0 support automatically Signed-off-by: Andy Green <andy.green@linaro.org>
This commit is contained in:
parent
b21122994c
commit
7df53c5550
10 changed files with 300 additions and 26 deletions
|
@ -493,8 +493,14 @@ if (LWS_WITH_SSL)
|
|||
|
||||
list(APPEND LIB_LIST "${LWS_CYASSL_LIB}")
|
||||
else()
|
||||
if (OPENSSL_ROOT_DIR)
|
||||
set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/include")
|
||||
set(OPENSSL_LIBRARIES "${OPENSSL_ROOT_DIR}/lib/libssl.so" "${OPENSSL_ROOT_DIR}/lib/libcrypto.so")
|
||||
else(OPENSSL_ROOT_DIR)
|
||||
|
||||
# TODO: Add support for STATIC also.
|
||||
find_package(OpenSSL REQUIRED)
|
||||
endif(OPENSSL_ROOT_DIR)
|
||||
|
||||
message("OpenSSL include dir: ${OPENSSL_INCLUDE_DIR}")
|
||||
message("OpenSSL libraries: ${OPENSSL_LIBRARIES}")
|
||||
|
|
32
README.build
32
README.build
|
@ -66,7 +66,14 @@ Building on Unix:
|
|||
access to ALPN support only in newer OpenSSL versions) the nice way to
|
||||
express that in one cmake command is eg,
|
||||
|
||||
-DOPENSSL_ROOT_DIR=/usr/local/ssl
|
||||
cmake .. -DOPENSSL_ROOT_DIR=/usr/local/ssl \
|
||||
-DCMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE=/usr/local/ssl \
|
||||
-DLWS_WITH_HTTP2=1
|
||||
|
||||
When you run the test apps using non-distro SSL, you have to force them
|
||||
to use your libs, not the distro ones
|
||||
|
||||
LD_LIBRARY_PATH=/usr/local/ssl/lib libwebsockets-test-server --ssl
|
||||
|
||||
4. Finally you can build using the generated Makefile:
|
||||
|
||||
|
@ -150,6 +157,29 @@ cmake .. -DLWS_USE_CYASSL=1 \
|
|||
|
||||
NOTE: On windows use the .lib file extension for LWS_CYASSL_LIB instead.
|
||||
|
||||
|
||||
Reproducing HTTP2.0 tests
|
||||
-------------------------
|
||||
|
||||
You must have built and be running lws against a version of openssl that has
|
||||
ALPN / NPN. Most distros still have older versions. You'll know it's right by
|
||||
seeing
|
||||
|
||||
lwsts[4752]: Compiled with OpenSSL support
|
||||
lwsts[4752]: Using SSL mode
|
||||
lwsts[4752]: HTTP2 / ALPN enabled
|
||||
|
||||
at lws startup.
|
||||
|
||||
For non-SSL HTTP2.0 upgrade
|
||||
|
||||
nghttp -nvasu http://localhost:7681/test.htm
|
||||
|
||||
For SSL / ALPN HTTP2.0 upgrade
|
||||
|
||||
nghttp -nvas https://localhost:7681/test.html
|
||||
|
||||
|
||||
Cross compiling
|
||||
---------------
|
||||
To enable cross compiling libwebsockets using CMake you need to create
|
||||
|
|
50
lib/http2.c
50
lib/http2.c
|
@ -77,8 +77,11 @@ lws_create_server_child_wsi(struct libwebsocket_context *context, struct libwebs
|
|||
|
||||
wsi->state = WSI_STATE_HTTP2_ESTABLISHED;
|
||||
wsi->mode = parent_wsi->mode;
|
||||
|
||||
wsi->protocol = &context->protocols[0];
|
||||
libwebsocket_ensure_user_space(wsi);
|
||||
|
||||
lwsl_info("%s: %p new child %p, sid %d\n", __func__, parent_wsi, wsi, sid);
|
||||
lwsl_info("%s: %p new child %p, sid %d, user_space=%p\n", __func__, parent_wsi, wsi, sid, wsi->user_space);
|
||||
|
||||
return wsi;
|
||||
}
|
||||
|
@ -189,7 +192,7 @@ lws_http2_parser(struct libwebsocket_context *context,
|
|||
return 1;
|
||||
|
||||
if (!https_client_preface[wsi->u.http2.count]) {
|
||||
lwsl_err("http2: %p: established\n", wsi);
|
||||
lwsl_info("http2: %p: established\n", wsi);
|
||||
wsi->state = WSI_STATE_HTTP2_ESTABLISHED_PRE_SETTINGS;
|
||||
wsi->u.http2.count = 0;
|
||||
|
||||
|
@ -224,6 +227,16 @@ lws_http2_parser(struct libwebsocket_context *context,
|
|||
if (lws_hpack_interpret(context, wsi->u.http2.stream_wsi, c))
|
||||
return 1;
|
||||
break;
|
||||
case LWS_HTTP2_FRAME_TYPE_GOAWAY:
|
||||
if (wsi->u.http2.count >= 5 && wsi->u.http2.count <= 8) {
|
||||
wsi->u.http2.hpack_e_dep <<= 8;
|
||||
wsi->u.http2.hpack_e_dep |= c;
|
||||
if (wsi->u.http2.count == 8) {
|
||||
lwsl_info("goaway err 0x%x\n", wsi->u.http2.hpack_e_dep);
|
||||
}
|
||||
}
|
||||
wsi->u.http2.GOING_AWAY = 1;
|
||||
break;
|
||||
}
|
||||
if (wsi->u.http2.count != wsi->u.http2.length)
|
||||
break;
|
||||
|
@ -241,7 +254,7 @@ lws_http2_parser(struct libwebsocket_context *context,
|
|||
switch (wsi->u.http2.type) {
|
||||
case LWS_HTTP2_FRAME_TYPE_HEADERS:
|
||||
/* service the http request itself */
|
||||
lwsl_info("servicing initial http request\n");
|
||||
lwsl_info("servicing initial http request, wsi=%p, stream wsi=%p\n", wsi, wsi->u.http2.stream_wsi);
|
||||
n = lws_http_action(context, wsi->u.http2.stream_wsi);
|
||||
lwsl_info(" action result %d\n", n);
|
||||
break;
|
||||
|
@ -378,6 +391,11 @@ int lws_http2_do_pps_send(struct libwebsocket_context *context, struct libwebsoc
|
|||
|
||||
wsi->u.http.fd = LWS_INVALID_FILE;
|
||||
|
||||
if (lws_is_ssl(lws_http2_get_network_wsi(wsi))) {
|
||||
lwsl_info("skipping nonexistant ssl upgrade headers\n");
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* we need to treat the headers from this upgrade
|
||||
* as the first job. These need to get
|
||||
|
@ -405,4 +423,28 @@ int lws_http2_do_pps_send(struct libwebsocket_context *context, struct libwebsoc
|
|||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
struct libwebsocket * lws_http2_get_nth_child(struct libwebsocket *wsi, int n)
|
||||
{
|
||||
do {
|
||||
wsi = wsi->u.http2.next_child_wsi;
|
||||
if (!wsi)
|
||||
return NULL;
|
||||
} while (n--);
|
||||
|
||||
return wsi;
|
||||
}
|
||||
#if 0
|
||||
struct libwebsocket * lws_http2_get_next_waiting_child(struct libwebsocket *wsi)
|
||||
{
|
||||
struct libwebsocket *wsi_child = wsi, *wsi2;
|
||||
|
||||
do {
|
||||
wsi2 = lws_http2_get_nth_child(wsi_child, wsi_child->round_robin_POLLOUT);
|
||||
if (wsi2 == NULL) {
|
||||
wsi_child->round_robin = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -702,6 +702,7 @@ libwebsocket_get_reserved_bits(struct libwebsocket *wsi)
|
|||
int
|
||||
libwebsocket_ensure_user_space(struct libwebsocket *wsi)
|
||||
{
|
||||
lwsl_info("%s: %p protocol %p\n", __func__, wsi, wsi->protocol);
|
||||
if (!wsi->protocol)
|
||||
return 1;
|
||||
|
||||
|
@ -716,7 +717,8 @@ libwebsocket_ensure_user_space(struct libwebsocket *wsi)
|
|||
}
|
||||
memset(wsi->user_space, 0,
|
||||
wsi->protocol->per_session_data_size);
|
||||
}
|
||||
} else
|
||||
lwsl_info("%s: %p protocol pss %u, user_space=%d\n", __func__, wsi, wsi->protocol->per_session_data_size, wsi->user_space);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
34
lib/pollfd.c
34
lib/pollfd.c
|
@ -193,6 +193,40 @@ LWS_VISIBLE int
|
|||
libwebsocket_callback_on_writable(struct libwebsocket_context *context,
|
||||
struct libwebsocket *wsi)
|
||||
{
|
||||
#ifdef LWS_USE_HTTP2
|
||||
struct libwebsocket *network_wsi, *wsi2;
|
||||
int already;
|
||||
|
||||
lwsl_info("%s: %p\n", __func__, wsi);
|
||||
|
||||
if (wsi->mode != LWS_CONNMODE_HTTP2_SERVING)
|
||||
goto network_sock;
|
||||
|
||||
if (wsi->u.http2.requested_POLLOUT) {
|
||||
lwsl_info("already pending writable\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
network_wsi = lws_http2_get_network_wsi(wsi);
|
||||
already = network_wsi->u.http2.requested_POLLOUT;
|
||||
|
||||
/* mark everybody above him as requesting pollout */
|
||||
|
||||
wsi2 = wsi;
|
||||
while (wsi2) {
|
||||
wsi2->u.http2.requested_POLLOUT = 1;
|
||||
lwsl_info("mark %p pending writable\n", wsi2);
|
||||
wsi2 = wsi2->u.http2.parent_wsi;
|
||||
}
|
||||
|
||||
/* for network action, act only on the network wsi */
|
||||
|
||||
wsi = network_wsi;
|
||||
if (already)
|
||||
return 1;
|
||||
network_sock:
|
||||
#endif
|
||||
|
||||
if (lws_ext_callback_for_each_active(wsi,
|
||||
LWS_EXT_CALLBACK_REQUEST_ON_WRITEABLE, NULL, 0))
|
||||
return 1;
|
||||
|
|
|
@ -697,9 +697,14 @@ struct _lws_http2_related {
|
|||
unsigned char frame_state;
|
||||
unsigned char padding;
|
||||
|
||||
unsigned short round_robin_POLLOUT;
|
||||
unsigned short count_POLLOUT_children;
|
||||
|
||||
unsigned int END_STREAM:1;
|
||||
unsigned int END_HEADERS:1;
|
||||
unsigned int send_END_STREAM:1;
|
||||
unsigned int GOING_AWAY;
|
||||
unsigned int requested_POLLOUT:1;
|
||||
|
||||
/* hpack */
|
||||
enum http2_hpack_state hpack;
|
||||
|
@ -720,7 +725,7 @@ struct _lws_http2_related {
|
|||
unsigned char one_setting[LWS_HTTP2_SETTINGS_LENGTH];
|
||||
};
|
||||
|
||||
#define HTTP2_IS_TOPLEVEL_WSI(wsi) (!wsi->parent_wsi)
|
||||
#define HTTP2_IS_TOPLEVEL_WSI(wsi) (!wsi->u.http2.parent_wsi)
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -824,6 +829,7 @@ struct libwebsocket {
|
|||
BIO *client_bio;
|
||||
unsigned int use_ssl:2;
|
||||
unsigned int buffered_reads_pending:1;
|
||||
unsigned int upgraded:1;
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
|
@ -954,6 +960,7 @@ user_callback_handle_rxflow(callback_function,
|
|||
void *in, size_t len);
|
||||
#ifdef LWS_USE_HTTP2
|
||||
LWS_EXTERN struct libwebsocket *lws_http2_get_network_wsi(struct libwebsocket *wsi);
|
||||
struct libwebsocket * lws_http2_get_nth_child(struct libwebsocket *wsi, int n);
|
||||
LWS_EXTERN int
|
||||
lws_http2_interpret_settings_payload(struct http2_settings *settings, unsigned char *buf, int len);
|
||||
LWS_EXTERN void lws_http2_init(struct http2_settings *settings);
|
||||
|
@ -989,6 +996,10 @@ lws_add_http2_header_status(struct libwebsocket_context *context,
|
|||
unsigned int code,
|
||||
unsigned char **p,
|
||||
unsigned char *end);
|
||||
LWS_EXTERN
|
||||
void lws_http2_configure_if_upgraded(struct libwebsocket *wsi);
|
||||
#else
|
||||
#define lws_http2_configure_if_upgraded(x)
|
||||
#endif
|
||||
|
||||
LWS_EXTERN int
|
||||
|
|
|
@ -21,12 +21,38 @@
|
|||
|
||||
#include "private-libwebsockets.h"
|
||||
|
||||
static int
|
||||
lws_calllback_as_writeable(struct libwebsocket_context *context,
|
||||
struct libwebsocket *wsi)
|
||||
{
|
||||
int n;
|
||||
|
||||
switch (wsi->mode) {
|
||||
case LWS_CONNMODE_WS_CLIENT:
|
||||
n = LWS_CALLBACK_CLIENT_WRITEABLE;
|
||||
break;
|
||||
case LWS_CONNMODE_WS_SERVING:
|
||||
n = LWS_CALLBACK_SERVER_WRITEABLE;
|
||||
break;
|
||||
default:
|
||||
n = LWS_CALLBACK_HTTP_WRITEABLE;
|
||||
break;
|
||||
}
|
||||
lwsl_info("%s: %p (user=%p)\n", __func__, wsi, wsi->user_space);
|
||||
return user_callback_handle_rxflow(wsi->protocol->callback, context,
|
||||
wsi, (enum libwebsocket_callback_reasons) n,
|
||||
wsi->user_space, NULL, 0);
|
||||
}
|
||||
|
||||
int
|
||||
lws_handle_POLLOUT_event(struct libwebsocket_context *context,
|
||||
struct libwebsocket *wsi, struct libwebsocket_pollfd *pollfd)
|
||||
{
|
||||
int n;
|
||||
struct lws_tokens eff_buf;
|
||||
#ifdef LWS_USE_HTTP2
|
||||
struct libwebsocket *wsi2;
|
||||
#endif
|
||||
int ret;
|
||||
int m;
|
||||
int handled = 0;
|
||||
|
@ -186,14 +212,56 @@ user_service:
|
|||
}
|
||||
|
||||
notify_action:
|
||||
if (wsi->mode == LWS_CONNMODE_WS_CLIENT)
|
||||
n = LWS_CALLBACK_CLIENT_WRITEABLE;
|
||||
else
|
||||
n = LWS_CALLBACK_SERVER_WRITEABLE;
|
||||
#ifdef LWS_USE_HTTP2
|
||||
/*
|
||||
* we are the 'network wsi' for potentially many muxed child wsi with
|
||||
* no network connection of their own, who have to use us for all their
|
||||
* network actions. So we use a round-robin scheme to share out the
|
||||
* POLLOUT notifications to our children.
|
||||
*
|
||||
* But because any child could exhaust the socket's ability to take
|
||||
* writes, we can only let one child get notified each time.
|
||||
*
|
||||
* In addition children may be closed / deleted / added between POLLOUT
|
||||
* notifications, so we can't hold pointers
|
||||
*/
|
||||
|
||||
if (wsi->mode != LWS_CONNMODE_HTTP2_SERVING) {
|
||||
lwsl_info("%s: non http2\n", __func__);
|
||||
goto notify;
|
||||
}
|
||||
|
||||
return user_callback_handle_rxflow(wsi->protocol->callback, context,
|
||||
wsi, (enum libwebsocket_callback_reasons) n,
|
||||
wsi->user_space, NULL, 0);
|
||||
wsi->u.http2.requested_POLLOUT = 0;
|
||||
if (!wsi->u.http2.initialized) {
|
||||
lwsl_info("pollout on uninitialized http2 conn\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
lwsl_info("%s: doing children\n", __func__);
|
||||
|
||||
wsi2 = wsi;
|
||||
do {
|
||||
wsi2 = wsi2->u.http2.next_child_wsi;
|
||||
lwsl_info("%s: child %p\n", __func__, wsi2);
|
||||
if (!wsi2)
|
||||
continue;
|
||||
if (!wsi2->u.http2.requested_POLLOUT)
|
||||
continue;
|
||||
wsi2->u.http2.requested_POLLOUT = 0;
|
||||
if (lws_calllback_as_writeable(context, wsi2)) {
|
||||
lwsl_debug("Closing POLLOUT child\n");
|
||||
libwebsocket_close_and_free_session(context, wsi2,
|
||||
LWS_CLOSE_STATUS_NOSTATUS);
|
||||
}
|
||||
wsi2 = wsi;
|
||||
} while (wsi2 != NULL && !lws_send_pipe_choked(wsi));
|
||||
|
||||
lwsl_info("%s: completed\n", __func__);
|
||||
|
||||
return 0;
|
||||
notify:
|
||||
#endif
|
||||
return lws_calllback_as_writeable(context, wsi);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -53,14 +53,35 @@
|
|||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||
static int alpn_select_proto_cb(SSL* ssl,
|
||||
const unsigned char **out,
|
||||
unsigned char *outlen,
|
||||
const unsigned char *in, unsigned int inlen,
|
||||
void *arg)
|
||||
|
||||
struct alpn_ctx {
|
||||
unsigned char *data;
|
||||
unsigned short len;
|
||||
};
|
||||
|
||||
static int npn_cb(SSL *s, const unsigned char **data, unsigned int *len, void *arg)
|
||||
{
|
||||
lwsl_err((char *)in);
|
||||
return SSL_TLSEXT_ERR_OK; /* SSL_TLSEXT_ERR_NOACK */
|
||||
struct alpn_ctx *alpn_ctx = arg;
|
||||
|
||||
lwsl_info("%s\n", __func__);
|
||||
*data = alpn_ctx->data;
|
||||
*len = alpn_ctx->len;
|
||||
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
|
||||
static int alpn_cb(SSL *s, const unsigned char **out,
|
||||
unsigned char *outlen, const unsigned char *in,
|
||||
unsigned int inlen, void *arg)
|
||||
{
|
||||
struct alpn_ctx *alpn_ctx = arg;
|
||||
|
||||
if (SSL_select_next_proto((unsigned char **)out, outlen,
|
||||
alpn_ctx->data, alpn_ctx->len, in, inlen) !=
|
||||
OPENSSL_NPN_NEGOTIATED)
|
||||
return SSL_TLSEXT_ERR_NOACK;
|
||||
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -68,13 +89,68 @@ LWS_VISIBLE void
|
|||
lws_context_init_http2_ssl(struct libwebsocket_context *context)
|
||||
{
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||
static struct alpn_ctx protos = { (unsigned char *)
|
||||
"\x05h2-14"
|
||||
"\x08http/1.1",
|
||||
6 + 9 };
|
||||
|
||||
SSL_CTX_set_next_protos_advertised_cb(context->ssl_ctx, npn_cb, &protos);
|
||||
|
||||
// ALPN selection callback
|
||||
SSL_CTX_set_alpn_select_cb(context->ssl_ctx, alpn_select_proto_cb, NULL);
|
||||
SSL_CTX_set_alpn_select_cb(context->ssl_ctx, alpn_cb, &protos);
|
||||
lwsl_notice(" HTTP2 / ALPN enabled\n");
|
||||
#else
|
||||
lwsl_notice(" HTTP2 / ALPN configured but not supported by OpenSSL version 0x%x\n", OPENSSL_VERSION_NUMBER);
|
||||
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||
}
|
||||
|
||||
void lws_http2_configure_if_upgraded(struct libwebsocket *wsi)
|
||||
{
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||
struct allocated_headers *ah;
|
||||
const unsigned char *name;
|
||||
unsigned len;
|
||||
const char *method = "alpn";
|
||||
|
||||
SSL_get0_alpn_selected(wsi->ssl, &name, &len);
|
||||
|
||||
if (!len) {
|
||||
SSL_get0_next_proto_negotiated(wsi->ssl, &name, &len);
|
||||
method = "npn";
|
||||
}
|
||||
|
||||
if (len) {
|
||||
lwsl_info("negotiated %s using %s\n", name, method);
|
||||
wsi->use_ssl = 1;
|
||||
if (strncmp((char *)name, "http/1.1", 8) == 0)
|
||||
return;
|
||||
|
||||
/* http2 */
|
||||
|
||||
wsi->mode = LWS_CONNMODE_HTTP2_SERVING;
|
||||
wsi->state = WSI_STATE_HTTP2_AWAIT_CLIENT_PREFACE;
|
||||
|
||||
/* adopt the header info */
|
||||
|
||||
ah = wsi->u.hdr.ah;
|
||||
|
||||
wsi->mode = LWS_CONNMODE_HTTP2_SERVING;
|
||||
|
||||
/* union transition */
|
||||
memset(&wsi->u, 0, sizeof(wsi->u));
|
||||
|
||||
/* http2 union member has http union struct at start */
|
||||
wsi->u.http.ah = ah;
|
||||
|
||||
lws_http2_init(&wsi->u.http2.peer_settings);
|
||||
lws_http2_init(&wsi->u.http2.my_settings);
|
||||
|
||||
/* HTTP2 union */
|
||||
|
||||
} else
|
||||
lwsl_info("no npn/alpn upgrade\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
|
@ -593,6 +593,8 @@ accepted:
|
|||
|
||||
wsi->mode = LWS_CONNMODE_HTTP_SERVING;
|
||||
|
||||
lws_http2_configure_if_upgraded(wsi);
|
||||
|
||||
lwsl_debug("accepted new SSL conn\n");
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -345,8 +345,9 @@ static int callback_http(struct libwebsocket_context *context,
|
|||
/*
|
||||
* we can send more of whatever it is we were sending
|
||||
*/
|
||||
|
||||
lwsl_info("LWS_CALLBACK_HTTP_WRITEABLE\n");
|
||||
do {
|
||||
lwsl_info("a\n");
|
||||
n = read(pss->fd, buffer + LWS_SEND_BUFFER_PRE_PADDING,
|
||||
sizeof (buffer) - LWS_SEND_BUFFER_PRE_PADDING);
|
||||
/* problem reading, close conn */
|
||||
|
@ -355,7 +356,7 @@ static int callback_http(struct libwebsocket_context *context,
|
|||
/* sent it all, close conn */
|
||||
if (n == 0)
|
||||
goto flush_bail;
|
||||
|
||||
lwsl_info("b\n");
|
||||
/*
|
||||
* To support HTTP2, must take care about preamble space
|
||||
* and identify when we send the last frame
|
||||
|
@ -366,13 +367,14 @@ static int callback_http(struct libwebsocket_context *context,
|
|||
if (m < 0)
|
||||
/* write failed, close conn */
|
||||
goto bail;
|
||||
lwsl_info("c\n");
|
||||
/*
|
||||
* http2 won't do this
|
||||
*/
|
||||
if (m != n)
|
||||
/* partial write, adjust */
|
||||
lseek(pss->fd, m - n, SEEK_CUR);
|
||||
|
||||
lwsl_info("d\n");
|
||||
if (m) /* while still active, extend timeout */
|
||||
libwebsocket_set_timeout(wsi,
|
||||
PENDING_TIMEOUT_HTTP_CONTENT, 5);
|
||||
|
@ -382,8 +384,9 @@ static int callback_http(struct libwebsocket_context *context,
|
|||
break;
|
||||
|
||||
} while (!lws_send_pipe_choked(wsi));
|
||||
|
||||
lwsl_info("e\n");
|
||||
libwebsocket_callback_on_writable(context, wsi);
|
||||
lwsl_info("f\n");
|
||||
break;
|
||||
flush_bail:
|
||||
/* true if still partial pending */
|
||||
|
|
Loading…
Add table
Reference in a new issue