/** * 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; compatible_close(wsi->sock); 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); }
/** * 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 **pwsi, int ssl, const char *address, int port, const char *path, const char *host) { char origin[300] = "", protocol[300] = "", method[32] = "", iface[16] = "", *p; struct lws *wsi = *pwsi; if (wsi->redirects == 3) { lwsl_err("%s: Too many redirects\n", __func__); return NULL; } wsi->redirects++; p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN); if (p) strncpy(origin, p, sizeof(origin) - 1); p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS); if (p) strncpy(protocol, p, sizeof(protocol) - 1); p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD); if (p) strncpy(method, p, sizeof(method) - 1); p = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_IFACE); if (p) strncpy(method, p, sizeof(iface) - 1); lwsl_info("redirect ads='%s', port=%d, path='%s', ssl = %d\n", address, port, path, ssl); /* close the connection by hand */ #ifdef LWS_OPENSSL_SUPPORT lws_ssl_close(wsi); #endif #ifdef LWS_USE_LIBUV if (LWS_LIBUV_ENABLED(wsi->context)) { lwsl_debug("%s: lws_libuv_closehandle: wsi %p\n", __func__, wsi); /* * libuv has to do his own close handle processing asynchronously * but once it starts we can do everything else synchronously, * including trash wsi->desc.sockfd since it took a copy. * * When it completes it will call compatible_close() */ lws_libuv_closehandle_manually(wsi); } else #else compatible_close(wsi->desc.sockfd); #endif remove_wsi_socket_from_fds(wsi); #ifdef LWS_OPENSSL_SUPPORT wsi->use_ssl = ssl; #else if (ssl) { lwsl_err("%s: not configured for ssl\n", __func__); return NULL; } #endif wsi->desc.sockfd = LWS_SOCK_INVALID; wsi->state = LWSS_CLIENT_UNCONNECTED; wsi->protocol = NULL; wsi->pending_timeout = NO_PENDING_TIMEOUT; wsi->c_port = port; wsi->hdr_parsing_completed = 0; _lws_header_table_reset(wsi->u.hdr.ah); if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, address)) return NULL; if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_HOST, host)) return NULL; if (origin[0]) if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_ORIGIN, origin)) return NULL; if (protocol[0]) if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS, protocol)) return NULL; if (method[0]) if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_METHOD, method)) return NULL; if (iface[0]) if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_IFACE, iface)) return NULL; origin[0] = '/'; strncpy(&origin[1], path, sizeof(origin) - 2); if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_URI, origin)) return NULL; *pwsi = lws_client_connect_2(wsi); return *pwsi; }
void libwebsocket_close_and_free_session(struct libwebsocket_context *context, struct libwebsocket *wsi, enum lws_close_status reason) { int n, m, ret; int old_state; unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 2 + LWS_SEND_BUFFER_POST_PADDING]; struct lws_tokens eff_buf; if (!wsi) return; old_state = wsi->state; if (wsi->socket_is_permanently_unusable || reason == LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY) goto just_kill_connection; switch (old_state) { case WSI_STATE_DEAD_SOCKET: return; /* we tried the polite way... */ case WSI_STATE_AWAITING_CLOSE_ACK: goto just_kill_connection; case WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE: if (wsi->truncated_send_len) { libwebsocket_callback_on_writable(context, wsi); return; } lwsl_info("wsi %p completed WSI_STATE_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; libwebsocket_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) { context->protocols[0].callback(context, wsi, LWS_CALLBACK_CLIENT_CONNECTION_ERROR, wsi->user_space, NULL, 0); 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 == LWS_CONNMODE_HTTP_SERVING_ACCEPTED) { if (wsi->u.http.fd != LWS_INVALID_FILE) { // TODO: If we're just closing with LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY this file descriptor might leak? lwsl_debug("closing http file\n"); compatible_file_close(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); } } /* * 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) { lwsl_ext("extension vetoed close\n"); return; } /* * flush any tx pending from extensions, since we may send close packet * if there are problems with send, just nuke the connection */ do { ret = 0; eff_buf.token = NULL; eff_buf.token_len = 0; /* show every extension the new incoming data */ m = lws_ext_callback_for_each_active(wsi, LWS_EXT_CALLBACK_FLUSH_PENDING_TX, &eff_buf, 0); if (m < 0) { lwsl_ext("Extension reports fatal error\n"); goto just_kill_connection; } if (m) /* * at least one extension told us he has more * to spill, so we will go around again after */ ret = 1; /* assuming they left us something to send, send it */ if (eff_buf.token_len) if (lws_issue_raw(wsi, (unsigned char *)eff_buf.token, eff_buf.token_len) != eff_buf.token_len) { lwsl_debug("close: ext spill failed\n"); goto just_kill_connection; } } while (ret); /* * signal we are closing, libwebsocket_write will * 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. * * 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. */ if (old_state == WSI_STATE_ESTABLISHED && 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 = libwebsocket_write(wsi, &buf[LWS_SEND_BUFFER_PRE_PADDING + 2], 0, 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; /* * ...and we should wait for a reply for a bit * out of politeness */ libwebsocket_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_ACK, 1); lwsl_debug("sent close indication, awaiting ack\n"); return; } lwsl_info("close: sending close packet failed, hanging up\n"); /* else, the send failed and we should just hang up */ } just_kill_connection: lwsl_debug("close: just_kill_connection\n"); /* * 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(context, wsi); // checking return redundant since we anyway close remove_wsi_socket_from_fds(context, wsi); wsi->state = WSI_STATE_DEAD_SOCKET; lws_free2(wsi->rxflow_buffer); lws_free_header_table(wsi); if ((old_state == WSI_STATE_ESTABLISHED || wsi->mode == LWS_CONNMODE_WS_SERVING || wsi->mode == LWS_CONNMODE_WS_CLIENT)) { lws_free2(wsi->u.ws.rx_user_buffer); if (wsi->truncated_send_malloc) { /* not going to be completed... nuke it */ lws_free2(wsi->truncated_send_malloc); wsi->truncated_send_len = 0; } 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; } } /* 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))) { lwsl_debug("calling back CLOSED\n"); wsi->protocol->callback(context, wsi, LWS_CALLBACK_CLOSED, wsi->user_space, NULL, 0); } else if (wsi->mode == LWS_CONNMODE_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) { lwsl_debug("Connection closed before server reply\n"); context->protocols[0].callback(context, 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); /* deallocate any active extension contexts */ if (lws_ext_callback_for_each_active(wsi, LWS_EXT_CALLBACK_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) lwsl_warn("ext destroy wsi failed\n"); /* lwsl_info("closing fd=%d\n", wsi->sock); */ if (!lws_ssl_close(wsi) && wsi->sock >= 0) { 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->user_space, NULL, 0); lws_free_wsi(wsi); }