mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-09 00:00:04 +01:00
http: allow receiving body in case ws upgrade failed
Process HTTP headers related to content length for ws connections and make 1 callback before continuing to the ws upgrade code. This gives one last opportunity to ws protocols to inspect server reply before the ws upgrade code discard it. ie: download reply body in case of any other response code than 101.
This commit is contained in:
parent
eaab6e28b0
commit
b6824c88fd
2 changed files with 102 additions and 79 deletions
|
@ -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
|
||||
|
@ -850,6 +850,16 @@ lws_http_compression_apply(struct lws *wsi, const char *name,
|
|||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_http_is_redirected_to_get(struct lws *wsi);
|
||||
|
||||
/**
|
||||
* lws_http_client_http_error() - determine if the response code indicates an error
|
||||
*
|
||||
* \param code: the response code to test
|
||||
*
|
||||
* Returns nonzero if the code indicates an error, else zero if reflects a
|
||||
* non-error condition
|
||||
*/
|
||||
#define lws_http_client_http_resp_is_error(code) (!(code < 400))
|
||||
|
||||
/**
|
||||
* lws_h2_update_peer_txcredit() - manually update stream peer tx credit
|
||||
*
|
||||
|
|
|
@ -779,72 +779,113 @@ lws_client_interpret_server_handshake(struct lws *wsi)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (!wsi->do_ws) {
|
||||
/* if h1 KA is allowed, enable the queued pipeline guys */
|
||||
|
||||
/* if h1 KA is allowed, enable the queued pipeline guys */
|
||||
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 {
|
||||
/*
|
||||
* Ugh... now the main http connection has seen
|
||||
* both sides, we learn the server doesn't
|
||||
* support keepalive.
|
||||
*
|
||||
* That means any guys queued on us are going
|
||||
* to have to be restarted from connect2 with
|
||||
* their own connections.
|
||||
*/
|
||||
|
||||
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 {
|
||||
/*
|
||||
* Ugh... now the main http connection has seen
|
||||
* both sides, we learn the server doesn't
|
||||
* support keepalive.
|
||||
*
|
||||
* That means any guys queued on us are going
|
||||
* to have to be restarted from connect2 with
|
||||
* their own connections.
|
||||
*/
|
||||
/*
|
||||
* stick around telling any new guys they can't
|
||||
* pipeline to this server
|
||||
*/
|
||||
wsi->keepalive_rejected = 1;
|
||||
|
||||
/*
|
||||
* stick around telling any new guys they can't
|
||||
* pipeline to this server
|
||||
*/
|
||||
wsi->keepalive_rejected = 1;
|
||||
lws_vhost_lock(wsi->vhost);
|
||||
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_vhost_lock(wsi->vhost);
|
||||
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);
|
||||
/* remove him from our queue */
|
||||
lws_dll2_remove(&ww->dll2_cli_txn_queue);
|
||||
/* give up on pipelining */
|
||||
ww->client_pipeline = 0;
|
||||
|
||||
/* remove him from our queue */
|
||||
lws_dll2_remove(&ww->dll2_cli_txn_queue);
|
||||
/* give up on pipelining */
|
||||
ww->client_pipeline = 0;
|
||||
|
||||
/* go back to "trying to connect" state */
|
||||
lws_role_transition(ww, LWSIFR_CLIENT,
|
||||
LRS_UNCONNECTED,
|
||||
/* go back to "trying to connect" state */
|
||||
lws_role_transition(ww, LWSIFR_CLIENT,
|
||||
LRS_UNCONNECTED,
|
||||
#if defined(LWS_ROLE_H1)
|
||||
&role_ops_h1);
|
||||
&role_ops_h1);
|
||||
#else
|
||||
#if defined (LWS_ROLE_H2)
|
||||
&role_ops_h2);
|
||||
&role_ops_h2);
|
||||
#else
|
||||
&role_ops_raw);
|
||||
&role_ops_raw);
|
||||
#endif
|
||||
#endif
|
||||
ww->user_space = NULL;
|
||||
} lws_end_foreach_dll_safe(d, d1);
|
||||
lws_vhost_unlock(wsi->vhost);
|
||||
}
|
||||
ww->user_space = NULL;
|
||||
} lws_end_foreach_dll_safe(d, d1);
|
||||
lws_vhost_unlock(wsi->vhost);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef LWS_WITH_HTTP_PROXY
|
||||
wsi->http.perform_rewrite = 0;
|
||||
if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE)) {
|
||||
if (!strncmp(lws_hdr_simple_ptr(wsi,
|
||||
WSI_TOKEN_HTTP_CONTENT_TYPE),
|
||||
"text/html", 9))
|
||||
wsi->http.perform_rewrite = 0;
|
||||
}
|
||||
wsi->http.perform_rewrite = 0;
|
||||
if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE)) {
|
||||
if (!strncmp(lws_hdr_simple_ptr(wsi,
|
||||
WSI_TOKEN_HTTP_CONTENT_TYPE),
|
||||
"text/html", 9))
|
||||
wsi->http.perform_rewrite = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* he may choose to send us stuff in chunked transfer-coding */
|
||||
wsi->chunked = 0;
|
||||
wsi->chunk_remaining = 0; /* ie, next thing is chunk size */
|
||||
if (lws_hdr_total_length(wsi,
|
||||
WSI_TOKEN_HTTP_TRANSFER_ENCODING)) {
|
||||
wsi->chunked = !strcmp(lws_hdr_simple_ptr(wsi,
|
||||
WSI_TOKEN_HTTP_TRANSFER_ENCODING),
|
||||
"chunked");
|
||||
/* first thing is hex, after payload there is crlf */
|
||||
wsi->chunk_parser = ELCP_HEX;
|
||||
}
|
||||
|
||||
wsi->http.content_length_given = 0;
|
||||
if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
|
||||
wsi->http.rx_content_length =
|
||||
atoll(lws_hdr_simple_ptr(wsi,
|
||||
WSI_TOKEN_HTTP_CONTENT_LENGTH));
|
||||
lwsl_info("%s: incoming content length %llu\n",
|
||||
__func__, (unsigned long long)
|
||||
wsi->http.rx_content_length);
|
||||
wsi->http.rx_content_remain =
|
||||
wsi->http.rx_content_length;
|
||||
wsi->http.content_length_given = 1;
|
||||
} else { /* can't do 1.1 without a content length or chunked */
|
||||
if (!wsi->chunked)
|
||||
wsi->http.conn_type = HTTP_CONNECTION_CLOSE;
|
||||
lwsl_debug("%s: no content length\n", __func__);
|
||||
}
|
||||
|
||||
if (wsi->do_ws) {
|
||||
/*
|
||||
* Give one last opportunity to ws protocols to inspect server reply
|
||||
* before the ws upgrade code discard it. ie: download reply body in case
|
||||
* of any other response code than 101.
|
||||
*/
|
||||
if (wsi->protocol->callback(wsi,
|
||||
LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP,
|
||||
wsi->user_space, NULL, 0)) {
|
||||
|
||||
cce = "HS: disallowed by client filter";
|
||||
goto bail2;
|
||||
}
|
||||
} else {
|
||||
/* allocate the per-connection user memory (if any) */
|
||||
if (lws_ensure_user_space(wsi)) {
|
||||
lwsl_err("Problem allocating wsi user mem\n");
|
||||
|
@ -852,34 +893,6 @@ lws_client_interpret_server_handshake(struct lws *wsi)
|
|||
goto bail2;
|
||||
}
|
||||
|
||||
/* he may choose to send us stuff in chunked transfer-coding */
|
||||
wsi->chunked = 0;
|
||||
wsi->chunk_remaining = 0; /* ie, next thing is chunk size */
|
||||
if (lws_hdr_total_length(wsi,
|
||||
WSI_TOKEN_HTTP_TRANSFER_ENCODING)) {
|
||||
wsi->chunked = !strcmp(lws_hdr_simple_ptr(wsi,
|
||||
WSI_TOKEN_HTTP_TRANSFER_ENCODING),
|
||||
"chunked");
|
||||
/* first thing is hex, after payload there is crlf */
|
||||
wsi->chunk_parser = ELCP_HEX;
|
||||
}
|
||||
|
||||
wsi->http.content_length_given = 0;
|
||||
if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
|
||||
wsi->http.rx_content_length =
|
||||
atoll(lws_hdr_simple_ptr(wsi,
|
||||
WSI_TOKEN_HTTP_CONTENT_LENGTH));
|
||||
lwsl_info("%s: incoming content length %llu\n",
|
||||
__func__, (unsigned long long)
|
||||
wsi->http.rx_content_length);
|
||||
wsi->http.rx_content_remain =
|
||||
wsi->http.rx_content_length;
|
||||
wsi->http.content_length_given = 1;
|
||||
} else { /* can't do 1.1 without a content length or chunked */
|
||||
if (!wsi->chunked)
|
||||
wsi->http.conn_type = HTTP_CONNECTION_CLOSE;
|
||||
lwsl_debug("%s: no content length\n", __func__);
|
||||
}
|
||||
|
||||
/*
|
||||
* we seem to be good to go, give client last chance to check
|
||||
|
|
Loading…
Add table
Reference in a new issue