/*
* libwebsockets - small server side websockets and web server implementation
*
- * Copyright (C) 2010-2014 Andy Green <andy@warmcat.com>
+ * Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
wsi->user_space && !wsi->user_space_externally_allocated)
lws_free(wsi->user_space);
- lws_free2(wsi->rxflow_buffer);
- lws_free2(wsi->truncated_send_malloc);
- lws_free_header_table(wsi);
+ lws_free_set_NULL(wsi->rxflow_buffer);
+ lws_free_set_NULL(wsi->trunc_alloc);
+
+ if (wsi->u.hdr.ah) {
+ /* we're closing, losing some rx is OK */
+ wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen;
+ lws_header_table_detach(wsi);
+ }
+
+ wsi->context->count_wsi_allocated--;
+ lwsl_debug("%s: %p, remaining wsi %d\n", __func__, wsi,
+ wsi->context->count_wsi_allocated);
+
lws_free(wsi);
}
+static void
+lws_remove_from_timeout_list(struct lws *wsi)
+{
+ struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+
+ if (!wsi->timeout_list_prev)
+ return;
+
+ lws_pt_lock(pt);
+ if (wsi->timeout_list)
+ wsi->timeout_list->timeout_list_prev = wsi->timeout_list_prev;
+ *wsi->timeout_list_prev = wsi->timeout_list;
+
+ wsi->timeout_list_prev = NULL;
+ wsi->timeout_list = NULL;
+ lws_pt_unlock(pt);
+}
+
+/**
+ * lws_set_timeout() - marks the wsi as subject to a timeout
+ *
+ * You will not need this unless you are doing something special
+ *
+ * @wsi: Websocket connection instance
+ * @reason: timeout reason
+ * @secs: how many seconds
+ */
+
+LWS_VISIBLE void
+lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs)
+{
+ struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+ time_t now;
+
+ lws_pt_lock(pt);
+
+ time(&now);
+
+ if (!wsi->pending_timeout && reason) {
+ wsi->timeout_list = pt->timeout_list;
+ if (wsi->timeout_list)
+ wsi->timeout_list->timeout_list_prev = &wsi->timeout_list;
+ wsi->timeout_list_prev = &pt->timeout_list;
+ *wsi->timeout_list_prev = wsi;
+ }
+
+ wsi->pending_timeout_limit = now + secs;
+ wsi->pending_timeout = reason;
+
+ lws_pt_unlock(pt);
+
+ if (!reason)
+ lws_remove_from_timeout_list(wsi);
+}
+
void
lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
{
- struct lws_context *context = wsi->context;
- int n, m, ret, old_state;
+ struct lws_context *context;
+ struct lws_context_per_thread *pt;
+ int n, m, ret;
struct lws_tokens eff_buf;
- unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 2 +
- LWS_SEND_BUFFER_POST_PADDING];
if (!wsi)
return;
- old_state = wsi->state;
+ context = wsi->context;
+ pt = &context->pt[(int)wsi->tsi];
- if (wsi->mode == LWS_CONNMODE_HTTP_SERVING_ACCEPTED &&
+ if (wsi->mode == LWSCM_HTTP_SERVING_ACCEPTED &&
wsi->u.http.fd != LWS_INVALID_FILE) {
lwsl_debug("closing http file\n");
lws_plat_file_close(wsi, wsi->u.http.fd);
wsi->u.http.fd = LWS_INVALID_FILE;
- context->protocols[0].callback(context, wsi,
- LWS_CALLBACK_CLOSED_HTTP, wsi->user_space, NULL, 0);
+ context->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_HTTP,
+ wsi->user_space, NULL, 0);
}
if (wsi->socket_is_permanently_unusable ||
- reason == LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY)
+ reason == LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY ||
+ wsi->state == LWSS_SHUTDOWN)
goto just_kill_connection;
- switch (old_state) {
- case WSI_STATE_DEAD_SOCKET:
+ wsi->state_pre_close = wsi->state;
+
+ switch (wsi->state_pre_close) {
+ case LWSS_DEAD_SOCKET:
return;
/* we tried the polite way... */
- case WSI_STATE_AWAITING_CLOSE_ACK:
+ case LWSS_AWAITING_CLOSE_ACK:
goto just_kill_connection;
- case WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE:
- if (wsi->truncated_send_len) {
+ case LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE:
+ if (wsi->trunc_len) {
lws_callback_on_writable(wsi);
return;
}
- lwsl_info("wsi %p completed WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE\n", wsi);
+ lwsl_info("wsi %p completed LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE\n", wsi);
goto just_kill_connection;
default:
- if (wsi->truncated_send_len) {
- lwsl_info("wsi %p entering WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE\n", wsi);
- wsi->state = WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE;
+ if (wsi->trunc_len) {
+ lwsl_info("wsi %p entering LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE\n", wsi);
+ wsi->state = LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE;
lws_set_timeout(wsi, PENDING_FLUSH_STORED_SEND_BEFORE_CLOSE, 5);
return;
}
break;
}
- wsi->u.ws.close_reason = reason;
-
- if (wsi->mode == LWS_CONNMODE_WS_CLIENT_WAITING_CONNECT ||
- wsi->mode == LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE)
+ if (wsi->mode == LWSCM_WSCL_WAITING_CONNECT ||
+ wsi->mode == LWSCM_WSCL_ISSUE_HANDSHAKE)
goto just_kill_connection;
- if (wsi->mode == LWS_CONNMODE_HTTP_SERVING)
- context->protocols[0].callback(context, wsi,
- LWS_CALLBACK_CLOSED_HTTP, wsi->user_space, NULL, 0);
+ if (wsi->mode == LWSCM_HTTP_SERVING)
+ context->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_HTTP,
+ wsi->user_space, NULL, 0);
/*
* are his extensions okay with him closing? Eg he might be a mux
* parent and just his ch1 aspect is closing?
*/
- if (lws_ext_callback_for_each_active(wsi,
- LWS_EXT_CALLBACK_CHECK_OK_TO_REALLY_CLOSE, NULL, 0) > 0) {
+ if (lws_ext_cb_active(wsi,
+ LWS_EXT_CB_CHECK_OK_TO_REALLY_CLOSE, NULL, 0) > 0) {
lwsl_ext("extension vetoed close\n");
return;
}
/* show every extension the new incoming data */
- m = lws_ext_callback_for_each_active(wsi,
- LWS_EXT_CALLBACK_FLUSH_PENDING_TX, &eff_buf, 0);
+ m = lws_ext_cb_active(wsi,
+ LWS_EXT_CB_FLUSH_PENDING_TX, &eff_buf, 0);
if (m < 0) {
lwsl_ext("Extension reports fatal error\n");
goto just_kill_connection;
* add any necessary version-specific stuff. If the write fails,
* no worries we are closing anyway. If we didn't initiate this
* close, then our state has been changed to
- * WSI_STATE_RETURNED_CLOSE_ALREADY and we will skip this.
+ * LWSS_RETURNED_CLOSE_ALREADY and we will skip this.
*
* Likewise if it's a second call to close this connection after we
* sent the close indication to the peer already, we are in state
- * WSI_STATE_AWAITING_CLOSE_ACK and will skip doing this a second time.
+ * LWSS_AWAITING_CLOSE_ACK and will skip doing this a second time.
*/
- if (old_state == WSI_STATE_ESTABLISHED &&
- reason != LWS_CLOSE_STATUS_NOSTATUS &&
- reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY) {
-
+ if (wsi->state_pre_close == LWSS_ESTABLISHED &&
+ (wsi->u.ws.close_in_ping_buffer_len || /* already a reason */
+ (reason != LWS_CLOSE_STATUS_NOSTATUS &&
+ (reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY)))) {
lwsl_debug("sending close indication...\n");
- /* make valgrind happy */
- memset(buf, 0, sizeof(buf));
- n = lws_write(wsi, &buf[LWS_SEND_BUFFER_PRE_PADDING + 2],
- 0, LWS_WRITE_CLOSE);
+ /* if no prepared close reason, use 1000 and no aux data */
+ if (!wsi->u.ws.close_in_ping_buffer_len) {
+ wsi->u.ws.close_in_ping_buffer_len = 2;
+ wsi->u.ws.ping_payload_buf[LWS_PRE] =
+ (reason >> 16) & 0xff;
+ wsi->u.ws.ping_payload_buf[LWS_PRE + 1] =
+ reason & 0xff;
+ }
+
+ n = lws_write(wsi, &wsi->u.ws.ping_payload_buf[LWS_PRE],
+ wsi->u.ws.close_in_ping_buffer_len,
+ LWS_WRITE_CLOSE);
if (n >= 0) {
/*
* we have sent a nice protocol level indication we
* now wish to close, we should not send anything more
*/
- wsi->state = WSI_STATE_AWAITING_CLOSE_ACK;
+ wsi->state = LWSS_AWAITING_CLOSE_ACK;
/*
* ...and we should wait for a reply for a bit
just_kill_connection:
- lwsl_debug("close: just_kill_connection\n");
+#if LWS_POSIX
+ /*
+ * Testing with ab shows that we have to stage the socket close when
+ * the system is under stress... shutdown any further TX, change the
+ * state to one that won't emit anything more, and wait with a timeout
+ * for the POLLIN to show a zero-size rx before coming back and doing
+ * the actual close.
+ */
+ if (wsi->state != LWSS_SHUTDOWN &&
+ reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY &&
+ !wsi->socket_is_permanently_unusable) {
+ lwsl_info("%s: shutting down connection: %p\n", __func__, wsi);
+ n = shutdown(wsi->sock, SHUT_WR);
+ if (n)
+ lwsl_debug("closing: shutdown ret %d\n", LWS_ERRNO);
+
+ lws_change_pollfd(wsi, LWS_POLLOUT, LWS_POLLIN);
+ wsi->state = LWSS_SHUTDOWN;
+ lws_set_timeout(wsi, PENDING_TIMEOUT_SHUTDOWN_FLUSH,
+ context->timeout_secs);
+ return;
+ }
+#endif
+
+ lwsl_info("%s: real just_kill_connection: %p\n", __func__, wsi);
/*
* we won't be servicing or receiving anything further from this guy
* delete socket from the internal poll list if still present
*/
lws_ssl_remove_wsi_from_buffered_list(wsi);
+ lws_remove_from_timeout_list(wsi);
/* checking return redundant since we anyway close */
remove_wsi_socket_from_fds(wsi);
- wsi->state = WSI_STATE_DEAD_SOCKET;
+ wsi->state = LWSS_DEAD_SOCKET;
- lws_free2(wsi->rxflow_buffer);
- lws_free_header_table(wsi);
+ lws_free_set_NULL(wsi->rxflow_buffer);
- if ((old_state == WSI_STATE_ESTABLISHED ||
- wsi->mode == LWS_CONNMODE_WS_SERVING ||
- wsi->mode == LWS_CONNMODE_WS_CLIENT)) {
+ if (wsi->state_pre_close == LWSS_ESTABLISHED ||
+ wsi->mode == LWSCM_WS_SERVING ||
+ wsi->mode == LWSCM_WS_CLIENT) {
- lws_free2(wsi->u.ws.rx_user_buffer);
+ if (wsi->u.ws.rx_draining_ext) {
+ struct lws **w = &pt->rx_draining_ext_list;
- if (wsi->truncated_send_malloc)
- /* not going to be completed... nuke it */
- lws_free2(wsi->truncated_send_malloc);
+ wsi->u.ws.rx_draining_ext = 0;
+ /* remove us from context draining ext list */
+ while (*w) {
+ if (*w == wsi) {
+ *w = wsi->u.ws.rx_draining_ext_list;
+ break;
+ }
+ w = &((*w)->u.ws.rx_draining_ext_list);
+ }
+ wsi->u.ws.rx_draining_ext_list = NULL;
+ }
- if (wsi->u.ws.ping_payload_buf) {
- lws_free2(wsi->u.ws.ping_payload_buf);
- wsi->u.ws.ping_payload_alloc = 0;
- wsi->u.ws.ping_payload_len = 0;
- wsi->u.ws.ping_pending_flag = 0;
+ if (wsi->u.ws.tx_draining_ext) {
+ struct lws **w = &pt->tx_draining_ext_list;
+
+ wsi->u.ws.tx_draining_ext = 0;
+ /* remove us from context draining ext list */
+ while (*w) {
+ if (*w == wsi) {
+ *w = wsi->u.ws.tx_draining_ext_list;
+ break;
+ }
+ w = &((*w)->u.ws.tx_draining_ext_list);
+ }
+ wsi->u.ws.tx_draining_ext_list = NULL;
}
+ lws_free_set_NULL(wsi->u.ws.rx_ubuf);
+
+ if (wsi->trunc_alloc)
+ /* not going to be completed... nuke it */
+ lws_free_set_NULL(wsi->trunc_alloc);
+
+ wsi->u.ws.ping_payload_len = 0;
+ wsi->u.ws.ping_pending_flag = 0;
}
/* tell the user it's all over for this guy */
if (wsi->protocol && wsi->protocol->callback &&
- ((old_state == WSI_STATE_ESTABLISHED) ||
- (old_state == WSI_STATE_RETURNED_CLOSE_ALREADY) ||
- (old_state == WSI_STATE_AWAITING_CLOSE_ACK) ||
- (old_state == WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE))) {
+ ((wsi->state_pre_close == LWSS_ESTABLISHED) ||
+ (wsi->state_pre_close == LWSS_RETURNED_CLOSE_ALREADY) ||
+ (wsi->state_pre_close == LWSS_AWAITING_CLOSE_ACK) ||
+ (wsi->state_pre_close == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE) ||
+ (wsi->mode == LWSCM_WS_CLIENT && wsi->state_pre_close == LWSS_HTTP) ||
+ (wsi->mode == LWSCM_WS_SERVING && wsi->state_pre_close == LWSS_HTTP))) {
lwsl_debug("calling back CLOSED\n");
- wsi->protocol->callback(context, wsi, LWS_CALLBACK_CLOSED,
+ wsi->protocol->callback(wsi, LWS_CALLBACK_CLOSED,
wsi->user_space, NULL, 0);
- } else if (wsi->mode == LWS_CONNMODE_HTTP_SERVING_ACCEPTED) {
+ } else if (wsi->mode == LWSCM_HTTP_SERVING_ACCEPTED) {
lwsl_debug("calling back CLOSED_HTTP\n");
- context->protocols[0].callback(context, wsi,
- LWS_CALLBACK_CLOSED_HTTP, wsi->user_space, NULL, 0 );
- } else if (wsi->mode == LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY ||
- wsi->mode == LWS_CONNMODE_WS_CLIENT_WAITING_CONNECT) {
+ context->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_HTTP,
+ wsi->user_space, NULL, 0 );
+ } else if (wsi->mode == LWSCM_WSCL_WAITING_SERVER_REPLY ||
+ wsi->mode == LWSCM_WSCL_WAITING_CONNECT) {
lwsl_debug("Connection closed before server reply\n");
- context->protocols[0].callback(context, wsi,
+ context->protocols[0].callback(wsi,
LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
wsi->user_space, NULL, 0);
} else
lwsl_debug("not calling back closed mode=%d state=%d\n",
- wsi->mode, old_state);
+ wsi->mode, wsi->state_pre_close);
/* deallocate any active extension contexts */
- if (lws_ext_callback_for_each_active(wsi, LWS_EXT_CALLBACK_DESTROY,
- NULL, 0) < 0)
+ if (lws_ext_cb_active(wsi, LWS_EXT_CB_DESTROY, NULL, 0) < 0)
lwsl_warn("extension destruction failed\n");
-#ifndef LWS_NO_EXTENSIONS
- for (n = 0; n < wsi->count_active_extensions; n++)
- lws_free(wsi->active_extensions_user[n]);
-#endif
/*
* inform all extensions in case they tracked this guy out of band
* even though not active on him specifically
*/
- if (lws_ext_callback_for_each_extension_type(context, wsi,
- LWS_EXT_CALLBACK_DESTROY_ANY_WSI_CLOSING, NULL, 0) < 0)
+ if (lws_ext_cb_all_exts(context, wsi,
+ LWS_EXT_CB_DESTROY_ANY_WSI_CLOSING, NULL, 0) < 0)
lwsl_warn("ext destroy wsi failed\n");
+ wsi->socket_is_permanently_unusable = 1;
+
+#ifdef LWS_USE_LIBUV
+ if (LWS_LIBUV_ENABLED(context)) {
+ /* libuv has to do his own close handle processing asynchronously */
+ lws_libuv_closehandle(wsi);
+
+ return;
+ }
+#endif
+
+ lws_close_free_wsi_final(wsi);
+}
+
+void
+lws_close_free_wsi_final(struct lws *wsi)
+{
+ int n;
+
if (!lws_ssl_close(wsi) && lws_socket_is_valid(wsi->sock)) {
#if LWS_POSIX
- n = shutdown(wsi->sock, SHUT_RDWR);
- if (n)
- lwsl_debug("closing: shutdown ret %d\n", LWS_ERRNO);
-
n = compatible_close(wsi->sock);
if (n)
lwsl_debug("closing: close ret %d\n", LWS_ERRNO);
}
/* outermost destroy notification for wsi (user_space still intact) */
- context->protocols[0].callback(context, wsi, LWS_CALLBACK_WSI_DESTROY,
+ wsi->context->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY,
wsi->user_space, NULL, 0);
lws_free_wsi(wsi);
}
+#if LWS_POSIX
+LWS_VISIBLE int
+interface_to_sa(struct lws_context *context, const char *ifname, struct sockaddr_in *addr, size_t addrlen)
+{
+ int ipv6 = 0;
+#ifdef LWS_USE_IPV6
+ ipv6 = LWS_IPV6_ENABLED(context);
+#endif
+ (void)context;
+
+ return lws_interface_to_sa(ipv6, ifname, addr, addrlen);
+}
+#endif
+
LWS_VISIBLE int
lws_get_addresses(struct lws_context *context, void *ads, char *name,
int name_len, char *rip, int rip_len)
if (addr4.sin_family == AF_UNSPEC)
return -1;
- lws_plat_inet_ntop(AF_INET, &addr4.sin_addr, rip, rip_len);
+ if (lws_plat_inet_ntop(AF_INET, &addr4.sin_addr, rip, rip_len) == NULL)
+ return -1;
return 0;
#else
/**
* lws_get_peer_addresses() - Get client address information
- * @context: Libwebsockets context
* @wsi: Local struct lws associated with
* @fd: Connection socket descriptor
* @name: Buffer to take client address name
lws_callback_all_protocol(struct lws_context *context,
const struct lws_protocols *protocol, int reason)
{
+ struct lws_context_per_thread *pt = &context->pt[0];
+ unsigned int n, m = context->count_threads;
struct lws *wsi;
- int n;
- for (n = 0; n < context->fds_count; n++) {
- wsi = wsi_from_fd(context, context->fds[n].fd);
- if (!wsi)
- continue;
- if (wsi->protocol == protocol)
- protocol->callback(context, wsi,
- reason, wsi->user_space, NULL, 0);
+ while (m--) {
+ for (n = 0; n < pt->fds_count; n++) {
+ wsi = wsi_from_fd(context, pt->fds[n].fd);
+ if (!wsi)
+ continue;
+ if (wsi->protocol == protocol)
+ protocol->callback(wsi, reason, wsi->user_space,
+ NULL, 0);
+ }
+ pt++;
}
return 0;
}
-/**
- * lws_set_timeout() - marks the wsi as subject to a timeout
- *
- * You will not need this unless you are doing something special
- *
- * @wsi: Websocket connection instance
- * @reason: timeout reason
- * @secs: how many seconds
- */
-
-LWS_VISIBLE void
-lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs)
-{
- time_t now;
-
- time(&now);
-
- wsi->pending_timeout_limit = now + secs;
- wsi->pending_timeout = reason;
-}
-
-
#if LWS_POSIX
/**
lws_rx_flow_allow_all_protocol(const struct lws_context *context,
const struct lws_protocols *protocol)
{
- int n;
+ const struct lws_context_per_thread *pt = &context->pt[0];
struct lws *wsi;
-
- for (n = 0; n < context->fds_count; n++) {
- wsi = wsi_from_fd(context, context->fds[n].fd);
- if (!wsi)
- continue;
- if (wsi->protocol == protocol)
- lws_rx_flow_control(wsi, LWS_RXFLOW_ALLOW);
+ unsigned int n, m = context->count_threads;
+
+ while (m--) {
+ for (n = 0; n < pt->fds_count; n++) {
+ wsi = wsi_from_fd(context, pt->fds[n].fd);
+ if (!wsi)
+ continue;
+ if (wsi->protocol == protocol)
+ lws_rx_flow_control(wsi, LWS_RXFLOW_ALLOW);
+ }
+ pt++;
}
}
return (const char *)context->canonical_hostname;
}
-int user_callback_handle_rxflow(callback_function callback_function,
- struct lws_context *context, struct lws *wsi,
+int user_callback_handle_rxflow(lws_callback_function callback_function,
+ struct lws *wsi,
enum lws_callback_reasons reason, void *user,
void *in, size_t len)
{
int n;
- n = callback_function(context, wsi, reason, user, in, len);
+ n = callback_function(wsi, reason, user, in, len);
if (!n)
n = _lws_rx_flow_control(wsi);
LWS_VISIBLE int
lws_is_final_fragment(struct lws *wsi)
{
- return wsi->u.ws.final;
+ lwsl_info("%s: final %d, rx pk length %d, draining %d", __func__,
+ wsi->u.ws.final, wsi->u.ws.rx_packet_length,
+ wsi->u.ws.rx_draining_ext);
+ return wsi->u.ws.final && !wsi->u.ws.rx_packet_length && !wsi->u.ws.rx_draining_ext;
}
LWS_VISIBLE unsigned char
LWS_VISIBLE void lwsl_emit_stderr(int level, const char *line)
{
+ time_t o_now = time(NULL);
unsigned long long now;
+ struct tm *ptm = NULL;
char buf[300];
+#ifndef WIN32
+ struct tm tm;
+#endif
int n;
+#ifdef WIN32
+ ptm = localtime(&o_now);
+#else
+ if (localtime_r(&o_now, &tm))
+ ptm = &tm;
+#endif
buf[0] = '\0';
for (n = 0; n < LLL_COUNT; n++) {
if (level != (1 << n))
continue;
now = time_in_microseconds() / 100;
- sprintf(buf, "[%llu:%04d] %s: ",
- (unsigned long long) now / 10000,
- (int)(now % 10000), log_level_names[n]);
+ if (ptm)
+ sprintf(buf, "[%04d/%02d/%02d %02d:%02d:%02d:%04d] %s: ",
+ ptm->tm_year + 1900,
+ ptm->tm_mon,
+ ptm->tm_mday,
+ ptm->tm_hour,
+ ptm->tm_min,
+ ptm->tm_sec,
+ (int)(now % 10000), log_level_names[n]);
+ else
+ sprintf(buf, "[%llu:%04d] %s: ",
+ (unsigned long long) now / 10000,
+ (int)(now % 10000), log_level_names[n]);
break;
}
LWS_VISIBLE int
lws_partial_buffered(struct lws *wsi)
{
- return !!wsi->truncated_send_len;
+ return !!wsi->trunc_len;
}
void lws_set_protocol_write_pending(struct lws *wsi,
{
#ifdef LWS_USE_HTTP2
/* only if we are using HTTP2 on this connection */
- if (wsi->mode != LWS_CONNMODE_HTTP2_SERVING)
+ if (wsi->mode != LWSCM_HTTP2_SERVING)
return -1;
/* user is only interested in how much he can send, or that he can't */
if (wsi->u.http2.tx_credit <= 0)
LWS_VISIBLE void
lws_union_transition(struct lws *wsi, enum connection_mode mode)
{
+ lwsl_debug("%s: %p: mode %d\n", __func__, wsi, mode);
memset(&wsi->u, 0, sizeof(wsi->u));
wsi->mode = mode;
}
}
LWS_VISIBLE LWS_EXTERN struct lws_context *
-lws_get_ctx(const struct lws *wsi)
+lws_get_context(const struct lws *wsi)
{
return wsi->context;
}
+LWS_VISIBLE LWS_EXTERN int
+lws_get_count_threads(struct lws_context *context)
+{
+ return context->count_threads;
+}
+
LWS_VISIBLE LWS_EXTERN void *
lws_wsi_user(struct lws *wsi)
{
return wsi->user_space;
}
+
+LWS_VISIBLE LWS_EXTERN void
+lws_close_reason(struct lws *wsi, enum lws_close_status status,
+ unsigned char *buf, size_t len)
+{
+ unsigned char *p, *start;
+ int budget = sizeof(wsi->u.ws.ping_payload_buf) - LWS_PRE;
+
+ assert(wsi->mode == LWSCM_WS_SERVING || wsi->mode == LWSCM_WS_CLIENT);
+
+ start = p = &wsi->u.ws.ping_payload_buf[LWS_PRE];
+
+ *p++ = (((int)status) >> 8) & 0xff;
+ *p++ = ((int)status) & 0xff;
+
+ if (buf)
+ while (len-- && p < start + budget)
+ *p++ = *buf++;
+
+ wsi->u.ws.close_in_ping_buffer_len = p - start;
+}
+
+LWS_EXTERN int
+_lws_rx_flow_control(struct lws *wsi)
+{
+ /* there is no pending change */
+ if (!(wsi->rxflow_change_to & LWS_RXFLOW_PENDING_CHANGE)) {
+ lwsl_debug("%s: no pending change\n", __func__);
+ return 0;
+ }
+
+ /* stuff is still buffered, not ready to really accept new input */
+ if (wsi->rxflow_buffer) {
+ /* get ourselves called back to deal with stashed buffer */
+ lws_callback_on_writable(wsi);
+ return 0;
+ }
+
+ /* pending is cleared, we can change rxflow state */
+
+ wsi->rxflow_change_to &= ~LWS_RXFLOW_PENDING_CHANGE;
+
+ lwsl_info("rxflow: wsi %p change_to %d\n", wsi,
+ wsi->rxflow_change_to & LWS_RXFLOW_ALLOW);
+
+ /* adjust the pollfd for this wsi */
+
+ if (wsi->rxflow_change_to & LWS_RXFLOW_ALLOW) {
+ if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) {
+ lwsl_info("%s: fail\n", __func__);
+ return -1;
+ }
+ } else
+ if (lws_change_pollfd(wsi, LWS_POLLIN, 0))
+ return -1;
+
+ return 0;
+}
+
+LWS_EXTERN int
+lws_check_utf8(unsigned char *state, unsigned char *buf, size_t len)
+{
+ static const unsigned char e0f4[] = {
+ 0xa0 | ((2 - 1) << 2) | 1, /* e0 */
+ 0x80 | ((4 - 1) << 2) | 1, /* e1 */
+ 0x80 | ((4 - 1) << 2) | 1, /* e2 */
+ 0x80 | ((4 - 1) << 2) | 1, /* e3 */
+ 0x80 | ((4 - 1) << 2) | 1, /* e4 */
+ 0x80 | ((4 - 1) << 2) | 1, /* e5 */
+ 0x80 | ((4 - 1) << 2) | 1, /* e6 */
+ 0x80 | ((4 - 1) << 2) | 1, /* e7 */
+ 0x80 | ((4 - 1) << 2) | 1, /* e8 */
+ 0x80 | ((4 - 1) << 2) | 1, /* e9 */
+ 0x80 | ((4 - 1) << 2) | 1, /* ea */
+ 0x80 | ((4 - 1) << 2) | 1, /* eb */
+ 0x80 | ((4 - 1) << 2) | 1, /* ec */
+ 0x80 | ((2 - 1) << 2) | 1, /* ed */
+ 0x80 | ((4 - 1) << 2) | 1, /* ee */
+ 0x80 | ((4 - 1) << 2) | 1, /* ef */
+ 0x90 | ((3 - 1) << 2) | 2, /* f0 */
+ 0x80 | ((4 - 1) << 2) | 2, /* f1 */
+ 0x80 | ((4 - 1) << 2) | 2, /* f2 */
+ 0x80 | ((4 - 1) << 2) | 2, /* f3 */
+ 0x80 | ((1 - 1) << 2) | 2, /* f4 */
+
+ 0, /* s0 */
+ 0x80 | ((4 - 1) << 2) | 0, /* s2 */
+ 0x80 | ((4 - 1) << 2) | 1, /* s3 */
+ };
+ unsigned char s = *state;
+
+ while (len--) {
+ unsigned char c = *buf++;
+
+ if (!s) {
+ if (c >= 0x80) {
+ if (c < 0xc2 || c > 0xf4)
+ return 1;
+ if (c < 0xe0)
+ s = 0x80 | ((4 - 1) << 2);
+ else
+ s = e0f4[c - 0xe0];
+ }
+ } else {
+ if (c < (s & 0xf0) ||
+ c >= (s & 0xf0) + 0x10 + ((s << 2) & 0x30))
+ return 1;
+ s = e0f4[21 + (s & 3)];
+ }
+ }
+
+ *state = s;
+
+ return 0;
+}
+
+/**
+ * lws_parse_uri: cut up prot:/ads:port/path into pieces
+ * Notice it does so by dropping '\0' into input string
+ * and the leading / on the path is consequently lost
+ *
+ * @p: incoming uri string.. will get written to
+ * @prot: result pointer for protocol part (https://)
+ * @ads: result pointer for address part
+ * @port: result pointer for port part
+ * @path: result pointer for path part
+ */
+
+LWS_VISIBLE LWS_EXTERN int
+lws_parse_uri(char *p, const char **prot, const char **ads, int *port,
+ const char **path)
+{
+ const char *end;
+ static const char *slash = "/";
+
+ /* cut up the location into address, port and path */
+ *prot = p;
+ while (*p && (*p != ':' || p[1] != '/' || p[2] != '/'))
+ p++;
+ if (!*p) {
+ end = p;
+ p = (char *)*prot;
+ *prot = end;
+ } else {
+ *p = '\0';
+ p += 3;
+ }
+ *ads = p;
+ if (!strcmp(*prot, "http") || !strcmp(*prot, "ws"))
+ *port = 80;
+ else if (!strcmp(*prot, "https") || !strcmp(*prot, "wss"))
+ *port = 443;
+
+ while (*p && *p != ':' && *p != '/')
+ p++;
+ if (*p == ':') {
+ *p++ = '\0';
+ *port = atoi(p);
+ while (*p && *p != '/')
+ p++;
+ }
+ *path = slash;
+ if (*p) {
+ *p++ = '\0';
+ if (*p)
+ *path = p;
+ }
+
+ return 0;
+}
+
+#ifdef LWS_NO_EXTENSIONS
+
+/* we need to provide dummy callbacks for internal exts
+ * so user code runs when faced with a lib compiled with
+ * extensions disabled.
+ */
+
+int
+lws_extension_callback_pm_deflate(struct lws_context *context,
+ const struct lws_extension *ext,
+ struct lws *wsi,
+ enum lws_extension_callback_reasons reason,
+ void *user, void *in, size_t len)
+{
+ (void)context;
+ (void)ext;
+ (void)wsi;
+ (void)reason;
+ (void)user;
+ (void)in;
+ (void)len;
+
+ return 0;
+}
+#endif
+