mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-30 00:00:16 +01:00
minimal-http-client-multi: add POST
This adds support for POST in both h1 and h2 queues / stream binding. The previous queueing tried to keep the "leader" wsi who made the actual connection around and have it act on the transaction queue tail if it had done its own thing. This refactors it so instead, who is the "leader" moves down the queue and the queued guys inherit the fd, SSL * and queue from the old leader as they take over. This lets them operate in their own wsi identity directly and gets rid of all the "effective wsi" checks, which was applied incompletely and getting out of hand considering the separate lws_mux checks for h2 and other muxed protocols alongside it. This change also allows one wsi at a time to own the transaction for POST. --post is added as an option to lws-minimal-http-client-multi and 6 extra selftests with POST on h1/h2, pipelined or not and staggered or not are added to the CI.
This commit is contained in:
parent
fddca26be0
commit
ac1229f2f7
22 changed files with 523 additions and 336 deletions
58
lib/core-net/README.md
Normal file
58
lib/core-net/README.md
Normal file
|
@ -0,0 +1,58 @@
|
|||
# Implementation background
|
||||
|
||||
## Client connection Queueing
|
||||
|
||||
By default lws treats each client connection as completely separate, and each is
|
||||
made from scratch with its own network connection independently.
|
||||
|
||||
If the user code sets the `LCCSCF_PIPELINE` bit on `info.ssl_connection` when
|
||||
creating the client connection though, lws attempts to optimize multiple client
|
||||
connections to the same place by sharing any existing connection and its tls
|
||||
tunnel where possible.
|
||||
|
||||
There are two basic approaches, for h1 additional connections of the same type
|
||||
and endpoint basically queue on a leader and happen sequentially.
|
||||
|
||||
For muxed protocols like h2, they may also queue if the initial connection is
|
||||
not up yet, but subsequently the will all join the existing connection
|
||||
simultaneously "broadside".
|
||||
|
||||
## h1 queueing
|
||||
|
||||
The initial wsi to start the network connection becomes the "leader" that
|
||||
subsequent connection attempts will queue against. Each vhost has a dll2_owner
|
||||
`wsi->dll_cli_active_conns_owner` that "leaders" who are actually making network
|
||||
connections themselves can register on as "active client connections".
|
||||
|
||||
Other client wsi being created who find there is already a leader on the active
|
||||
client connection list for the vhost, can join their dll2 wsi->dll2_cli_txn_queue
|
||||
to the leader's wsi->dll2_cli_txn_queue_owner to "queue" on the leader.
|
||||
|
||||
The user code does not know which wsi was first or is queued, it just waits for
|
||||
stuff to happen the same either way.
|
||||
|
||||
When the "leader" wsi connects, it performs its client transaction as normal,
|
||||
and at the end arrives at `lws_http_transaction_completed_client()`. Here, it
|
||||
calls through to the lws_mux `_lws_generic_transaction_completed_active_conn()`
|
||||
helper. This helper sees if anything else is queued, and if so, migrates assets
|
||||
like the SSL *, the socket fd, and any remaining queue from the original leader
|
||||
to the head of the list, which replaces the old leader as the "active client
|
||||
connection" any subsequent connects would queue on.
|
||||
|
||||
It has to be done this way so that user code which may know each client wsi by
|
||||
its wsi, or have marked it with an opaque_user_data pointer, is getting its
|
||||
specific request handled by the wsi it expects it to be handled by.
|
||||
|
||||
A side effect of this, and in order to be able to handle POSTs cleanly, lws
|
||||
does not attempt to send the headers for the next queued child before the
|
||||
previous child has finished.
|
||||
|
||||
The process of moving the SSL context and fd etc between the queued wsi continues
|
||||
until the queue is all handled.
|
||||
|
||||
## muxed protocol queueing and stream binding
|
||||
|
||||
h2 connections act the same as h1 before the initial connection has been made,
|
||||
but once it is made all the queued connections join the network connection as
|
||||
child mux streams immediately, "broadside", binding the stream to the existing
|
||||
network connection.
|
|
@ -94,7 +94,7 @@ lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len)
|
|||
return 0;
|
||||
|
||||
if (!wsi->mux_substream && !lws_socket_is_valid(wsi->desc.sockfd))
|
||||
lwsl_warn("** error invalid sock but expected to send\n");
|
||||
lwsl_err("%s: invalid sock %p\n", __func__, wsi);
|
||||
|
||||
/* limit sending */
|
||||
if (wsi->protocol->tx_packet_size)
|
||||
|
|
|
@ -502,6 +502,7 @@ int
|
|||
lws_callback_on_writable(struct lws *wsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt;
|
||||
struct lws *w = wsi;
|
||||
|
||||
if (lwsi_state(wsi) == LRS_SHUTDOWN)
|
||||
return 0;
|
||||
|
@ -527,16 +528,16 @@ lws_callback_on_writable(struct lws *wsi)
|
|||
if (wsi->role_ops->callback_on_writable) {
|
||||
if (wsi->role_ops->callback_on_writable(wsi))
|
||||
return 1;
|
||||
wsi = lws_get_network_wsi(wsi);
|
||||
w = lws_get_network_wsi(wsi);
|
||||
}
|
||||
|
||||
if (wsi->position_in_fds_table == LWS_NO_FDS_POS) {
|
||||
if (w->position_in_fds_table == LWS_NO_FDS_POS) {
|
||||
lwsl_debug("%s: failed to find socket %d\n", __func__,
|
||||
wsi->desc.sockfd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (__lws_change_pollfd(wsi, 0, LWS_POLLOUT))
|
||||
if (__lws_change_pollfd(w, 0, LWS_POLLOUT))
|
||||
return -1;
|
||||
|
||||
return 1;
|
||||
|
|
|
@ -856,21 +856,21 @@ lws_service_do_ripe_rxflow(struct lws_context_per_thread *pt);
|
|||
const struct lws_role_ops *
|
||||
lws_role_by_name(const char *name);
|
||||
|
||||
LWS_EXTERN int
|
||||
int
|
||||
lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port,
|
||||
const char *iface, int ipv6_allowed);
|
||||
|
||||
#if defined(LWS_WITH_IPV6)
|
||||
LWS_EXTERN unsigned long
|
||||
unsigned long
|
||||
lws_get_addr_scope(const char *ipaddr);
|
||||
#endif
|
||||
|
||||
LWS_EXTERN void
|
||||
void
|
||||
lws_close_free_wsi(struct lws *wsi, enum lws_close_status, const char *caller);
|
||||
LWS_EXTERN void
|
||||
void
|
||||
__lws_close_free_wsi(struct lws *wsi, enum lws_close_status, const char *caller);
|
||||
|
||||
LWS_EXTERN void
|
||||
void
|
||||
__lws_free_wsi(struct lws *wsi);
|
||||
|
||||
#if LWS_MAX_SMP > 1
|
||||
|
@ -917,61 +917,61 @@ lws_pt_stats_unlock(struct lws_context_per_thread *pt)
|
|||
#define lws_context_init_extensions(_a, _b)
|
||||
#endif
|
||||
|
||||
LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
||||
int LWS_WARN_UNUSED_RESULT
|
||||
lws_client_interpret_server_handshake(struct lws *wsi);
|
||||
|
||||
LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
||||
int LWS_WARN_UNUSED_RESULT
|
||||
lws_ws_rx_sm(struct lws *wsi, char already_processed, unsigned char c);
|
||||
|
||||
LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
||||
int LWS_WARN_UNUSED_RESULT
|
||||
lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len);
|
||||
|
||||
LWS_EXTERN void
|
||||
void
|
||||
lws_role_transition(struct lws *wsi, enum lwsi_role role, enum lwsi_state state,
|
||||
const struct lws_role_ops *ops);
|
||||
|
||||
int
|
||||
lws_http_to_fallback(struct lws *wsi, unsigned char *buf, size_t len);
|
||||
|
||||
LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
||||
int LWS_WARN_UNUSED_RESULT
|
||||
user_callback_handle_rxflow(lws_callback_function, struct lws *wsi,
|
||||
enum lws_callback_reasons reason, void *user,
|
||||
void *in, size_t len);
|
||||
|
||||
LWS_EXTERN int
|
||||
int
|
||||
lws_plat_set_nonblocking(lws_sockfd_type fd);
|
||||
|
||||
LWS_EXTERN int
|
||||
int
|
||||
lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd,
|
||||
int unix_skt);
|
||||
|
||||
LWS_EXTERN int
|
||||
int
|
||||
lws_plat_check_connection_error(struct lws *wsi);
|
||||
|
||||
LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
||||
int LWS_WARN_UNUSED_RESULT
|
||||
lws_header_table_attach(struct lws *wsi, int autoservice);
|
||||
|
||||
LWS_EXTERN int
|
||||
int
|
||||
lws_header_table_detach(struct lws *wsi, int autoservice);
|
||||
LWS_EXTERN int
|
||||
int
|
||||
__lws_header_table_detach(struct lws *wsi, int autoservice);
|
||||
|
||||
LWS_EXTERN void
|
||||
void
|
||||
lws_header_table_reset(struct lws *wsi, int autoservice);
|
||||
|
||||
void
|
||||
__lws_header_table_reset(struct lws *wsi, int autoservice);
|
||||
|
||||
LWS_EXTERN char * LWS_WARN_UNUSED_RESULT
|
||||
char * LWS_WARN_UNUSED_RESULT
|
||||
lws_hdr_simple_ptr(struct lws *wsi, enum lws_token_indexes h);
|
||||
|
||||
LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
||||
int LWS_WARN_UNUSED_RESULT
|
||||
lws_hdr_simple_create(struct lws *wsi, enum lws_token_indexes h, const char *s);
|
||||
|
||||
LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
||||
int LWS_WARN_UNUSED_RESULT
|
||||
lws_ensure_user_space(struct lws *wsi);
|
||||
|
||||
LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
||||
int LWS_WARN_UNUSED_RESULT
|
||||
lws_change_pollfd(struct lws *wsi, int _and, int _or);
|
||||
|
||||
#if defined(LWS_WITH_SERVER)
|
||||
|
@ -990,7 +990,7 @@ lws_change_pollfd(struct lws *wsi, int _and, int _or);
|
|||
#define lws_server_get_canonical_hostname(_a, _b)
|
||||
#endif
|
||||
|
||||
LWS_EXTERN int
|
||||
int
|
||||
__remove_wsi_socket_from_fds(struct lws *wsi);
|
||||
|
||||
enum {
|
||||
|
@ -1004,32 +1004,32 @@ enum {
|
|||
int
|
||||
_lws_plat_service_forced_tsi(struct lws_context *context, int tsi);
|
||||
|
||||
LWS_EXTERN int
|
||||
int
|
||||
lws_rxflow_cache(struct lws *wsi, unsigned char *buf, int n, int len);
|
||||
|
||||
LWS_EXTERN int
|
||||
int
|
||||
lws_service_flag_pending(struct lws_context *context, int tsi);
|
||||
|
||||
static LWS_INLINE int
|
||||
lws_has_buffered_out(struct lws *wsi) { return !!wsi->buflist_out; }
|
||||
|
||||
LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
||||
int LWS_WARN_UNUSED_RESULT
|
||||
lws_ws_client_rx_sm(struct lws *wsi, unsigned char c);
|
||||
|
||||
LWS_EXTERN lws_parser_return_t LWS_WARN_UNUSED_RESULT
|
||||
lws_parser_return_t LWS_WARN_UNUSED_RESULT
|
||||
lws_parse(struct lws *wsi, unsigned char *buf, int *len);
|
||||
|
||||
LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
||||
int LWS_WARN_UNUSED_RESULT
|
||||
lws_parse_urldecode(struct lws *wsi, uint8_t *_c);
|
||||
|
||||
LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
||||
int LWS_WARN_UNUSED_RESULT
|
||||
lws_http_action(struct lws *wsi);
|
||||
|
||||
LWS_EXTERN void
|
||||
void
|
||||
__lws_close_free_wsi_final(struct lws *wsi);
|
||||
LWS_EXTERN void
|
||||
void
|
||||
lws_libuv_closehandle(struct lws *wsi);
|
||||
LWS_EXTERN int
|
||||
int
|
||||
lws_libuv_check_watcher_active(struct lws *wsi);
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
|
@ -1089,63 +1089,61 @@ void
|
|||
lws_sum_stats(const struct lws_context *ctx, struct lws_conn_stats *cs);
|
||||
#endif
|
||||
|
||||
LWS_EXTERN int
|
||||
int
|
||||
__lws_timed_callback_remove(struct lws_vhost *vh, struct lws_timed_vh_protocol *p);
|
||||
|
||||
LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
||||
int LWS_WARN_UNUSED_RESULT
|
||||
__insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi);
|
||||
|
||||
LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
||||
int LWS_WARN_UNUSED_RESULT
|
||||
lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len);
|
||||
|
||||
LWS_EXTERN lws_usec_t
|
||||
lws_usec_t
|
||||
__lws_seq_timeout_check(struct lws_context_per_thread *pt, lws_usec_t usnow);
|
||||
|
||||
LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT
|
||||
struct lws * LWS_WARN_UNUSED_RESULT
|
||||
lws_client_connect_2_dnsreq(struct lws *wsi);
|
||||
|
||||
LWS_VISIBLE struct lws * LWS_WARN_UNUSED_RESULT
|
||||
lws_client_reset(struct lws **wsi, int ssl, const char *address, int port,
|
||||
const char *path, const char *host, char weak);
|
||||
|
||||
LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT
|
||||
struct lws * LWS_WARN_UNUSED_RESULT
|
||||
lws_create_new_server_wsi(struct lws_vhost *vhost, int fixed_tsi);
|
||||
|
||||
LWS_EXTERN char * LWS_WARN_UNUSED_RESULT
|
||||
char * LWS_WARN_UNUSED_RESULT
|
||||
lws_generate_client_handshake(struct lws *wsi, char *pkt);
|
||||
|
||||
LWS_EXTERN int
|
||||
int
|
||||
lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd);
|
||||
|
||||
LWS_EXTERN struct lws *
|
||||
struct lws *
|
||||
lws_http_client_connect_via_info2(struct lws *wsi);
|
||||
|
||||
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
LWS_EXTERN int lws_client_socket_service(struct lws *wsi,
|
||||
struct lws_pollfd *pollfd,
|
||||
struct lws *wsi_conn);
|
||||
LWS_EXTERN struct lws *
|
||||
lws_client_wsi_effective(struct lws *wsi);
|
||||
LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
||||
int
|
||||
lws_client_socket_service(struct lws *wsi, struct lws_pollfd *pollfd);
|
||||
|
||||
int LWS_WARN_UNUSED_RESULT
|
||||
lws_http_transaction_completed_client(struct lws *wsi);
|
||||
#if !defined(LWS_WITH_TLS)
|
||||
#define lws_context_init_client_ssl(_a, _b) (0)
|
||||
#endif
|
||||
LWS_EXTERN void
|
||||
void
|
||||
lws_decode_ssl_error(void);
|
||||
#else
|
||||
#define lws_context_init_client_ssl(_a, _b) (0)
|
||||
#endif
|
||||
|
||||
LWS_EXTERN int
|
||||
int
|
||||
__lws_rx_flow_control(struct lws *wsi);
|
||||
|
||||
LWS_EXTERN int
|
||||
int
|
||||
_lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa);
|
||||
|
||||
#if defined(LWS_WITH_SERVER)
|
||||
LWS_EXTERN int
|
||||
int
|
||||
lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len);
|
||||
#else
|
||||
#define lws_server_socket_service(_b, _c) (0)
|
||||
|
@ -1153,9 +1151,9 @@ lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len);
|
|||
#endif
|
||||
|
||||
#ifdef LWS_WITH_ACCESS_LOG
|
||||
LWS_EXTERN int
|
||||
int
|
||||
lws_access_log(struct lws *wsi);
|
||||
LWS_EXTERN void
|
||||
void
|
||||
lws_prepare_access_log_info(struct lws *wsi, char *uri_ptr, int len, int meth);
|
||||
#else
|
||||
#define lws_access_log(_a)
|
||||
|
@ -1207,35 +1205,35 @@ lws_plat_pipe_close(struct lws *wsi);
|
|||
void
|
||||
lws_addrinfo_clean(struct lws *wsi);
|
||||
|
||||
LWS_EXTERN void
|
||||
void
|
||||
lws_add_wsi_to_draining_ext_list(struct lws *wsi);
|
||||
LWS_EXTERN void
|
||||
void
|
||||
lws_remove_wsi_from_draining_ext_list(struct lws *wsi);
|
||||
LWS_EXTERN int
|
||||
int
|
||||
lws_poll_listen_fd(struct lws_pollfd *fd);
|
||||
LWS_EXTERN int
|
||||
int
|
||||
lws_plat_service(struct lws_context *context, int timeout_ms);
|
||||
LWS_EXTERN LWS_VISIBLE int
|
||||
LWS_VISIBLE int
|
||||
_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi);
|
||||
|
||||
LWS_EXTERN int
|
||||
int
|
||||
lws_pthread_self_to_tsi(struct lws_context *context);
|
||||
LWS_EXTERN const char * LWS_WARN_UNUSED_RESULT
|
||||
const char * LWS_WARN_UNUSED_RESULT
|
||||
lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt);
|
||||
LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
||||
int LWS_WARN_UNUSED_RESULT
|
||||
lws_plat_inet_pton(int af, const char *src, void *dst);
|
||||
|
||||
LWS_EXTERN void
|
||||
void
|
||||
lws_same_vh_protocol_remove(struct lws *wsi);
|
||||
LWS_EXTERN void
|
||||
void
|
||||
__lws_same_vh_protocol_remove(struct lws *wsi);
|
||||
LWS_EXTERN void
|
||||
void
|
||||
lws_same_vh_protocol_insert(struct lws *wsi, int n);
|
||||
|
||||
void
|
||||
lws_seq_destroy_all_on_pt(struct lws_context_per_thread *pt);
|
||||
|
||||
LWS_EXTERN int
|
||||
int
|
||||
lws_broadcast(struct lws_context_per_thread *pt, int reason, void *in, size_t len);
|
||||
|
||||
#if defined(LWS_WITH_STATS)
|
||||
|
@ -1320,7 +1318,7 @@ lws_async_dns_deinit(lws_async_dns_t *dns);
|
|||
int
|
||||
lws_protocol_init_vhost(struct lws_vhost *vh, int *any);
|
||||
int
|
||||
_lws_generic_transaction_completed_active_conn(struct lws *wsi);
|
||||
_lws_generic_transaction_completed_active_conn(struct lws **wsi);
|
||||
|
||||
#define ACTIVE_CONNS_SOLO 0
|
||||
#define ACTIVE_CONNS_MUXED 1
|
||||
|
|
|
@ -450,11 +450,13 @@ lws_buflist_aware_finished_consuming(struct lws *wsi, struct lws_tokens *ebuf,
|
|||
return 0;
|
||||
|
||||
if (used && buffered) {
|
||||
m = (int)lws_buflist_use_segment(&wsi->buflist, (size_t)used);
|
||||
// lwsl_notice("%s: used %d, next %d\n", __func__, used, m);
|
||||
// lws_buflist_describe(&wsi->buflist, wsi, __func__);
|
||||
if (m)
|
||||
return 0;
|
||||
if (wsi->buflist) {
|
||||
m = (int)lws_buflist_use_segment(&wsi->buflist, (size_t)used);
|
||||
// lwsl_notice("%s: used %d, next %d\n", __func__, used, m);
|
||||
// lws_buflist_describe(&wsi->buflist, wsi, __func__);
|
||||
if (m)
|
||||
return 0;
|
||||
}
|
||||
|
||||
lwsl_info("%s: removed %p from dll_buflist\n", __func__, wsi);
|
||||
lws_dll2_remove(&wsi->dll_buflist);
|
||||
|
|
|
@ -1444,15 +1444,25 @@ lws_vhost_active_conns(struct lws *wsi, struct lws **nwsi, const char *adsin)
|
|||
*/
|
||||
if (w->client_h2_alpn && w->client_mux_migrated &&
|
||||
(lwsi_state(w) == LRS_H2_WAITING_TO_SEND_HEADERS ||
|
||||
lwsi_state(w) == LRS_ESTABLISHED)) {
|
||||
lwsi_state(w) == LRS_ESTABLISHED ||
|
||||
lwsi_state(w) == LRS_IDLING)) {
|
||||
|
||||
lwsl_info("%s: just join h2 directly\n",
|
||||
__func__);
|
||||
lwsl_notice("%s: just join h2 directly 0x%x\n",
|
||||
__func__, lwsi_state(w));
|
||||
|
||||
if (lwsi_state(w) == LRS_IDLING) {
|
||||
// lwsi_set_state(w, LRS_ESTABLISHED);
|
||||
_lws_generic_transaction_completed_active_conn(&w);
|
||||
}
|
||||
|
||||
//lwsi_set_state(w, LRS_H1C_ISSUE_HANDSHAKE2);
|
||||
|
||||
wsi->client_h2_alpn = 1;
|
||||
lws_wsi_h2_adopt(w, wsi);
|
||||
lws_vhost_unlock(wsi->vhost); /* } ---------- */
|
||||
|
||||
*nwsi = w;
|
||||
|
||||
return ACTIVE_CONNS_MUXED;
|
||||
}
|
||||
#endif
|
||||
|
@ -1461,11 +1471,16 @@ lws_vhost_active_conns(struct lws *wsi, struct lws **nwsi, const char *adsin)
|
|||
wsi, w, (unsigned long)w->wsistate);
|
||||
/*
|
||||
* ...let's add ourselves to his transaction queue...
|
||||
* we are adding ourselves at the HEAD
|
||||
* we are adding ourselves at the TAIL
|
||||
*/
|
||||
lws_dll2_add_head(&wsi->dll2_cli_txn_queue,
|
||||
lws_dll2_add_tail(&wsi->dll2_cli_txn_queue,
|
||||
&w->dll2_cli_txn_queue_owner);
|
||||
|
||||
if (lwsi_state(w) == LRS_IDLING) {
|
||||
// lwsi_set_state(w, LRS_ESTABLISHED);
|
||||
_lws_generic_transaction_completed_active_conn(&w);
|
||||
}
|
||||
|
||||
/*
|
||||
* h1: pipeline our headers out on him,
|
||||
* and wait for our turn at client transaction_complete
|
||||
|
|
|
@ -759,9 +759,9 @@ lws_get_context(const struct lws *wsi)
|
|||
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
int
|
||||
_lws_generic_transaction_completed_active_conn(struct lws *wsi)
|
||||
_lws_generic_transaction_completed_active_conn(struct lws **_wsi)
|
||||
{
|
||||
struct lws *wsi_eff = lws_client_wsi_effective(wsi);
|
||||
struct lws *wnew, *wsi = *_wsi;
|
||||
|
||||
/*
|
||||
* Are we constitutionally capable of having a queue, ie, we are on
|
||||
|
@ -773,34 +773,25 @@ _lws_generic_transaction_completed_active_conn(struct lws *wsi)
|
|||
if (lws_dll2_is_detached(&wsi->dll_cli_active_conns))
|
||||
return 0; /* no new transaction */
|
||||
|
||||
/* if this was a queued guy, close him and remove from queue */
|
||||
|
||||
if (wsi->transaction_from_pipeline_queue) {
|
||||
lwsl_debug("closing queued wsi %p\n", wsi_eff);
|
||||
/* so the close doesn't trigger a CCE */
|
||||
wsi_eff->already_did_cce = 1;
|
||||
__lws_close_free_wsi(wsi_eff,
|
||||
LWS_CLOSE_STATUS_CLIENT_TRANSACTION_DONE,
|
||||
"queued client done");
|
||||
}
|
||||
|
||||
/* after the first one, they can only be coming from the queue */
|
||||
wsi->transaction_from_pipeline_queue = 1;
|
||||
|
||||
wsi->hdr_parsing_completed = 0;
|
||||
|
||||
/* is there a new tail after removing that one? */
|
||||
wsi_eff = lws_client_wsi_effective(wsi);
|
||||
|
||||
/*
|
||||
* Do we have something pipelined waiting?
|
||||
* it's OK if he hasn't managed to send his headers yet... he's next
|
||||
* in line to do that...
|
||||
* With h1 queuing, the original "active client" moves his attributes
|
||||
* like fd, ssl, queue and active client list entry to the next guy in
|
||||
* the queue before closing... it's because the user code knows the
|
||||
* individual wsi and the action must take place in the correct wsi
|
||||
* context. Note this means we don't truly pipeline headers.
|
||||
*
|
||||
* Trying to keep the original "active client" in place to do the work
|
||||
* of the wsi breaks down when dealing with queued POSTs otherwise; it's
|
||||
* also competing with the real mux child arrangements and complicating
|
||||
* the code.
|
||||
*
|
||||
* For that reason, see if we have any queued child now...
|
||||
*/
|
||||
if (wsi_eff == wsi) {
|
||||
|
||||
if (!wsi->dll2_cli_txn_queue_owner.head) {
|
||||
/*
|
||||
* Nothing pipelined... we should hang around a bit
|
||||
* in case something turns up...
|
||||
* in case something turns up... otherwise we'll close
|
||||
*/
|
||||
lwsl_info("%s: nothing pipelined waiting\n", __func__);
|
||||
lwsi_set_state(wsi, LRS_IDLING);
|
||||
|
@ -810,6 +801,96 @@ _lws_generic_transaction_completed_active_conn(struct lws *wsi)
|
|||
return 0; /* no new transaction right now */
|
||||
}
|
||||
|
||||
/*
|
||||
* We have a queued child wsi we should bequeath our assets to, before
|
||||
* closing ourself
|
||||
*/
|
||||
|
||||
lws_vhost_lock(wsi->vhost);
|
||||
|
||||
wnew = lws_container_of(wsi->dll2_cli_txn_queue_owner.head, struct lws,
|
||||
dll2_cli_txn_queue);
|
||||
|
||||
assert(wsi != wnew);
|
||||
|
||||
lws_dll2_remove(&wnew->dll2_cli_txn_queue);
|
||||
|
||||
assert(lws_socket_is_valid(wsi->desc.sockfd));
|
||||
|
||||
/* copy the fd */
|
||||
wnew->desc = wsi->desc;
|
||||
|
||||
assert(lws_socket_is_valid(wnew->desc.sockfd));
|
||||
|
||||
/* disconnect the fd from association with old wsi */
|
||||
|
||||
if (__remove_wsi_socket_from_fds(wsi))
|
||||
return -1;
|
||||
wsi->desc.sockfd = LWS_SOCK_INVALID;
|
||||
|
||||
/* point the fd table entry to new guy */
|
||||
|
||||
assert(lws_socket_is_valid(wnew->desc.sockfd));
|
||||
|
||||
if (__insert_wsi_socket_into_fds(wsi->context, wnew))
|
||||
return -1;
|
||||
|
||||
#if defined(LWS_WITH_TLS)
|
||||
/* pass on the tls */
|
||||
|
||||
wnew->tls = wsi->tls;
|
||||
wsi->tls.client_bio = NULL;
|
||||
wsi->tls.ssl = NULL;
|
||||
wsi->tls.use_ssl = 0;
|
||||
#endif
|
||||
|
||||
/* take over his copy of his endpoint as an active connection */
|
||||
|
||||
wnew->cli_hostname_copy = wsi->cli_hostname_copy;
|
||||
wsi->cli_hostname_copy = NULL;
|
||||
|
||||
|
||||
/*
|
||||
* selected queued guy now replaces the original leader on the
|
||||
* active client conn list
|
||||
*/
|
||||
|
||||
lws_dll2_remove(&wsi->dll_cli_active_conns);
|
||||
lws_dll2_add_tail(&wnew->dll_cli_active_conns,
|
||||
&wsi->vhost->dll_cli_active_conns_owner);
|
||||
|
||||
/* move any queued guys to queue on new active conn */
|
||||
|
||||
lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
|
||||
wsi->dll2_cli_txn_queue_owner.head) {
|
||||
struct lws *ww = lws_container_of(d, struct lws,
|
||||
dll2_cli_txn_queue);
|
||||
|
||||
lws_dll2_remove(&ww->dll2_cli_txn_queue);
|
||||
lws_dll2_add_tail(&ww->dll2_cli_txn_queue,
|
||||
&wnew->dll2_cli_txn_queue_owner);
|
||||
|
||||
} lws_end_foreach_dll_safe(d, d1);
|
||||
|
||||
lws_vhost_unlock(wsi->vhost);
|
||||
|
||||
/*
|
||||
* The original leader who passed on all his powers already can die...
|
||||
* in the call stack above us there are guys who still want to touch
|
||||
* him, so have him die next time around the event loop, not now.
|
||||
*/
|
||||
|
||||
wsi->already_did_cce = 1; /* so the close doesn't trigger a CCE */
|
||||
lws_set_timeout(wsi, 1, LWS_TO_KILL_ASYNC);
|
||||
|
||||
/* after the first one, they can only be coming from the queue */
|
||||
wnew->transaction_from_pipeline_queue = 1;
|
||||
|
||||
lwsl_notice("%s: pipeline queue passed wsi %p on to queued wsi %p\n",
|
||||
__func__, wsi, wnew);
|
||||
|
||||
*_wsi = wnew; /* inform caller we swapped */
|
||||
|
||||
return 1; /* new transaction */
|
||||
}
|
||||
#endif
|
||||
|
@ -1095,7 +1176,8 @@ lws_wsi_mux_mark_parents_needing_writeable(struct lws *wsi)
|
|||
wsi2 = wsi;
|
||||
while (wsi2) {
|
||||
wsi2->mux.requested_POLLOUT = 1;
|
||||
lwsl_info("mark %p pending writable\n", wsi2);
|
||||
lwsl_info("%s: mark %p (sid %u) pending writable\n", __func__,
|
||||
wsi2, wsi2->mux.my_sid);
|
||||
wsi2 = wsi2->mux.parent_wsi;
|
||||
}
|
||||
|
||||
|
@ -1237,9 +1319,11 @@ lws_wsi_mux_apply_queue(struct lws *wsi)
|
|||
dll2_cli_txn_queue);
|
||||
|
||||
if (lwsi_role_http(wsi) &&
|
||||
lwsi_state(w) == LRS_H1C_ISSUE_HANDSHAKE2) {
|
||||
lwsi_state(w) == LRS_H2_WAITING_TO_SEND_HEADERS) {
|
||||
lwsl_info("%s: cli pipeq %p to be h2\n", __func__, w);
|
||||
|
||||
lwsi_set_state(w, LRS_H1C_ISSUE_HANDSHAKE2);
|
||||
|
||||
/* remove ourselves from client queue */
|
||||
lws_dll2_remove(&w->dll2_cli_txn_queue);
|
||||
|
||||
|
|
|
@ -688,7 +688,7 @@ rops_handle_POLLIN_h1(struct lws_context_per_thread *pt, struct lws *wsi,
|
|||
return LWS_HPI_RET_PLEASE_CLOSE_ME;
|
||||
}
|
||||
|
||||
if (lws_client_socket_service(wsi, pollfd, NULL))
|
||||
if (lws_client_socket_service(wsi, pollfd))
|
||||
return LWS_HPI_RET_WSI_ALREADY_DIED;
|
||||
#endif
|
||||
|
||||
|
@ -1095,16 +1095,14 @@ static int
|
|||
rops_close_kill_connection_h1(struct lws *wsi, enum lws_close_status reason)
|
||||
{
|
||||
#if defined(LWS_WITH_HTTP_PROXY)
|
||||
struct lws *wsi_eff = lws_client_wsi_effective(wsi);
|
||||
|
||||
if (!wsi_eff->http.proxy_clientside)
|
||||
if (!wsi->http.proxy_clientside)
|
||||
return 0;
|
||||
|
||||
wsi_eff->http.proxy_clientside = 0;
|
||||
wsi->http.proxy_clientside = 0;
|
||||
|
||||
if (user_callback_handle_rxflow(wsi_eff->protocol->callback, wsi_eff,
|
||||
if (user_callback_handle_rxflow(wsi->protocol->callback, wsi,
|
||||
LWS_CALLBACK_COMPLETED_CLIENT_HTTP,
|
||||
wsi_eff->user_space, NULL, 0))
|
||||
wsi->user_space, NULL, 0))
|
||||
return 0;
|
||||
#endif
|
||||
return 0;
|
||||
|
|
|
@ -314,6 +314,11 @@ lws_wsi_h2_adopt(struct lws *parent_wsi, struct lws *wsi)
|
|||
#endif
|
||||
wsi->h2.initialized = 1;
|
||||
|
||||
if (!wsi->mux.my_sid) {
|
||||
wsi->mux.my_sid = nwsi->h2.h2n->highest_sid;
|
||||
nwsi->h2.h2n->highest_sid += 2;
|
||||
}
|
||||
|
||||
lws_wsi_mux_insert(wsi, parent_wsi, wsi->mux.my_sid);
|
||||
|
||||
wsi->txc.tx_cr = nwsi->h2.h2n->peer_set.s[H2SET_INITIAL_WINDOW_SIZE];
|
||||
|
@ -2392,7 +2397,8 @@ lws_h2_client_handshake(struct lws *wsi)
|
|||
#if defined(LWS_WITH_CLIENT)
|
||||
/* below is not needed in spec, indeed it destroys the long poll
|
||||
* feature, but required by nghttp2 */
|
||||
if (wsi->flags & LCCSCF_H2_QUIRK_NGHTTP2_END_STREAM)
|
||||
if ((wsi->flags & LCCSCF_H2_QUIRK_NGHTTP2_END_STREAM) &&
|
||||
!(wsi->client_http_body_pending))
|
||||
m |= LWS_WRITE_H2_STREAM_END;
|
||||
#endif
|
||||
|
||||
|
|
|
@ -132,7 +132,7 @@ rops_handle_POLLIN_h2(struct lws_context_per_thread *pt, struct lws *wsi,
|
|||
return LWS_HPI_RET_PLEASE_CLOSE_ME;
|
||||
}
|
||||
|
||||
n = lws_client_socket_service(wsi, pollfd, NULL);
|
||||
n = lws_client_socket_service(wsi, pollfd);
|
||||
if (n)
|
||||
return LWS_HPI_RET_WSI_ALREADY_DIED;
|
||||
#endif
|
||||
|
@ -647,14 +647,13 @@ rops_close_kill_connection_h2(struct lws *wsi, enum lws_close_status reason)
|
|||
|
||||
#if defined(LWS_WITH_HTTP_PROXY)
|
||||
if (wsi->http.proxy_clientside) {
|
||||
struct lws *wsi_eff = lws_client_wsi_effective(wsi);
|
||||
|
||||
wsi->http.proxy_clientside = 0;
|
||||
|
||||
if (user_callback_handle_rxflow(wsi_eff->protocol->callback,
|
||||
wsi_eff,
|
||||
if (user_callback_handle_rxflow(wsi->protocol->callback,
|
||||
wsi,
|
||||
LWS_CALLBACK_COMPLETED_CLIENT_HTTP,
|
||||
wsi_eff->user_space, NULL, 0))
|
||||
wsi->user_space, NULL, 0))
|
||||
wsi->http.proxy_clientside = 0;
|
||||
}
|
||||
#endif
|
||||
|
@ -732,12 +731,14 @@ rops_callback_on_writable_h2(struct lws *wsi)
|
|||
/* is this for DATA or for control messages? */
|
||||
|
||||
if (wsi->upgraded_to_http2 && !wsi->h2.h2n->pps &&
|
||||
lws_wsi_txc_check_skint(&wsi->txc, lws_h2_tx_cr_get(wsi)))
|
||||
lws_wsi_txc_check_skint(&wsi->txc, lws_h2_tx_cr_get(wsi))) {
|
||||
/*
|
||||
* refuse his efforts to get WRITABLE if we have no credit and
|
||||
* no non-DATA pps to send
|
||||
*/
|
||||
lwsl_err("%s: skint\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
network_wsi = lws_get_network_wsi(wsi);
|
||||
|
|
|
@ -166,9 +166,12 @@ send_hs:
|
|||
/*
|
||||
* We are pipelining on an already-established connection...
|
||||
* we can skip tls establishment.
|
||||
*
|
||||
* Set these queued guys to a state where they won't actually
|
||||
* send their headers until we decide later.
|
||||
*/
|
||||
|
||||
lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2);
|
||||
lwsi_set_state(wsi, LRS_H2_WAITING_TO_SEND_HEADERS);
|
||||
|
||||
/*
|
||||
* we can't send our headers directly, because they have to
|
||||
|
@ -760,12 +763,14 @@ lws_client_connect_2_dnsreq(struct lws *wsi)
|
|||
case ACTIVE_CONNS_MUXED:
|
||||
lwsl_notice("%s: ACTIVE_CONNS_MUXED\n", __func__);
|
||||
if (lwsi_role_h2(wsi)) {
|
||||
|
||||
if (wsi->protocol->callback(wsi,
|
||||
LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP,
|
||||
wsi->user_space, NULL, 0))
|
||||
goto failed1;
|
||||
|
||||
lwsi_set_state(wsi, LRS_H2_WAITING_TO_SEND_HEADERS);
|
||||
//lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2);
|
||||
//lwsi_set_state(w, LRS_ESTABLISHED);
|
||||
lws_callback_on_writable(wsi);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -30,48 +30,12 @@ lws_client_http_body_pending(struct lws *wsi, int something_left_to_send)
|
|||
wsi->client_http_body_pending = !!something_left_to_send;
|
||||
}
|
||||
|
||||
/*
|
||||
* return self, or queued client wsi we are acting on behalf of
|
||||
*
|
||||
* That is the TAIL of the queue (new queue elements are added at the HEAD)
|
||||
*/
|
||||
|
||||
struct lws *
|
||||
lws_client_wsi_effective(struct lws *wsi)
|
||||
{
|
||||
struct lws_dll2 *tail = lws_dll2_get_tail(&wsi->dll2_cli_txn_queue_owner);
|
||||
|
||||
if (!wsi->transaction_from_pipeline_queue || !tail)
|
||||
return wsi;
|
||||
|
||||
return lws_container_of(tail, struct lws, dll2_cli_txn_queue);
|
||||
}
|
||||
|
||||
/*
|
||||
* return self or the guy we are queued under
|
||||
*
|
||||
* REQUIRES VHOST LOCK HELD
|
||||
*/
|
||||
|
||||
static struct lws *
|
||||
_lws_client_wsi_master(struct lws *wsi)
|
||||
{
|
||||
struct lws_dll2_owner *o = wsi->dll2_cli_txn_queue.owner;
|
||||
|
||||
if (!o)
|
||||
return wsi;
|
||||
|
||||
return lws_container_of(o, struct lws, dll2_cli_txn_queue_owner);
|
||||
}
|
||||
|
||||
int
|
||||
lws_client_socket_service(struct lws *wsi, struct lws_pollfd *pollfd,
|
||||
struct lws *wsi_conn)
|
||||
lws_client_socket_service(struct lws *wsi, struct lws_pollfd *pollfd)
|
||||
{
|
||||
struct lws_context *context = wsi->context;
|
||||
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
||||
char *p = (char *)&pt->serv_buf[0];
|
||||
struct lws *w;
|
||||
#if defined(LWS_WITH_TLS)
|
||||
char ebuf[128];
|
||||
#endif
|
||||
|
@ -83,61 +47,6 @@ lws_client_socket_service(struct lws *wsi, struct lws_pollfd *pollfd,
|
|||
ssize_t len;
|
||||
#endif
|
||||
|
||||
if ((pollfd->revents & LWS_POLLOUT) &&
|
||||
wsi->keepalive_active &&
|
||||
wsi->dll2_cli_txn_queue_owner.head) {
|
||||
struct lws *wfound = NULL;
|
||||
|
||||
lwsl_debug("%s: pollout HANDSHAKE2\n", __func__);
|
||||
|
||||
/*
|
||||
* We have a transaction / stream queued that wants to pipeline.
|
||||
*
|
||||
* We have to allow it to send headers strictly in the order
|
||||
* that it was queued, ie, tail-first.
|
||||
*/
|
||||
lws_vhost_lock(wsi->vhost);
|
||||
lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
|
||||
wsi->dll2_cli_txn_queue_owner.head) {
|
||||
struct lws *w = lws_container_of(d, struct lws,
|
||||
dll2_cli_txn_queue);
|
||||
|
||||
lwsl_debug("%s: %p states 0x%lx\n", __func__, w,
|
||||
(unsigned long)w->wsistate);
|
||||
if (lwsi_state(w) == LRS_H1C_ISSUE_HANDSHAKE2)
|
||||
wfound = w;
|
||||
} lws_end_foreach_dll_safe(d, d1);
|
||||
|
||||
if (wfound) {
|
||||
#if defined(LWS_WITH_DETAILED_LATENCY)
|
||||
wfound->detlat.earliest_write_req_pre_write = lws_now_usecs();
|
||||
#endif
|
||||
/*
|
||||
* pollfd has the nwsi sockfd in it... but we're a
|
||||
* logical child stream sharing that... recurse with
|
||||
* both the correct child stream wsi and the nwsi.
|
||||
*
|
||||
* Second time around wsi / child stream wsi is not
|
||||
* going to trigger this as it has no pipelined queue
|
||||
* children of its own
|
||||
*/
|
||||
if (lws_client_socket_service(wfound, pollfd, wsi) < 0) {
|
||||
/* closed */
|
||||
|
||||
lws_vhost_unlock(wsi->vhost);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
lws_callback_on_writable(wsi);
|
||||
} else
|
||||
lwsl_debug("%s: nothing in txn q in HS2\n", __func__);
|
||||
|
||||
lws_vhost_unlock(wsi->vhost);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (lwsi_state(wsi)) {
|
||||
|
||||
case LRS_WAITING_DNS:
|
||||
|
@ -427,16 +336,14 @@ start_ws_handshake:
|
|||
|
||||
/* send our request to the server */
|
||||
|
||||
w = _lws_client_wsi_master(wsi);
|
||||
lwsl_info("%s: HANDSHAKE2: %p: sending headers on %p "
|
||||
"(wsistate 0x%lx 0x%lx), w sock %d, wsi sock %d\n",
|
||||
__func__, wsi, w, (unsigned long)wsi->wsistate,
|
||||
(unsigned long)w->wsistate, w->desc.sockfd,
|
||||
lwsl_info("%s: HANDSHAKE2: %p: sending headers "
|
||||
"(wsistate 0x%lx), w sock %d\n",
|
||||
__func__, wsi, (unsigned long)wsi->wsistate,
|
||||
wsi->desc.sockfd);
|
||||
#if defined(LWS_WITH_DETAILED_LATENCY)
|
||||
wsi->detlat.earliest_write_req_pre_write = lws_now_usecs();
|
||||
#endif
|
||||
n = lws_ssl_capable_write(w, (unsigned char *)sb, (int)(p - sb));
|
||||
n = lws_ssl_capable_write(wsi, (unsigned char *)sb, (int)(p - sb));
|
||||
switch (n) {
|
||||
case LWS_SSL_CAPABLE_ERROR:
|
||||
lwsl_debug("ERROR writing to client socket\n");
|
||||
|
@ -468,14 +375,14 @@ start_ws_handshake:
|
|||
lwsi_set_state(wsi, LRS_WAITING_SERVER_REPLY);
|
||||
wsi->hdr_parsing_completed = 0;
|
||||
|
||||
if (lwsi_state(w) == LRS_IDLING) {
|
||||
lwsi_set_state(w, LRS_WAITING_SERVER_REPLY);
|
||||
w->hdr_parsing_completed = 0;
|
||||
if (lwsi_state(wsi) == LRS_IDLING) {
|
||||
lwsi_set_state(wsi, LRS_WAITING_SERVER_REPLY);
|
||||
wsi->hdr_parsing_completed = 0;
|
||||
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
|
||||
w->http.ah->parser_state = WSI_TOKEN_NAME_PART;
|
||||
w->http.ah->lextable_pos = 0;
|
||||
wsi->http.ah->parser_state = WSI_TOKEN_NAME_PART;
|
||||
wsi->http.ah->lextable_pos = 0;
|
||||
#if defined(LWS_WITH_CUSTOM_HEADERS)
|
||||
w->http.ah->unk_pos = 0;
|
||||
wsi->http.ah->unk_pos = 0;
|
||||
#endif
|
||||
/* If we're (re)starting on hdr, need other implied init */
|
||||
wsi->http.ah->ues = URIES_IDLE;
|
||||
|
@ -485,7 +392,7 @@ start_ws_handshake:
|
|||
lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE,
|
||||
wsi->context->timeout_secs);
|
||||
|
||||
lws_callback_on_writable(w);
|
||||
lws_callback_on_writable(wsi);
|
||||
|
||||
goto client_http_body_sent;
|
||||
|
||||
|
@ -629,21 +536,25 @@ bail3:
|
|||
int LWS_WARN_UNUSED_RESULT
|
||||
lws_http_transaction_completed_client(struct lws *wsi)
|
||||
{
|
||||
struct lws *wsi_eff = lws_client_wsi_effective(wsi);
|
||||
int n;
|
||||
|
||||
lwsl_info("%s: wsi: %p, wsi_eff: %p (%s)\n", __func__, wsi, wsi_eff,
|
||||
wsi_eff->protocol->name);
|
||||
lwsl_info("%s: wsi: %p (%s)\n", __func__, wsi, wsi->protocol->name);
|
||||
|
||||
if (user_callback_handle_rxflow(wsi_eff->protocol->callback, wsi_eff,
|
||||
if (user_callback_handle_rxflow(wsi->protocol->callback, wsi,
|
||||
LWS_CALLBACK_COMPLETED_CLIENT_HTTP,
|
||||
wsi_eff->user_space, NULL, 0)) {
|
||||
wsi->user_space, NULL, 0)) {
|
||||
lwsl_debug("%s: Completed call returned nonzero (role 0x%lx)\n",
|
||||
__func__, (unsigned long)lwsi_role(wsi_eff));
|
||||
__func__, (unsigned long)lwsi_role(wsi));
|
||||
return -1;
|
||||
}
|
||||
|
||||
n = _lws_generic_transaction_completed_active_conn(wsi);
|
||||
wsi->http.rx_content_length = 0;
|
||||
|
||||
/*
|
||||
* For h1, wsi may pass some assets on to a queued child and be
|
||||
* destroyed during this.
|
||||
*/
|
||||
n = _lws_generic_transaction_completed_active_conn(&wsi);
|
||||
|
||||
if (wsi->http.ah) {
|
||||
if (wsi->client_mux_substream)
|
||||
|
@ -654,9 +565,9 @@ lws_http_transaction_completed_client(struct lws *wsi)
|
|||
*/
|
||||
__lws_header_table_detach(wsi, 0);
|
||||
else
|
||||
_lws_header_table_reset(wsi->http.ah);
|
||||
if (!n)
|
||||
_lws_header_table_reset(wsi->http.ah);
|
||||
}
|
||||
wsi->http.rx_content_length = 0;
|
||||
|
||||
if (!n || !wsi->http.ah)
|
||||
return 0;
|
||||
|
@ -680,30 +591,21 @@ lws_http_transaction_completed_client(struct lws *wsi)
|
|||
|
||||
/* If we're (re)starting on headers, need other implied init */
|
||||
wsi->http.ah->ues = URIES_IDLE;
|
||||
lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2);
|
||||
|
||||
lwsl_info("%s: %p: new queued transaction as %p\n", __func__, wsi,
|
||||
wsi_eff);
|
||||
lwsl_info("%s: %p: new queued transaction\n", __func__, wsi);
|
||||
lws_callback_on_writable(wsi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int
|
||||
lws_http_client_http_response(struct lws *_wsi)
|
||||
lws_http_client_http_response(struct lws *wsi)
|
||||
{
|
||||
struct lws *wsi;
|
||||
unsigned int resp = 0;
|
||||
if (wsi->http.ah && wsi->http.ah->http_response)
|
||||
return wsi->http.ah->http_response;
|
||||
|
||||
if (_wsi->http.ah && _wsi->http.ah->http_response)
|
||||
return _wsi->http.ah->http_response;
|
||||
|
||||
lws_vhost_lock(_wsi->vhost);
|
||||
wsi = _lws_client_wsi_master(_wsi);
|
||||
if (wsi->http.ah)
|
||||
resp = wsi->http.ah->http_response;
|
||||
lws_vhost_unlock(_wsi->vhost);
|
||||
|
||||
return resp;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -722,7 +624,6 @@ lws_client_interpret_server_handshake(struct lws *wsi)
|
|||
int close_reason = LWS_CLOSE_STATUS_PROTOCOL_ERR;
|
||||
const char *prot, *ads = NULL, *path, *cce = NULL;
|
||||
struct allocated_headers *ah, *ah1;
|
||||
struct lws *w = lws_client_wsi_effective(wsi);
|
||||
struct lws *nwsi = lws_get_network_wsi(wsi);
|
||||
char *p, *q;
|
||||
char new_path[300];
|
||||
|
@ -918,8 +819,8 @@ lws_client_interpret_server_handshake(struct lws *wsi)
|
|||
|
||||
/* if h1 KA is allowed, enable the queued pipeline guys */
|
||||
|
||||
if (!wsi->client_h2_alpn && !wsi->client_mux_substream &&
|
||||
w == wsi) { /* ie, coming to this for the first time */
|
||||
if (!wsi->client_h2_alpn && !wsi->client_mux_substream) {
|
||||
/* ie, coming to this for the first time */
|
||||
if (wsi->http.conn_type == HTTP_CONNECTION_KEEP_ALIVE)
|
||||
wsi->keepalive_active = 1;
|
||||
else {
|
||||
|
@ -1016,12 +917,12 @@ lws_client_interpret_server_handshake(struct lws *wsi)
|
|||
* we seem to be good to go, give client last chance to check
|
||||
* headers and OK it
|
||||
*/
|
||||
ah1 = w->http.ah;
|
||||
w->http.ah = ah;
|
||||
if (w->protocol->callback(w,
|
||||
ah1 = wsi->http.ah;
|
||||
wsi->http.ah = ah;
|
||||
if (wsi->protocol->callback(wsi,
|
||||
LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH,
|
||||
w->user_space, NULL, 0)) {
|
||||
w->http.ah = ah1;
|
||||
wsi->user_space, NULL, 0)) {
|
||||
wsi->http.ah = ah1;
|
||||
cce = "HS: disallowed by client filter";
|
||||
goto bail2;
|
||||
}
|
||||
|
@ -1032,24 +933,15 @@ lws_client_interpret_server_handshake(struct lws *wsi)
|
|||
wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;
|
||||
|
||||
/* call him back to inform him he is up */
|
||||
if (w->protocol->callback(w,
|
||||
if (wsi->protocol->callback(wsi,
|
||||
LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP,
|
||||
w->user_space, NULL, 0)) {
|
||||
w->http.ah = ah1;
|
||||
wsi->user_space, NULL, 0)) {
|
||||
wsi->http.ah = ah1;
|
||||
cce = "HS: disallowed at ESTABLISHED";
|
||||
goto bail3;
|
||||
}
|
||||
|
||||
w->http.ah = ah1;
|
||||
|
||||
/*
|
||||
* for pipelining, master needs to keep his ah... guys who
|
||||
* queued on him can drop it now though.
|
||||
*/
|
||||
|
||||
if (w != wsi)
|
||||
/* free up parsing allocations for queued guy */
|
||||
lws_header_table_detach(w, 0);
|
||||
wsi->http.ah = ah1;
|
||||
|
||||
lwsl_info("%s: client connection up\n", __func__);
|
||||
|
||||
|
@ -1404,13 +1296,15 @@ spin_chunks:
|
|||
lwsl_err("%s: chunking failure B\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
wsi->chunk_parser = ELCP_CONTENT;
|
||||
//lwsl_info("starting chunk size %d (block rem %d)\n",
|
||||
// wsi->chunk_remaining, *len);
|
||||
if (wsi->chunk_remaining)
|
||||
if (wsi->chunk_remaining) {
|
||||
wsi->chunk_parser = ELCP_CONTENT;
|
||||
//lwsl_notice("starting chunk size %d (block rem %d)\n",
|
||||
// wsi->chunk_remaining, *len);
|
||||
break;
|
||||
lwsl_info("final chunk\n");
|
||||
goto completed;
|
||||
}
|
||||
|
||||
wsi->chunk_parser = ELCP_TRAILER_CR;
|
||||
break;
|
||||
|
||||
case ELCP_CONTENT:
|
||||
break;
|
||||
|
@ -1436,6 +1330,32 @@ spin_chunks:
|
|||
wsi->chunk_parser = ELCP_HEX;
|
||||
wsi->chunk_remaining = 0;
|
||||
break;
|
||||
|
||||
case ELCP_TRAILER_CR:
|
||||
if ((*buf)[0] != '\x0d') {
|
||||
lwsl_err("%s: chunking failure F\n", __func__);
|
||||
lwsl_hexdump_err(*buf, *len);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
wsi->chunk_parser = ELCP_TRAILER_LF;
|
||||
break;
|
||||
|
||||
case ELCP_TRAILER_LF:
|
||||
if ((*buf)[0] != '\x0a') {
|
||||
lwsl_err("%s: chunking failure F\n", __func__);
|
||||
lwsl_hexdump_err(*buf, *len);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
(*buf)++;
|
||||
(*len)--;
|
||||
consumed++;
|
||||
|
||||
lwsl_info("final chunk\n");
|
||||
goto completed;
|
||||
}
|
||||
(*buf)++;
|
||||
(*len)--;
|
||||
|
@ -1462,19 +1382,17 @@ spin_chunks:
|
|||
else
|
||||
#endif
|
||||
{
|
||||
struct lws *wsi_eff = lws_client_wsi_effective(wsi);
|
||||
|
||||
if (
|
||||
#if defined(LWS_WITH_HTTP_PROXY)
|
||||
!wsi_eff->protocol_bind_balance ==
|
||||
!!wsi_eff->http.proxy_clientside
|
||||
!wsi->protocol_bind_balance ==
|
||||
!!wsi->http.proxy_clientside
|
||||
#else
|
||||
!!wsi_eff->protocol_bind_balance
|
||||
!!wsi->protocol_bind_balance
|
||||
#endif
|
||||
) {
|
||||
if (user_callback_handle_rxflow(wsi_eff->protocol->callback,
|
||||
wsi_eff, LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ,
|
||||
wsi_eff->user_space, *buf, n)) {
|
||||
if (user_callback_handle_rxflow(wsi->protocol->callback,
|
||||
wsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ,
|
||||
wsi->user_space, *buf, n)) {
|
||||
lwsl_info("%s: RECEIVE_CLIENT_HTTP_READ returned -1\n",
|
||||
__func__);
|
||||
|
||||
|
|
|
@ -1412,7 +1412,7 @@ set_parsing_complete:
|
|||
return LPR_OK;
|
||||
|
||||
forbid:
|
||||
lwsl_notice(" forbidding on uri sanitation\n");
|
||||
lwsl_info(" forbidding on uri sanitation\n");
|
||||
#if defined(LWS_WITH_SERVER)
|
||||
lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);
|
||||
#endif
|
||||
|
|
|
@ -283,6 +283,8 @@ enum lws_chunk_parser {
|
|||
ELCP_CONTENT,
|
||||
ELCP_POST_CR,
|
||||
ELCP_POST_LF,
|
||||
ELCP_TRAILER_CR,
|
||||
ELCP_TRAILER_LF
|
||||
};
|
||||
#endif
|
||||
|
||||
|
|
|
@ -111,7 +111,7 @@ try_pollout:
|
|||
}
|
||||
|
||||
#if defined(LWS_WITH_CLIENT)
|
||||
if (lws_client_socket_service(wsi, pollfd, NULL))
|
||||
if (lws_client_socket_service(wsi, pollfd))
|
||||
return LWS_HPI_RET_WSI_ALREADY_DIED;
|
||||
#endif
|
||||
|
||||
|
|
|
@ -989,7 +989,7 @@ rops_handle_POLLIN_ws(struct lws_context_per_thread *pt, struct lws *wsi,
|
|||
return LWS_HPI_RET_PLEASE_CLOSE_ME;
|
||||
}
|
||||
|
||||
n = lws_client_socket_service(wsi, pollfd, NULL);
|
||||
n = lws_client_socket_service(wsi, pollfd);
|
||||
if (n)
|
||||
return LWS_HPI_RET_WSI_ALREADY_DIED;
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
|
|
@ -1660,21 +1660,22 @@ _openssl_alpn_to_mbedtls(struct alpn_ctx *ac, char ***palpn_protos)
|
|||
|
||||
/* find out how many entries he gave us */
|
||||
|
||||
len = *p++;
|
||||
while (p - ac->data < ac->len) {
|
||||
if (len--) {
|
||||
p++;
|
||||
continue;
|
||||
}
|
||||
count++;
|
||||
if (ac->len) {
|
||||
len = *p++;
|
||||
if (!len)
|
||||
break;
|
||||
if (len)
|
||||
count++;
|
||||
while (p - ac->data < ac->len) {
|
||||
if (len--) {
|
||||
p++;
|
||||
continue;
|
||||
}
|
||||
len = *p++;
|
||||
if (!len)
|
||||
break;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!len)
|
||||
count++;
|
||||
|
||||
if (!count)
|
||||
return;
|
||||
|
||||
|
|
|
@ -229,7 +229,7 @@ lws_alpn_comma_to_openssl(const char *comma, uint8_t *os, int len)
|
|||
if (!comma)
|
||||
return 0;
|
||||
|
||||
while (*comma && len > 1) {
|
||||
while (*comma && len > 2) {
|
||||
if (!plen && *comma == ' ') {
|
||||
comma++;
|
||||
continue;
|
||||
|
@ -252,6 +252,8 @@ lws_alpn_comma_to_openssl(const char *comma, uint8_t *os, int len)
|
|||
if (plen)
|
||||
*plen = lws_ptr_diff(os, plen + 1);
|
||||
|
||||
*os = 0;
|
||||
|
||||
return lws_ptr_diff(os, oos);
|
||||
}
|
||||
|
||||
|
|
|
@ -25,3 +25,6 @@ Option|Meaning
|
|||
--uv|Use libuv event loop if lws built for it
|
||||
--event|Use libevent event loop if lws built for it
|
||||
--ev|Use libev event loop if lws built for it
|
||||
--post|POST to the server rather than GET
|
||||
-c<n>|Create n connections (n can be 1 .. 8)
|
||||
--path <path>|Force the URL path (should start with /)
|
|
@ -40,23 +40,33 @@ struct cliuser {
|
|||
int index;
|
||||
};
|
||||
|
||||
static int completed, failed, numbered, stagger_idx;
|
||||
static int completed, failed, numbered, stagger_idx, posting, count = COUNT;
|
||||
static lws_sorted_usec_list_t sul_stagger;
|
||||
static struct lws_client_connect_info i;
|
||||
static struct lws *client_wsi[COUNT];
|
||||
struct lws_context *context;
|
||||
static char urlpath[64];
|
||||
static struct lws_context *context;
|
||||
|
||||
/* we only need this for tracking POST emit state */
|
||||
|
||||
struct pss {
|
||||
char body_part;
|
||||
};
|
||||
|
||||
static int
|
||||
callback_http(struct lws *wsi, enum lws_callback_reasons reason,
|
||||
void *user, void *in, size_t len)
|
||||
{
|
||||
int idx = (int)(long)lws_get_opaque_user_data(wsi);
|
||||
char buf[LWS_PRE + 1024], *start = &buf[LWS_PRE], *p = start,
|
||||
*end = &buf[sizeof(buf) - LWS_PRE - 1];
|
||||
int n, idx = (int)(long)lws_get_opaque_user_data(wsi);
|
||||
struct pss *pss = (struct pss *)user;
|
||||
|
||||
switch (reason) {
|
||||
|
||||
case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP:
|
||||
lwsl_user("LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: resp %u\n",
|
||||
lws_http_client_http_response(wsi));
|
||||
lwsl_user("LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: idx: %d, resp %u\n",
|
||||
idx, lws_http_client_http_response(wsi));
|
||||
break;
|
||||
|
||||
/* because we are protocols[0] ... */
|
||||
|
@ -70,19 +80,21 @@ callback_http(struct lws *wsi, enum lws_callback_reasons reason,
|
|||
/* chunks of chunked content, with header removed */
|
||||
case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ:
|
||||
lwsl_user("RECEIVE_CLIENT_HTTP_READ: conn %d: read %d\n", idx, (int)len);
|
||||
#if 0 /* enable to dump the html */
|
||||
{
|
||||
const char *p = in;
|
||||
|
||||
while (len--)
|
||||
if (*p < 0x7f)
|
||||
putchar(*p++);
|
||||
else
|
||||
putchar('.');
|
||||
}
|
||||
#endif
|
||||
lwsl_hexdump_info(in, len);
|
||||
return 0; /* don't passthru */
|
||||
|
||||
case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
|
||||
/*
|
||||
* Tell lws we are going to send the body next...
|
||||
*/
|
||||
if (posting && !lws_http_is_redirected_to_get(wsi)) {
|
||||
lwsl_user("%s: doing POST flow\n", __func__);
|
||||
lws_client_http_body_pending(wsi, 1);
|
||||
lws_callback_on_writable(wsi);
|
||||
} else
|
||||
lwsl_user("%s: doing GET flow\n", __func__);
|
||||
break;
|
||||
|
||||
/* uninterpreted http content */
|
||||
case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
|
||||
{
|
||||
|
@ -115,6 +127,65 @@ callback_http(struct lws *wsi, enum lws_callback_reasons reason,
|
|||
}
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_CLIENT_HTTP_WRITEABLE:
|
||||
if (!posting)
|
||||
break;
|
||||
if (lws_http_is_redirected_to_get(wsi))
|
||||
break;
|
||||
lwsl_user("LWS_CALLBACK_CLIENT_HTTP_WRITEABLE: %p, part %d\n", wsi, pss->body_part);
|
||||
n = LWS_WRITE_HTTP;
|
||||
|
||||
/*
|
||||
* For a small body like this, we could prepare it in memory and
|
||||
* send it all at once. But to show how to handle, eg,
|
||||
* arbitrary-sized file payloads, or huge form-data fields, the
|
||||
* sending is done in multiple passes through the event loop.
|
||||
*/
|
||||
|
||||
switch (pss->body_part++) {
|
||||
case 0:
|
||||
if (lws_client_http_multipart(wsi, "text", NULL, NULL,
|
||||
&p, end))
|
||||
return -1;
|
||||
/* notice every usage of the boundary starts with -- */
|
||||
p += lws_snprintf(p, end - p, "my text field\xd\xa");
|
||||
break;
|
||||
case 1:
|
||||
if (lws_client_http_multipart(wsi, "file", "myfile.txt",
|
||||
"text/plain", &p, end))
|
||||
return -1;
|
||||
p += lws_snprintf(p, end - p,
|
||||
"This is the contents of the "
|
||||
"uploaded file.\xd\xa"
|
||||
"\xd\xa");
|
||||
break;
|
||||
case 2:
|
||||
if (lws_client_http_multipart(wsi, NULL, NULL, NULL,
|
||||
&p, end))
|
||||
return -1;
|
||||
lws_client_http_body_pending(wsi, 0);
|
||||
/* necessary to support H2, it means we will write no
|
||||
* more on this stream */
|
||||
n = LWS_WRITE_HTTP_FINAL;
|
||||
break;
|
||||
|
||||
default:
|
||||
/*
|
||||
* We can get extra callbacks here, if nothing to do,
|
||||
* then do nothing.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lws_write(wsi, (uint8_t *)start, lws_ptr_diff(p, start), n)
|
||||
!= lws_ptr_diff(p, start))
|
||||
return 1;
|
||||
|
||||
if (n != LWS_WRITE_HTTP_FINAL)
|
||||
lws_callback_on_writable(wsi);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -122,7 +193,7 @@ callback_http(struct lws *wsi, enum lws_callback_reasons reason,
|
|||
return lws_callback_http_dummy(wsi, reason, user, in, len);
|
||||
|
||||
finished:
|
||||
if (++completed == COUNT) {
|
||||
if (++completed == count) {
|
||||
if (!failed)
|
||||
lwsl_user("Done: all OK\n");
|
||||
else
|
||||
|
@ -140,7 +211,7 @@ finished:
|
|||
}
|
||||
|
||||
static const struct lws_protocols protocols[] = {
|
||||
{ "http", callback_http, 0, 0, },
|
||||
{ "http", callback_http, sizeof(struct pss), 0, },
|
||||
{ NULL, NULL, 0, 0 }
|
||||
};
|
||||
|
||||
|
@ -205,14 +276,14 @@ lws_try_client_connection(struct lws_client_connect_info *i, int m)
|
|||
lws_snprintf(path, sizeof(path), "/%d.png", m + 1);
|
||||
i->path = path;
|
||||
} else
|
||||
i->path = "/";
|
||||
i->path = urlpath;
|
||||
|
||||
i->pwsi = &client_wsi[m];
|
||||
i->opaque_user_data = (void *)(long)m;
|
||||
|
||||
if (!lws_client_connect_via_info(i)) {
|
||||
failed++;
|
||||
if (++completed == COUNT) {
|
||||
if (++completed == count) {
|
||||
lwsl_user("Done: failed: %d\n", failed);
|
||||
lws_context_destroy(context);
|
||||
}
|
||||
|
@ -233,11 +304,11 @@ stagger_cb(lws_sorted_usec_list_t *sul)
|
|||
*/
|
||||
lws_try_client_connection(&i, stagger_idx++);
|
||||
|
||||
if (stagger_idx == (int)LWS_ARRAY_SIZE(client_wsi))
|
||||
if (stagger_idx == count)
|
||||
return;
|
||||
|
||||
next = 300 * LWS_US_PER_MS;
|
||||
if (stagger_idx == (int)LWS_ARRAY_SIZE(client_wsi) - 1)
|
||||
if (stagger_idx == count - 1)
|
||||
next += 700 * LWS_US_PER_MS;
|
||||
|
||||
lws_sul_schedule(context, 0, &sul_stagger, stagger_cb, next);
|
||||
|
@ -280,7 +351,7 @@ int main(int argc, const char **argv)
|
|||
lws_set_log_level(logs, NULL);
|
||||
lwsl_user("LWS minimal http client [-s (staggered)] [-p (pipeline)]\n");
|
||||
lwsl_user(" [--h1 (http/1 only)] [-l (localhost)] [-d <logs>]\n");
|
||||
lwsl_user(" [-n (numbered)]\n");
|
||||
lwsl_user(" [-n (numbered)] [--post]\n");
|
||||
|
||||
info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */
|
||||
info.protocols = protocols;
|
||||
|
@ -317,6 +388,13 @@ int main(int argc, const char **argv)
|
|||
LCCSCF_H2_QUIRK_OVERFLOWS_TXCR |
|
||||
LCCSCF_H2_QUIRK_NGHTTP2_END_STREAM;
|
||||
|
||||
if (lws_cmdline_option(argc, argv, "--post")) {
|
||||
posting = 1;
|
||||
i.method = "POST";
|
||||
i.ssl_connection |= LCCSCF_HTTP_MULTIPART_MIME;
|
||||
} else
|
||||
i.method = "GET";
|
||||
|
||||
/* enables h1 or h2 connection sharing */
|
||||
if (lws_cmdline_option(argc, argv, "-p"))
|
||||
i.ssl_connection |= LCCSCF_PIPELINE;
|
||||
|
@ -325,13 +403,19 @@ int main(int argc, const char **argv)
|
|||
if (lws_cmdline_option(argc, argv, "--h1"))
|
||||
i.alpn = "http/1.1";
|
||||
|
||||
strcpy(urlpath, "/");
|
||||
|
||||
if (lws_cmdline_option(argc, argv, "-l")) {
|
||||
i.port = 7681;
|
||||
i.address = "localhost";
|
||||
i.ssl_connection |= LCCSCF_ALLOW_SELFSIGNED;
|
||||
if (posting)
|
||||
strcpy(urlpath, "/formtest");
|
||||
} else {
|
||||
i.port = 443;
|
||||
i.address = "warmcat.com";
|
||||
i.address = "libwebsockets.org";
|
||||
if (posting)
|
||||
strcpy(urlpath, "/testserver/formtest");
|
||||
}
|
||||
|
||||
if (lws_cmdline_option(argc, argv, "-n"))
|
||||
|
@ -343,12 +427,15 @@ int main(int argc, const char **argv)
|
|||
if ((p = lws_cmdline_option(argc, argv, "--port")))
|
||||
i.port = atoi(p);
|
||||
|
||||
if ((p = lws_cmdline_option(argc, argv, "--server")))
|
||||
i.address = p;
|
||||
if ((p = lws_cmdline_option(argc, argv, "--path")))
|
||||
lws_strncpy(urlpath, p, sizeof(urlpath));
|
||||
|
||||
if ((p = lws_cmdline_option(argc, argv, "-c")))
|
||||
if (atoi(p) <= COUNT && atoi(p))
|
||||
count = atoi(p);
|
||||
|
||||
i.host = i.address;
|
||||
i.origin = i.address;
|
||||
i.method = "GET";
|
||||
i.protocol = protocols[0].name;
|
||||
|
||||
if (!staggered)
|
||||
|
@ -356,7 +443,7 @@ int main(int argc, const char **argv)
|
|||
* just pile on all the connections at once, testing the
|
||||
* pipeline queuing before the first is connected
|
||||
*/
|
||||
for (m = 0; m < (int)LWS_ARRAY_SIZE(client_wsi); m++)
|
||||
for (m = 0; m < count; m++)
|
||||
lws_try_client_connection(&i, m);
|
||||
else
|
||||
/*
|
||||
|
@ -372,7 +459,7 @@ int main(int argc, const char **argv)
|
|||
lwsl_user("Duration: %lldms\n", (us() - start) / 1000);
|
||||
lws_context_destroy(context);
|
||||
|
||||
lwsl_user("Exiting with %d\n", failed || completed != COUNT);
|
||||
lwsl_user("Exiting with %d\n", failed || completed != count);
|
||||
|
||||
return failed || completed != COUNT;
|
||||
return failed || completed != count;
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
. $5/selftests-library.sh
|
||||
|
||||
COUNT_TESTS=16
|
||||
COUNT_TESTS=22
|
||||
|
||||
dotest $1 $2 warmcat
|
||||
dotest $1 $2 warmcat-pipe -p
|
||||
|
@ -28,6 +28,12 @@ dotest $1 $2 warmcat-stag -s
|
|||
dotest $1 $2 warmcat-pipe-stag -p -s
|
||||
dotest $1 $2 warmcat-h1-stag --h1 -s
|
||||
dotest $1 $2 warmcat-h1-pipe-stag --h1 -p -s
|
||||
dotest $1 $2 warmcat-post --post
|
||||
dotest $1 $2 warmcat-post-pipe --post -p
|
||||
dotest $1 $2 warmcat-post-pipe-stag --post -p -s
|
||||
dotest $1 $2 warmcat-h1-post --post --h1
|
||||
dotest $1 $2 warmcat-h1-post-pipe --post --h1 -p
|
||||
dotest $1 $2 warmcat-h1-post-pipe-stag --post --h1 -p -s
|
||||
|
||||
spawn "" $5/http-server/minimal-http-server-tls $1/lws-minimal-http-server-tls
|
||||
dotest $1 $2 localhost -l
|
||||
|
|
Loading…
Add table
Reference in a new issue