mirror of
https://libwebsockets.org/repo/libwebsockets
synced 2024-11-24 09:46:44 +00:00
957503436e
Provide a way to apply exception mount urls that exist on top of a larger mount, but provide an exception which enforces the url to not be serviced by the mount code, but by whatever dynamic handler is in place.
1234 lines
32 KiB
C
1234 lines
32 KiB
C
/*
|
|
* libwebsockets - small server side websockets and web server implementation
|
|
*
|
|
* Copyright (C) 2019 - 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
|
|
* deal in the Software without restriction, including without limitation the
|
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
* sell copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
* IN THE SOFTWARE.
|
|
*
|
|
* This is the glue that wires up h1 to Secure Streams.
|
|
*/
|
|
|
|
#include <private-lib-core.h>
|
|
|
|
#if !defined(LWS_PLAT_FREERTOS) || defined(LWS_ROLE_H2)
|
|
#define LWS_WITH_SS_RIDESHARE
|
|
#endif
|
|
|
|
#if defined(LWS_WITH_SS_RIDESHARE)
|
|
static int
|
|
ss_http_multipart_parser(lws_ss_handle_t *h, void *in, size_t len)
|
|
{
|
|
uint8_t *q = (uint8_t *)in;
|
|
int pending_issue = 0, n = 0;
|
|
|
|
|
|
/* let's stick it in the boundary state machine first */
|
|
while (n < (int)len) {
|
|
if (h->u.http.boundary_seq != h->u.http.boundary_len) {
|
|
if (q[n] == h->u.http.boundary[h->u.http.boundary_seq])
|
|
h->u.http.boundary_seq++;
|
|
else {
|
|
h->u.http.boundary_seq = 0;
|
|
h->u.http.boundary_dashes = 0;
|
|
h->u.http.boundary_post = 0;
|
|
}
|
|
goto around;
|
|
}
|
|
|
|
/*
|
|
* We already matched the boundary string, now we're
|
|
* looking if there's a -- afterwards
|
|
*/
|
|
if (h->u.http.boundary_dashes < 2) {
|
|
if (q[n] == '-') {
|
|
h->u.http.boundary_dashes++;
|
|
goto around;
|
|
}
|
|
/* there was no final -- ... */
|
|
}
|
|
|
|
if (h->u.http.boundary_dashes == 2) {
|
|
/*
|
|
* It's an EOM boundary: issue pending + multipart EOP
|
|
*/
|
|
lwsl_debug("%s: seen EOP, n %d pi %d\n",
|
|
__func__, n, pending_issue);
|
|
/*
|
|
* It's possible we already started the decode before
|
|
* the end of the last packet. Then there is no
|
|
* remainder to send.
|
|
*/
|
|
if (n >= pending_issue + h->u.http.boundary_len +
|
|
(h->u.http.any ? 2 : 0) + 1) {
|
|
h->info.rx(ss_to_userobj(h),
|
|
&q[pending_issue],
|
|
(unsigned int)(n - pending_issue -
|
|
h->u.http.boundary_len - 1 -
|
|
(h->u.http.any ? 2 : 0) /* crlf */),
|
|
(!h->u.http.som ? LWSSS_FLAG_SOM : 0) |
|
|
LWSSS_FLAG_EOM | LWSSS_FLAG_RELATED_END);
|
|
h->u.http.eom = 1;
|
|
}
|
|
|
|
/*
|
|
* Peer may not END_STREAM us
|
|
*/
|
|
return 0;
|
|
//return -1;
|
|
}
|
|
|
|
/* how about --boundaryCRLF */
|
|
|
|
if (h->u.http.boundary_post < 2) {
|
|
if ((!h->u.http.boundary_post && q[n] == '\x0d') ||
|
|
(h->u.http.boundary_post && q[n] == '\x0a')) {
|
|
h->u.http.boundary_post++;
|
|
goto around;
|
|
}
|
|
/* there was no final CRLF ... it's wrong */
|
|
|
|
return -1;
|
|
}
|
|
if (h->u.http.boundary_post != 2)
|
|
goto around;
|
|
|
|
/*
|
|
* We have a starting "--boundaryCRLF" or intermediate
|
|
* "CRLF--boundaryCRLF" boundary
|
|
*/
|
|
lwsl_debug("%s: b_post = 2 (pi %d)\n", __func__, pending_issue);
|
|
h->u.http.boundary_seq = 0;
|
|
h->u.http.boundary_post = 0;
|
|
|
|
if (n >= pending_issue && (h->u.http.any || !h->u.http.som)) {
|
|
/* Intermediate... do the EOM */
|
|
lwsl_debug("%s: seen interm EOP n %d pi %d\n", __func__,
|
|
n, pending_issue);
|
|
/*
|
|
* It's possible we already started the decode before
|
|
* the end of the last packet. Then there is no
|
|
* remainder to send.
|
|
*/
|
|
if (n >= pending_issue + h->u.http.boundary_len +
|
|
(h->u.http.any ? 2 : 0)) {
|
|
h->info.rx(ss_to_userobj(h), &q[pending_issue],
|
|
(unsigned int)(n - pending_issue -
|
|
h->u.http.boundary_len -
|
|
(h->u.http.any ? 2 /* crlf */ : 0)),
|
|
(!h->u.http.som ? LWSSS_FLAG_SOM : 0) |
|
|
LWSSS_FLAG_EOM);
|
|
h->u.http.eom = 1;
|
|
}
|
|
}
|
|
|
|
/* Next message starts after this boundary */
|
|
|
|
pending_issue = n;
|
|
if (h->u.http.eom) {
|
|
/* reset only if we have sent eom */
|
|
h->u.http.som = 0;
|
|
h->u.http.eom = 0;
|
|
}
|
|
|
|
around:
|
|
n++;
|
|
}
|
|
|
|
if (pending_issue != n) {
|
|
uint8_t oh = 0;
|
|
|
|
/*
|
|
* handle the first or last "--boundaryCRLF" case which is not captured in the
|
|
* previous loop, on the Bob downchannel (/directive)
|
|
*
|
|
* probably does not cover the case that one boundary term is separated in multipile
|
|
* one callbacks though never see such case
|
|
*/
|
|
|
|
if ((n >= h->u.http.boundary_len) &&
|
|
h->u.http.boundary_seq == h->u.http.boundary_len &&
|
|
h->u.http.boundary_post == 2) {
|
|
|
|
oh = 1;
|
|
}
|
|
|
|
h->info.rx(ss_to_userobj(h), &q[pending_issue],
|
|
(unsigned int)(oh ?
|
|
(n - pending_issue - h->u.http.boundary_len -
|
|
(h->u.http.any ? 2 : 0)) :
|
|
(n - pending_issue)),
|
|
(!h->u.http.som ? LWSSS_FLAG_SOM : 0) |
|
|
(oh && h->u.http.any ? LWSSS_FLAG_EOM : 0));
|
|
|
|
if (oh && h->u.http.any)
|
|
h->u.http.eom = 1;
|
|
|
|
h->u.http.any = 1;
|
|
h->u.http.som = 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Returns 0, or the ss state resp maps on to
|
|
*/
|
|
|
|
static int
|
|
lws_ss_http_resp_to_state(lws_ss_handle_t *h, int resp)
|
|
{
|
|
const lws_ss_http_respmap_t *r = h->policy->u.http.respmap;
|
|
int n = h->policy->u.http.count_respmap;
|
|
|
|
while (n--)
|
|
if (resp == r->resp)
|
|
return r->state;
|
|
else
|
|
r++;
|
|
|
|
return 0; /* no hit */
|
|
}
|
|
|
|
/*
|
|
* This converts any set metadata items into outgoing http headers
|
|
*/
|
|
|
|
static int
|
|
lws_apply_metadata(lws_ss_handle_t *h, struct lws *wsi, uint8_t *buf,
|
|
uint8_t **pp, uint8_t *end)
|
|
{
|
|
lws_ss_metadata_t *polmd = h->policy->metadata;
|
|
int m = 0;
|
|
|
|
while (polmd) {
|
|
|
|
/* has to have a non-empty header string */
|
|
|
|
if (polmd->value__may_own_heap &&
|
|
((uint8_t *)polmd->value__may_own_heap)[0] &&
|
|
h->metadata[m].value__may_own_heap) {
|
|
if (lws_add_http_header_by_name(wsi,
|
|
polmd->value__may_own_heap,
|
|
h->metadata[m].value__may_own_heap,
|
|
(int)h->metadata[m].length, pp, end))
|
|
return -1;
|
|
|
|
/*
|
|
* Check for the case he's setting a non-zero
|
|
* content-length "via the backdoor" metadata-
|
|
* driven headers, and set the body_pending()
|
|
* state if so...
|
|
*/
|
|
|
|
if (!strncmp(polmd->value__may_own_heap,
|
|
"content-length", 14) &&
|
|
atoi(h->metadata[m].value__may_own_heap))
|
|
lws_client_http_body_pending(wsi, 1);
|
|
}
|
|
|
|
m++;
|
|
polmd = polmd->next;
|
|
}
|
|
|
|
/*
|
|
* Content-length on POST / PUT / PATCH if we have the length information
|
|
*/
|
|
|
|
if (h->policy->u.http.method && (
|
|
(!strcmp(h->policy->u.http.method, "POST") ||
|
|
!strcmp(h->policy->u.http.method, "PATCH") ||
|
|
!strcmp(h->policy->u.http.method, "PUT"))) &&
|
|
wsi->http.writeable_len) {
|
|
if (!(h->policy->flags &
|
|
LWSSSPOLF_HTTP_NO_CONTENT_LENGTH)) {
|
|
int n = lws_snprintf((char *)buf, 20, "%u",
|
|
(unsigned int)wsi->http.writeable_len);
|
|
if (lws_add_http_header_by_token(wsi,
|
|
WSI_TOKEN_HTTP_CONTENT_LENGTH,
|
|
buf, n, pp, end))
|
|
return -1;
|
|
}
|
|
lws_client_http_body_pending(wsi, 1);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
#if defined(LWS_WITH_SS_DIRECT_PROTOCOL_STR)
|
|
static int
|
|
lws_apply_instant_metadata(lws_ss_handle_t *h, struct lws *wsi, uint8_t *buf,
|
|
uint8_t **pp, uint8_t *end)
|
|
{
|
|
lws_ss_metadata_t *imd = h->instant_metadata;
|
|
|
|
while (imd) {
|
|
if (imd->name && imd->value__may_own_heap) {
|
|
lwsl_debug("%s add header %s %s %d\n", __func__,
|
|
imd->name,
|
|
(char *)imd->value__may_own_heap,
|
|
(int)imd->length);
|
|
if (lws_add_http_header_by_name(wsi,
|
|
(const unsigned char *)imd->name,
|
|
(const unsigned char *)imd->value__may_own_heap,
|
|
(int)imd->length, pp, end))
|
|
return -1;
|
|
|
|
/* it's possible user set content-length directly */
|
|
if (!strncmp(imd->name,
|
|
"content-length", 14) &&
|
|
atoi(imd->value__may_own_heap))
|
|
lws_client_http_body_pending(wsi, 1);
|
|
|
|
}
|
|
|
|
imd = imd->next;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
/*
|
|
* Check if any metadata headers present in the server headers, and record
|
|
* them into the associated metadata item if so.
|
|
*/
|
|
|
|
static int
|
|
lws_extract_metadata(lws_ss_handle_t *h, struct lws *wsi)
|
|
{
|
|
lws_ss_metadata_t *polmd = h->policy->metadata, *omd;
|
|
int n;
|
|
|
|
while (polmd) {
|
|
|
|
if (polmd->value_is_http_token != LWS_HTTP_NO_KNOWN_HEADER) {
|
|
|
|
/* it's a well-known header token */
|
|
|
|
n = lws_hdr_total_length(wsi, polmd->value_is_http_token);
|
|
if (n) {
|
|
const char *cp = lws_hdr_simple_ptr(wsi,
|
|
polmd->value_is_http_token);
|
|
omd = lws_ss_get_handle_metadata(h, polmd->name);
|
|
if (!omd || !cp)
|
|
return 1;
|
|
|
|
assert(!strcmp(omd->name, polmd->name));
|
|
|
|
/*
|
|
* it's present on the wsi, we want to
|
|
* set the related metadata name to it then
|
|
*/
|
|
|
|
_lws_ss_alloc_set_metadata(omd, polmd->name, cp,
|
|
(unsigned int)n);
|
|
|
|
#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)
|
|
/*
|
|
* ...and because we are doing it from parsing
|
|
* onward rx, we want to mark the metadata as
|
|
* needing passing to the client
|
|
*/
|
|
omd->pending_onward = 1;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#if defined(LWS_WITH_CUSTOM_HEADERS)
|
|
else
|
|
|
|
/* has to have a non-empty header string */
|
|
|
|
if (polmd->value__may_own_heap &&
|
|
((uint8_t *)polmd->value__may_own_heap)[0]) {
|
|
char *p;
|
|
|
|
/*
|
|
* Can it be a custom header?
|
|
*/
|
|
|
|
n = lws_hdr_custom_length(wsi, (const char *)
|
|
polmd->value__may_own_heap,
|
|
polmd->value_length);
|
|
if (n > 0) {
|
|
|
|
p = lws_malloc((unsigned int)n + 1, __func__);
|
|
if (!p)
|
|
return 1;
|
|
|
|
/* if needed, free any previous value */
|
|
|
|
if (polmd->value_on_lws_heap) {
|
|
lws_free(
|
|
polmd->value__may_own_heap);
|
|
polmd->value_on_lws_heap = 0;
|
|
}
|
|
|
|
/*
|
|
* copy the named custom header value
|
|
* into the malloc'd buffer
|
|
*/
|
|
|
|
if (lws_hdr_custom_copy(wsi, p, n + 1,
|
|
(const char *)
|
|
polmd->value__may_own_heap,
|
|
polmd->value_length) < 0) {
|
|
lws_free(p);
|
|
|
|
return 1;
|
|
}
|
|
|
|
omd = lws_ss_get_handle_metadata(h,
|
|
polmd->name);
|
|
if (omd) {
|
|
|
|
_lws_ss_set_metadata(omd,
|
|
polmd->name, p, (size_t)n);
|
|
omd->value_on_lws_heap = 1;
|
|
|
|
#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)
|
|
omd->pending_onward = 1;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
polmd = polmd->next;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const uint8_t blob_idx[] = {
|
|
LWS_SYSBLOB_TYPE_AUTH,
|
|
LWS_SYSBLOB_TYPE_DEVICE_SERIAL,
|
|
LWS_SYSBLOB_TYPE_DEVICE_FW_VERSION,
|
|
LWS_SYSBLOB_TYPE_DEVICE_TYPE,
|
|
};
|
|
|
|
int
|
|
secstream_h1(struct lws *wsi, enum lws_callback_reasons reason, void *user,
|
|
void *in, size_t len)
|
|
{
|
|
#if defined(LWS_WITH_SERVER)
|
|
struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
|
|
#endif
|
|
lws_ss_handle_t *h = (lws_ss_handle_t *)lws_get_opaque_user_data(wsi);
|
|
uint8_t buf[LWS_PRE + 1520], *p = &buf[LWS_PRE],
|
|
#if defined(LWS_WITH_SERVER)
|
|
*start = p,
|
|
#endif
|
|
*end = &buf[sizeof(buf) - 1];
|
|
lws_ss_state_return_t r;
|
|
int f = 0, m, status;
|
|
char conceal_eom = 0;
|
|
lws_usec_t inter;
|
|
size_t buflen;
|
|
|
|
switch (reason) {
|
|
|
|
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
|
|
if (!h) {
|
|
lwsl_err("%s: CCE with no ss handle %s\n", __func__, lws_wsi_tag(wsi));
|
|
break;
|
|
}
|
|
|
|
lws_ss_assert_extant(wsi->a.context, wsi->tsi, h);
|
|
|
|
assert(h->policy);
|
|
|
|
#if defined(LWS_WITH_CONMON)
|
|
lws_conmon_ss_json(h);
|
|
#endif
|
|
|
|
lws_metrics_caliper_report_hist(h->cal_txn, wsi);
|
|
lwsl_info("%s: %s CLIENT_CONNECTION_ERROR: %s\n", __func__,
|
|
h->lc.gutag, in ? (const char *)in : "none");
|
|
if (h->ss_dangling_connected) {
|
|
/* already disconnected, no action for DISCONNECT_ME */
|
|
r = lws_ss_event_helper(h, LWSSSCS_DISCONNECTED);
|
|
if (r != LWSSSSRET_OK)
|
|
return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h);
|
|
} else {
|
|
/* already disconnected, no action for DISCONNECT_ME */
|
|
r = lws_ss_event_helper(h, LWSSSCS_UNREACHABLE);
|
|
if (r) {
|
|
if (h->inside_connect) {
|
|
h->pending_ret = r;
|
|
break;
|
|
}
|
|
|
|
return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h);
|
|
}
|
|
}
|
|
|
|
h->wsi = NULL;
|
|
r = lws_ss_backoff(h);
|
|
if (r != LWSSSSRET_OK) {
|
|
if (h->inside_connect) {
|
|
h->pending_ret = r;
|
|
break;
|
|
}
|
|
return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h);
|
|
}
|
|
break;
|
|
|
|
case LWS_CALLBACK_CLIENT_HTTP_REDIRECT:
|
|
|
|
if (!h)
|
|
return -1;
|
|
|
|
if (h->policy->u.http.fail_redirect)
|
|
lws_system_cpd_set(lws_get_context(wsi),
|
|
LWS_CPD_CAPTIVE_PORTAL);
|
|
/* unless it's explicitly allowed, reject to follow it */
|
|
return !(h->policy->flags & LWSSSPOLF_ALLOW_REDIRECTS);
|
|
|
|
case LWS_CALLBACK_CLOSED_HTTP: /* server */
|
|
case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
|
|
if (!h)
|
|
break;
|
|
|
|
lws_sul_cancel(&h->sul_timeout);
|
|
|
|
lws_ss_assert_extant(wsi->a.context, wsi->tsi, h);
|
|
|
|
#if defined(LWS_WITH_CONMON)
|
|
if (wsi->conmon.pcol == LWSCONMON_PCOL_NONE) {
|
|
wsi->conmon.pcol = LWSCONMON_PCOL_HTTP;
|
|
wsi->conmon.protocol_specific.http.response =
|
|
(int)lws_http_client_http_response(wsi);
|
|
}
|
|
|
|
lws_conmon_ss_json(h);
|
|
#endif
|
|
|
|
lws_metrics_caliper_report_hist(h->cal_txn, wsi);
|
|
//lwsl_notice("%s: %s LWS_CALLBACK_CLOSED_CLIENT_HTTP\n",
|
|
// __func__, wsi->lc.gutag);
|
|
|
|
h->wsi = NULL;
|
|
h->hanging_som = 0;
|
|
h->subseq = 0;
|
|
|
|
#if defined(LWS_WITH_SERVER)
|
|
lws_pt_lock(pt, __func__);
|
|
lws_dll2_remove(&h->cli_list);
|
|
lws_pt_unlock(pt);
|
|
#endif
|
|
|
|
if (h->policy && !(h->policy->flags & LWSSSPOLF_OPPORTUNISTIC) &&
|
|
#if defined(LWS_WITH_SERVER)
|
|
!(h->info.flags & LWSSSINFLAGS_ACCEPTED) && /* not server */
|
|
#endif
|
|
!h->txn_ok && !wsi->a.context->being_destroyed) {
|
|
r = lws_ss_backoff(h);
|
|
if (r != LWSSSSRET_OK)
|
|
return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h);
|
|
break;
|
|
} else
|
|
h->seqstate = SSSEQ_IDLE;
|
|
|
|
if (h->ss_dangling_connected) {
|
|
/* already disconnected, no action for DISCONNECT_ME */
|
|
r = lws_ss_event_helper(h, LWSSSCS_DISCONNECTED);
|
|
if (r == LWSSSSRET_DESTROY_ME)
|
|
return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h);
|
|
}
|
|
break;
|
|
|
|
case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP:
|
|
|
|
if (!h)
|
|
return -1;
|
|
|
|
lws_ss_assert_extant(wsi->a.context, wsi->tsi, h);
|
|
h->wsi = wsi; /* since we accept the wsi is bound to the SS,
|
|
* ensure the SS feels the same way about the wsi */
|
|
|
|
#if defined(LWS_WITH_CONMON)
|
|
if (wsi->conmon.pcol == LWSCONMON_PCOL_NONE) {
|
|
wsi->conmon.pcol = LWSCONMON_PCOL_HTTP;
|
|
wsi->conmon.protocol_specific.http.response =
|
|
(int)lws_http_client_http_response(wsi);
|
|
}
|
|
|
|
lws_conmon_ss_json(h);
|
|
#endif
|
|
|
|
status = (int)lws_http_client_http_response(wsi);
|
|
lwsl_info("%s: LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: %d\n", __func__, status);
|
|
// if (!status)
|
|
/* it's just telling use we connected / joined the nwsi */
|
|
// break;
|
|
|
|
#if defined(LWS_WITH_SYS_METRICS)
|
|
if (status) {
|
|
lws_snprintf((char *)buf, 10, "%d", status);
|
|
lws_metrics_tag_ss_add(h, "http_resp", (char *)buf);
|
|
}
|
|
#endif
|
|
|
|
if (status == HTTP_STATUS_SERVICE_UNAVAILABLE /* 503 */ ||
|
|
status == 429 /* Too many requests */) {
|
|
/*
|
|
* We understand this attempt failed, and that we should
|
|
* conceal this attempt. If there's a specified
|
|
* retry-after, we should use that if larger than our
|
|
* computed backoff
|
|
*/
|
|
|
|
inter = 0;
|
|
lws_http_check_retry_after(wsi, &inter);
|
|
|
|
r = _lws_ss_backoff(h, inter);
|
|
if (r != LWSSSSRET_OK)
|
|
return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h);
|
|
|
|
return -1; /* end this stream */
|
|
}
|
|
|
|
if (h->policy->u.http.resp_expect)
|
|
h->u.http.good_respcode =
|
|
status == h->policy->u.http.resp_expect;
|
|
else
|
|
h->u.http.good_respcode = (status >= 200 && status < 300);
|
|
// lwsl_err("%s: good resp %d %d\n", __func__, status, h->u.http.good_respcode);
|
|
|
|
if (lws_extract_metadata(h, wsi)) {
|
|
lwsl_info("%s: rx metadata extract failed\n", __func__);
|
|
|
|
return -1;
|
|
}
|
|
|
|
if (status) {
|
|
/*
|
|
* Check and see if it's something from the response
|
|
* map, if so, generate the requested status. If we're
|
|
* the proxy onward connection, metadata has priority
|
|
* over state updates on the serialization, so the
|
|
* state callback will see the right metadata.
|
|
*/
|
|
int n = lws_ss_http_resp_to_state(h, status);
|
|
if (n) {
|
|
r = lws_ss_event_helper(h, (lws_ss_constate_t)n);
|
|
if (r != LWSSSSRET_OK)
|
|
return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi,
|
|
&h);
|
|
}
|
|
}
|
|
|
|
if (h->u.http.good_respcode)
|
|
lwsl_info("%s: Connected streamtype %s, %d\n", __func__,
|
|
h->policy->streamtype, status);
|
|
else
|
|
if (h->u.http.good_respcode)
|
|
lwsl_warn("%s: Connected streamtype %s, BAD %d\n",
|
|
__func__, h->policy->streamtype,
|
|
status);
|
|
|
|
h->hanging_som = 0;
|
|
|
|
h->retry = 0;
|
|
h->seqstate = SSSEQ_CONNECTED;
|
|
lws_sul_cancel(&h->sul);
|
|
|
|
if (h->prev_ss_state != LWSSSCS_CONNECTED) {
|
|
wsi->client_suppress_CONNECTION_ERROR = 1;
|
|
/*
|
|
* back-to-back http transactions otherwise go
|
|
* DISCONNECTED -> CONNECTED, we should insert
|
|
* CONNECTING inbetween
|
|
*/
|
|
if (h->prev_ss_state == LWSSSCS_DISCONNECTED) {
|
|
r = lws_ss_event_helper(h, LWSSSCS_CONNECTING);
|
|
if (r != LWSSSSRET_OK)
|
|
return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h);
|
|
}
|
|
if (h->prev_ss_state != LWSSSCS_CONNECTED) {
|
|
r = lws_ss_event_helper(h, LWSSSCS_CONNECTED);
|
|
if (r != LWSSSSRET_OK)
|
|
return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Since it's an http transaction we initiated... this is
|
|
* proof of connection validity
|
|
*/
|
|
lws_validity_confirmed(wsi);
|
|
|
|
#if defined(LWS_WITH_SS_RIDESHARE)
|
|
|
|
/*
|
|
* There are two ways we might want to deal with multipart,
|
|
* one is pass it through raw (although the user code needs
|
|
* a helping hand for learning the boundary), and the other
|
|
* is to deframe it and provide basically submessages in the
|
|
* different parts.
|
|
*/
|
|
|
|
if (lws_hdr_copy(wsi, (char *)buf, sizeof(buf),
|
|
WSI_TOKEN_HTTP_CONTENT_TYPE) > 0 &&
|
|
/* multipart/form-data;
|
|
* boundary=----WebKitFormBoundarycc7YgAPEIHvgE9Bf */
|
|
|
|
(!strncmp((char *)buf, "multipart/form-data", 19) ||
|
|
!strncmp((char *)buf, "multipart/related", 17))) {
|
|
struct lws_tokenize ts;
|
|
lws_tokenize_elem e;
|
|
|
|
// puts((const char *)buf);
|
|
|
|
memset(&ts, 0, sizeof(ts));
|
|
ts.start = (char *)buf;
|
|
ts.len = strlen(ts.start);
|
|
ts.flags = LWS_TOKENIZE_F_RFC7230_DELIMS |
|
|
LWS_TOKENIZE_F_SLASH_NONTERM |
|
|
LWS_TOKENIZE_F_MINUS_NONTERM;
|
|
|
|
h->u.http.boundary[0] = '\0';
|
|
do {
|
|
e = lws_tokenize(&ts);
|
|
if (e == LWS_TOKZE_TOKEN_NAME_EQUALS &&
|
|
!strncmp(ts.token, "boundary", 8) &&
|
|
ts.token_len == 8) {
|
|
e = lws_tokenize(&ts);
|
|
if (e != LWS_TOKZE_TOKEN)
|
|
goto malformed;
|
|
h->u.http.boundary[0] = '\x0d';
|
|
h->u.http.boundary[1] = '\x0a';
|
|
h->u.http.boundary[2] = '-';
|
|
h->u.http.boundary[3] = '-';
|
|
lws_strnncpy(h->u.http.boundary + 4,
|
|
ts.token, ts.token_len,
|
|
sizeof(h->u.http.boundary) - 4);
|
|
h->u.http.boundary_len =
|
|
(uint8_t)(ts.token_len + 4);
|
|
h->u.http.boundary_seq = 2;
|
|
h->u.http.boundary_dashes = 0;
|
|
}
|
|
} while (e > 0);
|
|
lwsl_info("%s: multipart boundary '%s' len %d\n", __func__,
|
|
h->u.http.boundary, h->u.http.boundary_len);
|
|
|
|
/* inform the ss that a related message group begins */
|
|
|
|
if ((h->policy->flags & LWSSSPOLF_HTTP_MULTIPART_IN) &&
|
|
h->u.http.boundary[0])
|
|
h->info.rx(ss_to_userobj(h), NULL, 0,
|
|
LWSSS_FLAG_RELATED_START);
|
|
|
|
// lws_header_table_detach(wsi, 0);
|
|
}
|
|
break;
|
|
malformed:
|
|
lwsl_notice("%s: malformed multipart header\n", __func__);
|
|
return -1;
|
|
#else
|
|
break;
|
|
#endif
|
|
|
|
case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
|
|
if (!h)
|
|
return -1;
|
|
if (h->writeable_len)
|
|
wsi->http.writeable_len = h->writeable_len;
|
|
|
|
{
|
|
uint8_t **p = (uint8_t **)in, *end = (*p) + len,
|
|
*oin = *(uint8_t **)in;
|
|
|
|
/*
|
|
* blob-based headers
|
|
*/
|
|
|
|
for (m = 0; m < _LWSSS_HBI_COUNT; m++) {
|
|
lws_system_blob_t *ab;
|
|
int o = 0, n;
|
|
|
|
if (!h->policy->u.http.blob_header[m])
|
|
continue;
|
|
|
|
/*
|
|
* To be backward compatible, default is system-wide LWA auth,
|
|
* and "http_auth_header" is for default LWA auth, current users do not
|
|
* need any change in their policy.
|
|
* If user wants different auth/token, need to specify the "use_auth"
|
|
* and will be handled after metadata headers are applied.
|
|
*/
|
|
|
|
if (m == LWSSS_HBI_AUTH &&
|
|
h->policy->u.http.auth_preamble)
|
|
o = lws_snprintf((char *)buf, sizeof(buf), "%s",
|
|
h->policy->u.http.auth_preamble);
|
|
|
|
if (o > (int)sizeof(buf) - 2)
|
|
return -1;
|
|
|
|
ab = lws_system_get_blob(wsi->a.context, blob_idx[m], 0);
|
|
if (!ab)
|
|
return -1;
|
|
|
|
buflen = sizeof(buf) - (unsigned int)o - 2u;
|
|
n = lws_system_blob_get(ab, buf + o, &buflen, 0);
|
|
if (n < 0)
|
|
return -1;
|
|
|
|
buf[(unsigned int)o + buflen] = '\0';
|
|
lwsl_debug("%s: adding blob %d: %s\n", __func__, m, buf);
|
|
|
|
if (lws_add_http_header_by_name(wsi,
|
|
(uint8_t *)h->policy->u.http.blob_header[m],
|
|
buf, (int)((int)buflen + o), p, end))
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* metadata-based headers
|
|
*/
|
|
|
|
if (lws_apply_metadata(h, wsi, buf, p, end))
|
|
return -1;
|
|
|
|
#if defined(LWS_WITH_SS_DIRECT_PROTOCOL_STR)
|
|
if (h->policy->flags & LWSSSPOLF_DIRECT_PROTO_STR) {
|
|
if (lws_apply_instant_metadata(h, wsi, buf, p, end))
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
#if defined(LWS_WITH_SECURE_STREAMS_AUTH_SIGV4)
|
|
if (h->policy->auth && h->policy->auth->type &&
|
|
!strcmp(h->policy->auth->type, "sigv4")) {
|
|
|
|
if (lws_ss_apply_sigv4(wsi, h, p, end))
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
|
|
(void)oin;
|
|
//if (*p != oin)
|
|
// lwsl_hexdump_notice(oin, lws_ptr_diff_size_t(*p, oin));
|
|
|
|
}
|
|
|
|
/*
|
|
* So when proxied, for POST we have to synthesize a CONNECTED
|
|
* state, so it can request a writeable and deliver the POST
|
|
* body
|
|
*/
|
|
if ((h->policy->protocol == LWSSSP_H1 ||
|
|
h->policy->protocol == LWSSSP_H2) &&
|
|
h->being_serialized && (
|
|
!strcmp(h->policy->u.http.method, "PUT") ||
|
|
!strcmp(h->policy->u.http.method, "PATCH") ||
|
|
!strcmp(h->policy->u.http.method, "POST"))) {
|
|
|
|
wsi->client_suppress_CONNECTION_ERROR = 1;
|
|
if (h->prev_ss_state != LWSSSCS_CONNECTED) {
|
|
r = lws_ss_event_helper(h, LWSSSCS_CONNECTED);
|
|
if (r)
|
|
return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
/* chunks of chunked content, with header removed */
|
|
case LWS_CALLBACK_HTTP_BODY:
|
|
case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ:
|
|
lwsl_debug("%s: RECEIVE_CLIENT_HTTP_READ: read %d\n",
|
|
__func__, (int)len);
|
|
if (!h || !h->info.rx)
|
|
return 0;
|
|
|
|
#if defined(LWS_WITH_SS_RIDESHARE)
|
|
if ((h->policy->flags & LWSSSPOLF_HTTP_MULTIPART_IN) &&
|
|
h->u.http.boundary[0])
|
|
return ss_http_multipart_parser(h, in, len);
|
|
#endif
|
|
|
|
if (!h->subseq) {
|
|
f |= LWSSS_FLAG_SOM;
|
|
h->hanging_som = 1;
|
|
h->subseq = 1;
|
|
}
|
|
|
|
// lwsl_notice("%s: HTTP_READ: client side sent len %d fl 0x%x\n",
|
|
// __func__, (int)len, (int)f);
|
|
|
|
h->wsi = wsi; /* since we accept the wsi is bound to the SS,
|
|
* ensure the SS feels the same way about the wsi */
|
|
r = h->info.rx(ss_to_userobj(h), (const uint8_t *)in, len, f);
|
|
if (r != LWSSSSRET_OK)
|
|
return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h);
|
|
|
|
return 0; /* don't passthru */
|
|
|
|
/* uninterpreted http content */
|
|
case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
|
|
{
|
|
char *px = (char *)buf + LWS_PRE; /* guarantees LWS_PRE */
|
|
int lenx = sizeof(buf) - LWS_PRE;
|
|
|
|
m = lws_http_client_read(wsi, &px, &lenx);
|
|
if (m < 0)
|
|
return m;
|
|
}
|
|
lws_set_timeout(wsi, 99, 30);
|
|
|
|
return 0; /* don't passthru */
|
|
|
|
case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
|
|
// lwsl_debug("%s: LWS_CALLBACK_COMPLETED_CLIENT_HTTP\n", __func__);
|
|
|
|
if (!h)
|
|
return -1;
|
|
|
|
if (h->hanging_som) {
|
|
h->info.rx(ss_to_userobj(h), NULL, 0, LWSSS_FLAG_EOM);
|
|
h->hanging_som = 0;
|
|
h->subseq = 0;
|
|
}
|
|
|
|
wsi->http.writeable_len = h->writeable_len = 0;
|
|
lws_sul_cancel(&h->sul_timeout);
|
|
|
|
h->txn_ok = 1;
|
|
|
|
#if defined(LWS_WITH_SYS_METRICS)
|
|
lws_metrics_tag_ss_add(h, "result",
|
|
h->u.http.good_respcode ?
|
|
"SS_ACK_REMOTE" : "SS_NACK_REMOTE");
|
|
#endif
|
|
|
|
r = lws_ss_event_helper(h, h->u.http.good_respcode ?
|
|
LWSSSCS_QOS_ACK_REMOTE :
|
|
LWSSSCS_QOS_NACK_REMOTE);
|
|
if (r != LWSSSSRET_OK)
|
|
return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h);
|
|
|
|
lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */
|
|
break;
|
|
|
|
case LWS_CALLBACK_HTTP_WRITEABLE:
|
|
case LWS_CALLBACK_CLIENT_HTTP_WRITEABLE:
|
|
|
|
if (!h || !h->info.tx) {
|
|
lwsl_debug("%s: no handle / tx\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
#if defined(LWS_WITH_SERVER)
|
|
if (h->txn_resp_pending) {
|
|
/*
|
|
* If we're going to start sending something, we need to
|
|
* to take care of the http response header for it first
|
|
*/
|
|
h->txn_resp_pending = 0;
|
|
|
|
if (lws_add_http_common_headers(wsi,
|
|
(unsigned int)(h->txn_resp_set ?
|
|
(h->txn_resp ? h->txn_resp : 200) :
|
|
HTTP_STATUS_NOT_FOUND),
|
|
NULL,
|
|
h->policy->flags & LWSSSPOLF_HTTP_NO_CONTENT_LENGTH ?
|
|
LWS_ILLEGAL_HTTP_CONTENT_LEN :
|
|
h->wsi->http.writeable_len,
|
|
&p, end))
|
|
return 1;
|
|
|
|
/*
|
|
* metadata-based headers
|
|
*/
|
|
|
|
if (lws_apply_metadata(h, wsi, buf, &p, end))
|
|
return -1;
|
|
|
|
if (lws_finalize_write_http_header(wsi, start, &p, end))
|
|
return 1;
|
|
|
|
/* write the body separately */
|
|
lws_callback_on_writable(wsi);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
if (
|
|
#if defined(LWS_WITH_SERVER)
|
|
!(h->info.flags & LWSSSINFLAGS_ACCEPTED) && /* not accepted */
|
|
#endif
|
|
!h->rideshare)
|
|
|
|
h->rideshare = h->policy;
|
|
|
|
#if defined(LWS_WITH_SS_RIDESHARE)
|
|
if (
|
|
#if defined(LWS_WITH_SERVER)
|
|
!(h->info.flags & LWSSSINFLAGS_ACCEPTED) && /* not accepted */
|
|
#endif
|
|
!h->inside_msg && h->rideshare->u.http.multipart_name)
|
|
lws_client_http_multipart(wsi,
|
|
h->rideshare->u.http.multipart_name,
|
|
h->rideshare->u.http.multipart_filename,
|
|
h->rideshare->u.http.multipart_content_type,
|
|
(char **)&p, (char *)end);
|
|
|
|
buflen = lws_ptr_diff_size_t(end, p);
|
|
if (h->policy->u.http.multipart_name)
|
|
buflen -= 24; /* allow space for end of multipart */
|
|
#else
|
|
buflen = lws_ptr_diff_size_t(end, p);
|
|
#endif
|
|
r = h->info.tx(ss_to_userobj(h), h->txord++, p, &buflen, &f);
|
|
if (r == LWSSSSRET_TX_DONT_SEND)
|
|
return 0;
|
|
if (r < 0)
|
|
return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h);
|
|
|
|
// lwsl_notice("%s: WRITEABLE: user tx says len %d fl 0x%x\n",
|
|
// __func__, (int)buflen, (int)f);
|
|
|
|
p += buflen;
|
|
|
|
if (f & LWSSS_FLAG_EOM) {
|
|
#if defined(LWS_WITH_SERVER)
|
|
if (!(h->info.flags & LWSSSINFLAGS_ACCEPTED)) {
|
|
#endif
|
|
conceal_eom = 1;
|
|
/* end of rideshares */
|
|
if (!h->rideshare->rideshare_streamtype) {
|
|
lws_client_http_body_pending(wsi, 0);
|
|
#if defined(LWS_WITH_SS_RIDESHARE)
|
|
if (h->rideshare->u.http.multipart_name)
|
|
lws_client_http_multipart(wsi, NULL, NULL, NULL,
|
|
(char **)&p, (char *)end);
|
|
conceal_eom = 0;
|
|
#endif
|
|
} else {
|
|
h->rideshare = lws_ss_policy_lookup(wsi->a.context,
|
|
h->rideshare->rideshare_streamtype);
|
|
lws_callback_on_writable(wsi);
|
|
}
|
|
#if defined(LWS_WITH_SERVER)
|
|
}
|
|
#endif
|
|
|
|
h->inside_msg = 0;
|
|
} else {
|
|
/* otherwise we can spin with zero length writes */
|
|
if (!f && !lws_ptr_diff(p, buf + LWS_PRE))
|
|
break;
|
|
h->inside_msg = 1;
|
|
lws_callback_on_writable(wsi);
|
|
}
|
|
|
|
lwsl_info("%s: lws_write %d %d\n", __func__,
|
|
lws_ptr_diff(p, buf + LWS_PRE), f);
|
|
|
|
if (lws_write(wsi, buf + LWS_PRE, lws_ptr_diff_size_t(p, buf + LWS_PRE),
|
|
(!conceal_eom && (f & LWSSS_FLAG_EOM)) ?
|
|
LWS_WRITE_HTTP_FINAL : LWS_WRITE_HTTP) !=
|
|
(int)lws_ptr_diff(p, buf + LWS_PRE)) {
|
|
lwsl_err("%s: write failed\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
#if defined(LWS_WITH_SERVER)
|
|
if ((h->info.flags & LWSSSINFLAGS_ACCEPTED) /* server */ &&
|
|
(f & LWSSS_FLAG_EOM) &&
|
|
lws_http_transaction_completed(wsi))
|
|
return -1;
|
|
#else
|
|
lws_set_timeout(wsi, 0, 0);
|
|
#endif
|
|
break;
|
|
|
|
#if defined(LWS_WITH_SERVER)
|
|
case LWS_CALLBACK_HTTP:
|
|
|
|
if (!h)
|
|
return -1;
|
|
|
|
if (h->wsi && h->wsi->mount_hit)
|
|
break;
|
|
|
|
lwsl_info("%s: LWS_CALLBACK_HTTP\n", __func__);
|
|
{
|
|
|
|
h->txn_resp_set = 0;
|
|
h->txn_resp_pending = 1;
|
|
h->writeable_len = 0;
|
|
|
|
#if defined(LWS_ROLE_H2)
|
|
m = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_METHOD);
|
|
if (m) {
|
|
if (lws_ss_alloc_set_metadata(h, "method",
|
|
lws_hdr_simple_ptr(wsi,
|
|
WSI_TOKEN_HTTP_COLON_METHOD), (unsigned int)m))
|
|
return -1;
|
|
m = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_PATH);
|
|
if (m && lws_ss_alloc_set_metadata(h, "path",
|
|
lws_hdr_simple_ptr(wsi,
|
|
WSI_TOKEN_HTTP_COLON_PATH), (unsigned int)m))
|
|
return -1;
|
|
} else
|
|
#endif
|
|
{
|
|
m = lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI);
|
|
if (m) {
|
|
if (lws_ss_alloc_set_metadata(h, "path",
|
|
lws_hdr_simple_ptr(wsi,
|
|
WSI_TOKEN_GET_URI), (unsigned int)m))
|
|
return -1;
|
|
if (lws_ss_alloc_set_metadata(h, "method", "GET", 3))
|
|
return -1;
|
|
} else {
|
|
m = lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI);
|
|
if (m) {
|
|
if (lws_ss_alloc_set_metadata(h, "path",
|
|
lws_hdr_simple_ptr(wsi,
|
|
WSI_TOKEN_POST_URI), (unsigned int)m))
|
|
return -1;
|
|
if (lws_ss_alloc_set_metadata(h, "method", "POST", 4))
|
|
return -1;
|
|
} else {
|
|
m = lws_hdr_total_length(wsi, WSI_TOKEN_PATCH_URI);
|
|
if (m) {
|
|
if (lws_ss_alloc_set_metadata(h, "path",
|
|
lws_hdr_simple_ptr(wsi,
|
|
WSI_TOKEN_PATCH_URI), (unsigned int)m))
|
|
return -1;
|
|
if (lws_ss_alloc_set_metadata(h, "method", "PATCH", 5))
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!h->ss_dangling_connected) {
|
|
#if defined(LWS_WITH_SYS_METRICS)
|
|
/*
|
|
* If any hanging caliper measurement, dump it, and free any tags
|
|
*/
|
|
lws_metrics_caliper_report_hist(h->cal_txn, (struct lws *)NULL);
|
|
#endif
|
|
wsi->client_suppress_CONNECTION_ERROR = 1;
|
|
if (h->prev_ss_state != LWSSSCS_CONNECTED) {
|
|
r = lws_ss_event_helper(h, LWSSSCS_CONNECTED);
|
|
if (r)
|
|
return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h);
|
|
}
|
|
}
|
|
|
|
r = lws_ss_event_helper(h, LWSSSCS_SERVER_TXN);
|
|
if (r)
|
|
return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r,
|
|
wsi, &h);
|
|
|
|
return 0;
|
|
#endif
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return lws_callback_http_dummy(wsi, reason, user, in, len);
|
|
}
|
|
|
|
const struct lws_protocols protocol_secstream_h1 = {
|
|
"lws-secstream-h1",
|
|
secstream_h1,
|
|
0, 0, 0, NULL, 0
|
|
};
|
|
|
|
/*
|
|
* Munge connect info according to protocol-specific considerations... this
|
|
* usually means interpreting aux in a protocol-specific way and using the
|
|
* pieces at connection setup time, eg, http url pieces.
|
|
*
|
|
* len bytes of buf can be used for things with scope until after the actual
|
|
* connect.
|
|
*/
|
|
|
|
static int
|
|
secstream_connect_munge_h1(lws_ss_handle_t *h, char *buf, size_t len,
|
|
struct lws_client_connect_info *i,
|
|
union lws_ss_contemp *ct)
|
|
{
|
|
const char *pbasis = h->policy->u.http.url;
|
|
size_t used_in, used_out;
|
|
lws_strexp_t exp;
|
|
|
|
/* i.path on entry is used to override the policy urlpath if not "" */
|
|
|
|
if (i->path[0])
|
|
pbasis = i->path;
|
|
|
|
if (!pbasis)
|
|
return 0;
|
|
|
|
/* uncomment to force h1 */
|
|
// i->alpn = "http/1.1";
|
|
|
|
#if defined(LWS_WITH_SS_RIDESHARE)
|
|
if (h->policy->flags & LWSSSPOLF_HTTP_MULTIPART)
|
|
i->ssl_connection |= LCCSCF_HTTP_MULTIPART_MIME;
|
|
|
|
if (h->policy->flags & LWSSSPOLF_HTTP_X_WWW_FORM_URLENCODED)
|
|
i->ssl_connection |= LCCSCF_HTTP_X_WWW_FORM_URLENCODED;
|
|
#endif
|
|
|
|
if (h->policy->flags & LWSSSPOLF_HTTP_CACHE_COOKIES)
|
|
i->ssl_connection |= LCCSCF_CACHE_COOKIES;
|
|
|
|
/* protocol aux is the path part */
|
|
|
|
i->path = buf;
|
|
|
|
/* skip the unnessary '/' */
|
|
if (*pbasis == '/')
|
|
pbasis = pbasis + 1;
|
|
|
|
buf[0] = '/';
|
|
|
|
lws_strexp_init(&exp, (void *)h, lws_ss_exp_cb_metadata, buf + 1, len - 1);
|
|
|
|
if (lws_strexp_expand(&exp, pbasis, strlen(pbasis),
|
|
&used_in, &used_out) != LSTRX_DONE)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
const struct ss_pcols ss_pcol_h1 = {
|
|
"h1",
|
|
"http/1.1",
|
|
&protocol_secstream_h1,
|
|
secstream_connect_munge_h1,
|
|
NULL, NULL
|
|
};
|