/src/libwebsockets/lib/secure-streams/protocols/ss-h1.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * libwebsockets - small server side websockets and web server implementation |
3 | | * |
4 | | * Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com> |
5 | | * |
6 | | * Permission is hereby granted, free of charge, to any person obtaining a copy |
7 | | * of this software and associated documentation files (the "Software"), to |
8 | | * deal in the Software without restriction, including without limitation the |
9 | | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
10 | | * sell copies of the Software, and to permit persons to whom the Software is |
11 | | * furnished to do so, subject to the following conditions: |
12 | | * |
13 | | * The above copyright notice and this permission notice shall be included in |
14 | | * all copies or substantial portions of the Software. |
15 | | * |
16 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
17 | | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
18 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
19 | | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
20 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
21 | | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
22 | | * IN THE SOFTWARE. |
23 | | * |
24 | | * This is the glue that wires up h1 to Secure Streams. |
25 | | */ |
26 | | |
27 | | #include <private-lib-core.h> |
28 | | |
29 | | #if !defined(LWS_PLAT_FREERTOS) || defined(LWS_ROLE_H2) |
30 | | #define LWS_WITH_SS_RIDESHARE |
31 | | #endif |
32 | | |
33 | | #if defined(LWS_WITH_SS_RIDESHARE) |
34 | | static int |
35 | | ss_http_multipart_parser(lws_ss_handle_t *h, void *in, size_t len) |
36 | 0 | { |
37 | 0 | uint8_t *q = (uint8_t *)in; |
38 | 0 | int pending_issue = 0, n = 0; |
39 | | |
40 | | |
41 | | /* let's stick it in the boundary state machine first */ |
42 | 0 | while (n < (int)len) { |
43 | 0 | if (h->u.http.boundary_seq != h->u.http.boundary_len) { |
44 | 0 | if (q[n] == h->u.http.boundary[h->u.http.boundary_seq]) |
45 | 0 | h->u.http.boundary_seq++; |
46 | 0 | else { |
47 | 0 | h->u.http.boundary_seq = 0; |
48 | 0 | h->u.http.boundary_dashes = 0; |
49 | 0 | h->u.http.boundary_post = 0; |
50 | 0 | } |
51 | 0 | goto around; |
52 | 0 | } |
53 | | |
54 | | /* |
55 | | * We already matched the boundary string, now we're |
56 | | * looking if there's a -- afterwards |
57 | | */ |
58 | 0 | if (h->u.http.boundary_dashes < 2) { |
59 | 0 | if (q[n] == '-') { |
60 | 0 | h->u.http.boundary_dashes++; |
61 | 0 | goto around; |
62 | 0 | } |
63 | | /* there was no final -- ... */ |
64 | 0 | } |
65 | | |
66 | 0 | if (h->u.http.boundary_dashes == 2) { |
67 | | /* |
68 | | * It's an EOM boundary: issue pending + multipart EOP |
69 | | */ |
70 | 0 | lwsl_debug("%s: seen EOP, n %d pi %d\n", |
71 | 0 | __func__, n, pending_issue); |
72 | | /* |
73 | | * It's possible we already started the decode before |
74 | | * the end of the last packet. Then there is no |
75 | | * remainder to send. |
76 | | */ |
77 | 0 | if (n >= pending_issue + h->u.http.boundary_len + |
78 | 0 | (h->u.http.any ? 2 : 0) + 1) { |
79 | 0 | h->info.rx(ss_to_userobj(h), |
80 | 0 | &q[pending_issue], |
81 | 0 | (unsigned int)(n - pending_issue - |
82 | 0 | h->u.http.boundary_len - 1 - |
83 | 0 | (h->u.http.any ? 2 : 0) /* crlf */), |
84 | 0 | (!h->u.http.som ? LWSSS_FLAG_SOM : 0) | |
85 | 0 | LWSSS_FLAG_EOM | LWSSS_FLAG_RELATED_END); |
86 | 0 | h->u.http.eom = 1; |
87 | 0 | } |
88 | | |
89 | | /* |
90 | | * Peer may not END_STREAM us |
91 | | */ |
92 | 0 | return 0; |
93 | | //return -1; |
94 | 0 | } |
95 | | |
96 | | /* how about --boundaryCRLF */ |
97 | | |
98 | 0 | if (h->u.http.boundary_post < 2) { |
99 | 0 | if ((!h->u.http.boundary_post && q[n] == '\x0d') || |
100 | 0 | (h->u.http.boundary_post && q[n] == '\x0a')) { |
101 | 0 | h->u.http.boundary_post++; |
102 | 0 | goto around; |
103 | 0 | } |
104 | | /* there was no final CRLF ... it's wrong */ |
105 | | |
106 | 0 | return -1; |
107 | 0 | } |
108 | 0 | if (h->u.http.boundary_post != 2) |
109 | 0 | goto around; |
110 | | |
111 | | /* |
112 | | * We have a starting "--boundaryCRLF" or intermediate |
113 | | * "CRLF--boundaryCRLF" boundary |
114 | | */ |
115 | 0 | lwsl_debug("%s: b_post = 2 (pi %d)\n", __func__, pending_issue); |
116 | 0 | h->u.http.boundary_seq = 0; |
117 | 0 | h->u.http.boundary_post = 0; |
118 | |
|
119 | 0 | if (n >= pending_issue && (h->u.http.any || !h->u.http.som)) { |
120 | | /* Intermediate... do the EOM */ |
121 | 0 | lwsl_debug("%s: seen interm EOP n %d pi %d\n", __func__, |
122 | 0 | n, pending_issue); |
123 | | /* |
124 | | * It's possible we already started the decode before |
125 | | * the end of the last packet. Then there is no |
126 | | * remainder to send. |
127 | | */ |
128 | 0 | if (n >= pending_issue + h->u.http.boundary_len + |
129 | 0 | (h->u.http.any ? 2 : 0)) { |
130 | 0 | h->info.rx(ss_to_userobj(h), &q[pending_issue], |
131 | 0 | (unsigned int)(n - pending_issue - |
132 | 0 | h->u.http.boundary_len - |
133 | 0 | (h->u.http.any ? 2 /* crlf */ : 0)), |
134 | 0 | (!h->u.http.som ? LWSSS_FLAG_SOM : 0) | |
135 | 0 | LWSSS_FLAG_EOM); |
136 | 0 | h->u.http.eom = 1; |
137 | 0 | } |
138 | 0 | } |
139 | | |
140 | | /* Next message starts after this boundary */ |
141 | |
|
142 | 0 | pending_issue = n; |
143 | 0 | if (h->u.http.eom) { |
144 | | /* reset only if we have sent eom */ |
145 | 0 | h->u.http.som = 0; |
146 | 0 | h->u.http.eom = 0; |
147 | 0 | } |
148 | |
|
149 | 0 | around: |
150 | 0 | n++; |
151 | 0 | } |
152 | | |
153 | 0 | if (pending_issue != n) { |
154 | 0 | uint8_t oh = 0; |
155 | | |
156 | | /* |
157 | | * handle the first or last "--boundaryCRLF" case which is not captured in the |
158 | | * previous loop, on the Bob downchannel (/directive) |
159 | | * |
160 | | * probably does not cover the case that one boundary term is separated in multipile |
161 | | * one callbacks though never see such case |
162 | | */ |
163 | |
|
164 | 0 | if ((n >= h->u.http.boundary_len) && |
165 | 0 | h->u.http.boundary_seq == h->u.http.boundary_len && |
166 | 0 | h->u.http.boundary_post == 2) { |
167 | |
|
168 | 0 | oh = 1; |
169 | 0 | } |
170 | |
|
171 | 0 | h->info.rx(ss_to_userobj(h), &q[pending_issue], |
172 | 0 | (unsigned int)(oh ? |
173 | 0 | (n - pending_issue - h->u.http.boundary_len - |
174 | 0 | (h->u.http.any ? 2 : 0)) : |
175 | 0 | (n - pending_issue)), |
176 | 0 | (!h->u.http.som ? LWSSS_FLAG_SOM : 0) | |
177 | 0 | (oh && h->u.http.any ? LWSSS_FLAG_EOM : 0)); |
178 | |
|
179 | 0 | if (oh && h->u.http.any) |
180 | 0 | h->u.http.eom = 1; |
181 | |
|
182 | 0 | h->u.http.any = 1; |
183 | 0 | h->u.http.som = 1; |
184 | 0 | } |
185 | |
|
186 | 0 | return 0; |
187 | 0 | } |
188 | | #endif |
189 | | |
190 | | /* |
191 | | * Returns 0, or the ss state resp maps on to |
192 | | */ |
193 | | |
194 | | static int |
195 | | lws_ss_http_resp_to_state(lws_ss_handle_t *h, int resp) |
196 | 0 | { |
197 | 0 | const lws_ss_http_respmap_t *r = h->policy->u.http.respmap; |
198 | 0 | int n = h->policy->u.http.count_respmap; |
199 | |
|
200 | 0 | while (n--) |
201 | 0 | if (resp == r->resp) |
202 | 0 | return r->state; |
203 | 0 | else |
204 | 0 | r++; |
205 | | |
206 | 0 | return 0; /* no hit */ |
207 | 0 | } |
208 | | |
209 | | /* |
210 | | * This converts any set metadata items into outgoing http headers |
211 | | */ |
212 | | |
213 | | static int |
214 | | lws_apply_metadata(lws_ss_handle_t *h, struct lws *wsi, uint8_t *buf, |
215 | | uint8_t **pp, uint8_t *end) |
216 | 0 | { |
217 | 0 | lws_ss_metadata_t *polmd = h->policy->metadata; |
218 | 0 | int m = 0; |
219 | |
|
220 | 0 | while (polmd) { |
221 | | |
222 | | /* has to have a non-empty header string */ |
223 | |
|
224 | 0 | if (polmd->value__may_own_heap && |
225 | 0 | ((uint8_t *)polmd->value__may_own_heap)[0] && |
226 | 0 | h->metadata[m].value__may_own_heap) { |
227 | 0 | if (lws_add_http_header_by_name(wsi, |
228 | 0 | polmd->value__may_own_heap, |
229 | 0 | h->metadata[m].value__may_own_heap, |
230 | 0 | (int)h->metadata[m].length, pp, end)) |
231 | 0 | return -1; |
232 | | |
233 | | /* |
234 | | * Check for the case he's setting a non-zero |
235 | | * content-length "via the backdoor" metadata- |
236 | | * driven headers, and set the body_pending() |
237 | | * state if so... |
238 | | */ |
239 | | |
240 | 0 | if (!strncmp(polmd->value__may_own_heap, |
241 | 0 | "content-length", 14) && |
242 | 0 | atoi(h->metadata[m].value__may_own_heap)) |
243 | 0 | lws_client_http_body_pending(wsi, 1); |
244 | 0 | } |
245 | | |
246 | 0 | m++; |
247 | 0 | polmd = polmd->next; |
248 | 0 | } |
249 | | |
250 | | /* |
251 | | * Content-length on POST / PUT / PATCH if we have the length information |
252 | | */ |
253 | | |
254 | 0 | if (h->policy->u.http.method && ( |
255 | 0 | (!strcmp(h->policy->u.http.method, "POST") || |
256 | 0 | !strcmp(h->policy->u.http.method, "PATCH") || |
257 | 0 | !strcmp(h->policy->u.http.method, "PUT"))) && |
258 | 0 | wsi->http.writeable_len) { |
259 | 0 | if (!(h->policy->flags & |
260 | 0 | LWSSSPOLF_HTTP_NO_CONTENT_LENGTH)) { |
261 | 0 | int n = lws_snprintf((char *)buf, 20, "%u", |
262 | 0 | (unsigned int)wsi->http.writeable_len); |
263 | 0 | if (lws_add_http_header_by_token(wsi, |
264 | 0 | WSI_TOKEN_HTTP_CONTENT_LENGTH, |
265 | 0 | buf, n, pp, end)) |
266 | 0 | return -1; |
267 | 0 | } |
268 | 0 | lws_client_http_body_pending(wsi, 1); |
269 | 0 | } |
270 | | |
271 | 0 | return 0; |
272 | 0 | } |
273 | | |
274 | | |
275 | | #if defined(LWS_WITH_SS_DIRECT_PROTOCOL_STR) |
276 | | static int |
277 | | lws_apply_instant_metadata(lws_ss_handle_t *h, struct lws *wsi, uint8_t *buf, |
278 | | uint8_t **pp, uint8_t *end) |
279 | | { |
280 | | lws_ss_metadata_t *imd = h->instant_metadata; |
281 | | |
282 | | while (imd) { |
283 | | if (imd->name && imd->value__may_own_heap) { |
284 | | lwsl_debug("%s add header %s %s %d\n", __func__, |
285 | | imd->name, |
286 | | (char *)imd->value__may_own_heap, |
287 | | (int)imd->length); |
288 | | if (lws_add_http_header_by_name(wsi, |
289 | | (const unsigned char *)imd->name, |
290 | | (const unsigned char *)imd->value__may_own_heap, |
291 | | (int)imd->length, pp, end)) |
292 | | return -1; |
293 | | |
294 | | /* it's possible user set content-length directly */ |
295 | | if (!strncmp(imd->name, |
296 | | "content-length", 14) && |
297 | | atoi(imd->value__may_own_heap)) |
298 | | lws_client_http_body_pending(wsi, 1); |
299 | | |
300 | | } |
301 | | |
302 | | imd = imd->next; |
303 | | } |
304 | | |
305 | | return 0; |
306 | | } |
307 | | #endif |
308 | | /* |
309 | | * Check if any metadata headers present in the server headers, and record |
310 | | * them into the associated metadata item if so. |
311 | | */ |
312 | | |
313 | | static int |
314 | | lws_extract_metadata(lws_ss_handle_t *h, struct lws *wsi) |
315 | 0 | { |
316 | 0 | lws_ss_metadata_t *polmd = h->policy->metadata, *omd; |
317 | 0 | int n; |
318 | |
|
319 | 0 | while (polmd) { |
320 | |
|
321 | 0 | if (polmd->value_is_http_token != LWS_HTTP_NO_KNOWN_HEADER) { |
322 | | |
323 | | /* it's a well-known header token */ |
324 | |
|
325 | 0 | n = lws_hdr_total_length(wsi, polmd->value_is_http_token); |
326 | 0 | if (n) { |
327 | 0 | const char *cp = lws_hdr_simple_ptr(wsi, |
328 | 0 | polmd->value_is_http_token); |
329 | 0 | omd = lws_ss_get_handle_metadata(h, polmd->name); |
330 | 0 | if (!omd || !cp) |
331 | 0 | return 1; |
332 | | |
333 | 0 | assert(!strcmp(omd->name, polmd->name)); |
334 | | |
335 | | /* |
336 | | * it's present on the wsi, we want to |
337 | | * set the related metadata name to it then |
338 | | */ |
339 | | |
340 | 0 | _lws_ss_alloc_set_metadata(omd, polmd->name, cp, |
341 | 0 | (unsigned int)n); |
342 | |
|
343 | | #if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) |
344 | | /* |
345 | | * ...and because we are doing it from parsing |
346 | | * onward rx, we want to mark the metadata as |
347 | | * needing passing to the client |
348 | | */ |
349 | | omd->pending_onward = 1; |
350 | | #endif |
351 | 0 | } |
352 | 0 | } |
353 | | |
354 | 0 | #if defined(LWS_WITH_CUSTOM_HEADERS) |
355 | 0 | else |
356 | | |
357 | | /* has to have a non-empty header string */ |
358 | | |
359 | 0 | if (polmd->value__may_own_heap && |
360 | 0 | ((uint8_t *)polmd->value__may_own_heap)[0]) { |
361 | 0 | char *p; |
362 | | |
363 | | /* |
364 | | * Can it be a custom header? |
365 | | */ |
366 | |
|
367 | 0 | n = lws_hdr_custom_length(wsi, (const char *) |
368 | 0 | polmd->value__may_own_heap, |
369 | 0 | polmd->value_length); |
370 | 0 | if (n > 0) { |
371 | |
|
372 | 0 | p = lws_malloc((unsigned int)n + 1, __func__); |
373 | 0 | if (!p) |
374 | 0 | return 1; |
375 | | |
376 | | /* if needed, free any previous value */ |
377 | | |
378 | 0 | if (polmd->value_on_lws_heap) { |
379 | 0 | lws_free( |
380 | 0 | polmd->value__may_own_heap); |
381 | 0 | polmd->value_on_lws_heap = 0; |
382 | 0 | } |
383 | | |
384 | | /* |
385 | | * copy the named custom header value |
386 | | * into the malloc'd buffer |
387 | | */ |
388 | |
|
389 | 0 | if (lws_hdr_custom_copy(wsi, p, n + 1, |
390 | 0 | (const char *) |
391 | 0 | polmd->value__may_own_heap, |
392 | 0 | polmd->value_length) < 0) { |
393 | 0 | lws_free(p); |
394 | |
|
395 | 0 | return 1; |
396 | 0 | } |
397 | | |
398 | 0 | omd = lws_ss_get_handle_metadata(h, |
399 | 0 | polmd->name); |
400 | 0 | if (omd) { |
401 | |
|
402 | 0 | _lws_ss_set_metadata(omd, |
403 | 0 | polmd->name, p, (size_t)n); |
404 | 0 | omd->value_on_lws_heap = 1; |
405 | |
|
406 | | #if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) |
407 | | omd->pending_onward = 1; |
408 | | #endif |
409 | 0 | } |
410 | 0 | } |
411 | 0 | } |
412 | 0 | #endif |
413 | | |
414 | 0 | polmd = polmd->next; |
415 | 0 | } |
416 | | |
417 | 0 | return 0; |
418 | 0 | } |
419 | | |
420 | | static const uint8_t blob_idx[] = { |
421 | | LWS_SYSBLOB_TYPE_AUTH, |
422 | | LWS_SYSBLOB_TYPE_DEVICE_SERIAL, |
423 | | LWS_SYSBLOB_TYPE_DEVICE_FW_VERSION, |
424 | | LWS_SYSBLOB_TYPE_DEVICE_TYPE, |
425 | | }; |
426 | | |
427 | | int |
428 | | secstream_h1(struct lws *wsi, enum lws_callback_reasons reason, void *user, |
429 | | void *in, size_t len) |
430 | 0 | { |
431 | 0 | #if defined(LWS_WITH_SERVER) |
432 | 0 | struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi]; |
433 | 0 | #endif |
434 | 0 | lws_ss_handle_t *h = (lws_ss_handle_t *)lws_get_opaque_user_data(wsi); |
435 | 0 | uint8_t buf[LWS_PRE + 1520], *p = &buf[LWS_PRE], |
436 | 0 | #if defined(LWS_WITH_SERVER) |
437 | 0 | *start = p, |
438 | 0 | #endif |
439 | 0 | *end = &buf[sizeof(buf) - 1]; |
440 | 0 | lws_ss_state_return_t r; |
441 | 0 | int f = 0, m, status; |
442 | 0 | char conceal_eom = 0; |
443 | 0 | lws_usec_t inter; |
444 | 0 | size_t buflen; |
445 | |
|
446 | 0 | switch (reason) { |
447 | | |
448 | 0 | case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: |
449 | 0 | if (!h) { |
450 | 0 | lwsl_err("%s: CCE with no ss handle %s\n", __func__, lws_wsi_tag(wsi)); |
451 | 0 | break; |
452 | 0 | } |
453 | | |
454 | 0 | lws_ss_assert_extant(wsi->a.context, wsi->tsi, h); |
455 | |
|
456 | 0 | assert(h->policy); |
457 | | |
458 | 0 | #if defined(LWS_WITH_CONMON) |
459 | 0 | lws_conmon_ss_json(h); |
460 | 0 | #endif |
461 | |
|
462 | 0 | lws_metrics_caliper_report_hist(h->cal_txn, wsi); |
463 | 0 | lwsl_info("%s: %s CLIENT_CONNECTION_ERROR: %s\n", __func__, |
464 | 0 | h->lc.gutag, in ? (const char *)in : "none"); |
465 | 0 | if (h->ss_dangling_connected) { |
466 | | /* already disconnected, no action for DISCONNECT_ME */ |
467 | 0 | r = lws_ss_event_helper(h, LWSSSCS_DISCONNECTED); |
468 | 0 | if (r != LWSSSSRET_OK) |
469 | 0 | return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h); |
470 | 0 | } else { |
471 | | /* already disconnected, no action for DISCONNECT_ME */ |
472 | 0 | r = lws_ss_event_helper(h, LWSSSCS_UNREACHABLE); |
473 | 0 | if (r) { |
474 | 0 | if (h->inside_connect) { |
475 | 0 | h->pending_ret = r; |
476 | 0 | break; |
477 | 0 | } |
478 | | |
479 | 0 | return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h); |
480 | 0 | } |
481 | 0 | } |
482 | | |
483 | 0 | h->wsi = NULL; |
484 | 0 | r = lws_ss_backoff(h); |
485 | 0 | if (r != LWSSSSRET_OK) { |
486 | 0 | if (h->inside_connect) { |
487 | 0 | h->pending_ret = r; |
488 | 0 | break; |
489 | 0 | } |
490 | 0 | return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h); |
491 | 0 | } |
492 | 0 | break; |
493 | | |
494 | 0 | case LWS_CALLBACK_CLIENT_HTTP_REDIRECT: |
495 | |
|
496 | 0 | if (!h) |
497 | 0 | return -1; |
498 | | |
499 | 0 | if (h->policy->u.http.fail_redirect) |
500 | 0 | lws_system_cpd_set(lws_get_context(wsi), |
501 | 0 | LWS_CPD_CAPTIVE_PORTAL); |
502 | | /* unless it's explicitly allowed, reject to follow it */ |
503 | 0 | return !(h->policy->flags & LWSSSPOLF_ALLOW_REDIRECTS); |
504 | | |
505 | 0 | case LWS_CALLBACK_CLOSED_HTTP: /* server */ |
506 | 0 | case LWS_CALLBACK_CLOSED_CLIENT_HTTP: |
507 | 0 | if (!h) |
508 | 0 | break; |
509 | | |
510 | 0 | lws_sul_cancel(&h->sul_timeout); |
511 | |
|
512 | 0 | lws_ss_assert_extant(wsi->a.context, wsi->tsi, h); |
513 | |
|
514 | 0 | #if defined(LWS_WITH_CONMON) |
515 | 0 | if (wsi->conmon.pcol == LWSCONMON_PCOL_NONE) { |
516 | 0 | wsi->conmon.pcol = LWSCONMON_PCOL_HTTP; |
517 | 0 | wsi->conmon.protocol_specific.http.response = |
518 | 0 | (int)lws_http_client_http_response(wsi); |
519 | 0 | } |
520 | |
|
521 | 0 | lws_conmon_ss_json(h); |
522 | 0 | #endif |
523 | |
|
524 | 0 | lws_metrics_caliper_report_hist(h->cal_txn, wsi); |
525 | | //lwsl_notice("%s: %s LWS_CALLBACK_CLOSED_CLIENT_HTTP\n", |
526 | | // __func__, wsi->lc.gutag); |
527 | |
|
528 | 0 | h->wsi = NULL; |
529 | 0 | h->hanging_som = 0; |
530 | 0 | h->subseq = 0; |
531 | |
|
532 | 0 | #if defined(LWS_WITH_SERVER) |
533 | 0 | lws_pt_lock(pt, __func__); |
534 | 0 | lws_dll2_remove(&h->cli_list); |
535 | 0 | lws_pt_unlock(pt); |
536 | 0 | #endif |
537 | |
|
538 | 0 | if (h->policy && !(h->policy->flags & LWSSSPOLF_OPPORTUNISTIC) && |
539 | 0 | #if defined(LWS_WITH_SERVER) |
540 | 0 | !(h->info.flags & LWSSSINFLAGS_ACCEPTED) && /* not server */ |
541 | 0 | #endif |
542 | 0 | !h->txn_ok && !wsi->a.context->being_destroyed) { |
543 | 0 | r = lws_ss_backoff(h); |
544 | 0 | if (r != LWSSSSRET_OK) |
545 | 0 | return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h); |
546 | 0 | break; |
547 | 0 | } else |
548 | 0 | h->seqstate = SSSEQ_IDLE; |
549 | | |
550 | 0 | if (h->ss_dangling_connected) { |
551 | | /* already disconnected, no action for DISCONNECT_ME */ |
552 | 0 | r = lws_ss_event_helper(h, LWSSSCS_DISCONNECTED); |
553 | 0 | if (r == LWSSSSRET_DESTROY_ME) |
554 | 0 | return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h); |
555 | 0 | } |
556 | 0 | break; |
557 | | |
558 | 0 | case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: |
559 | |
|
560 | 0 | if (!h) |
561 | 0 | return -1; |
562 | | |
563 | 0 | lws_ss_assert_extant(wsi->a.context, wsi->tsi, h); |
564 | 0 | h->wsi = wsi; /* since we accept the wsi is bound to the SS, |
565 | | * ensure the SS feels the same way about the wsi */ |
566 | |
|
567 | 0 | #if defined(LWS_WITH_CONMON) |
568 | 0 | if (wsi->conmon.pcol == LWSCONMON_PCOL_NONE) { |
569 | 0 | wsi->conmon.pcol = LWSCONMON_PCOL_HTTP; |
570 | 0 | wsi->conmon.protocol_specific.http.response = |
571 | 0 | (int)lws_http_client_http_response(wsi); |
572 | 0 | } |
573 | |
|
574 | 0 | lws_conmon_ss_json(h); |
575 | 0 | #endif |
576 | |
|
577 | 0 | status = (int)lws_http_client_http_response(wsi); |
578 | 0 | lwsl_info("%s: LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: %d\n", __func__, status); |
579 | | // if (!status) |
580 | | /* it's just telling use we connected / joined the nwsi */ |
581 | | // break; |
582 | |
|
583 | | #if defined(LWS_WITH_SYS_METRICS) |
584 | | if (status) { |
585 | | lws_snprintf((char *)buf, 10, "%d", status); |
586 | | lws_metrics_tag_ss_add(h, "http_resp", (char *)buf); |
587 | | } |
588 | | #endif |
589 | |
|
590 | 0 | if (status == HTTP_STATUS_SERVICE_UNAVAILABLE /* 503 */ || |
591 | 0 | status == 429 /* Too many requests */) { |
592 | | /* |
593 | | * We understand this attempt failed, and that we should |
594 | | * conceal this attempt. If there's a specified |
595 | | * retry-after, we should use that if larger than our |
596 | | * computed backoff |
597 | | */ |
598 | |
|
599 | 0 | inter = 0; |
600 | 0 | lws_http_check_retry_after(wsi, &inter); |
601 | |
|
602 | 0 | r = _lws_ss_backoff(h, inter); |
603 | 0 | if (r != LWSSSSRET_OK) |
604 | 0 | return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h); |
605 | | |
606 | 0 | return -1; /* end this stream */ |
607 | 0 | } |
608 | | |
609 | 0 | if (h->policy->u.http.resp_expect) |
610 | 0 | h->u.http.good_respcode = |
611 | 0 | status == h->policy->u.http.resp_expect; |
612 | 0 | else |
613 | 0 | h->u.http.good_respcode = (status >= 200 && status < 300); |
614 | | // lwsl_err("%s: good resp %d %d\n", __func__, status, h->u.http.good_respcode); |
615 | |
|
616 | 0 | if (lws_extract_metadata(h, wsi)) { |
617 | 0 | lwsl_info("%s: rx metadata extract failed\n", __func__); |
618 | |
|
619 | 0 | return -1; |
620 | 0 | } |
621 | | |
622 | 0 | if (status) { |
623 | | /* |
624 | | * Check and see if it's something from the response |
625 | | * map, if so, generate the requested status. If we're |
626 | | * the proxy onward connection, metadata has priority |
627 | | * over state updates on the serialization, so the |
628 | | * state callback will see the right metadata. |
629 | | */ |
630 | 0 | int n = lws_ss_http_resp_to_state(h, status); |
631 | 0 | if (n) { |
632 | 0 | r = lws_ss_event_helper(h, (lws_ss_constate_t)n); |
633 | 0 | if (r != LWSSSSRET_OK) |
634 | 0 | return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, |
635 | 0 | &h); |
636 | 0 | } |
637 | 0 | } |
638 | | |
639 | 0 | if (h->u.http.good_respcode) |
640 | 0 | lwsl_info("%s: Connected streamtype %s, %d\n", __func__, |
641 | 0 | h->policy->streamtype, status); |
642 | 0 | else |
643 | 0 | if (h->u.http.good_respcode) |
644 | 0 | lwsl_warn("%s: Connected streamtype %s, BAD %d\n", |
645 | 0 | __func__, h->policy->streamtype, |
646 | 0 | status); |
647 | |
|
648 | 0 | h->hanging_som = 0; |
649 | |
|
650 | 0 | h->retry = 0; |
651 | 0 | h->seqstate = SSSEQ_CONNECTED; |
652 | 0 | lws_sul_cancel(&h->sul); |
653 | |
|
654 | 0 | if (h->prev_ss_state != LWSSSCS_CONNECTED) { |
655 | 0 | wsi->client_suppress_CONNECTION_ERROR = 1; |
656 | | /* |
657 | | * back-to-back http transactions otherwise go |
658 | | * DISCONNECTED -> CONNECTED, we should insert |
659 | | * CONNECTING inbetween |
660 | | */ |
661 | 0 | if (h->prev_ss_state == LWSSSCS_DISCONNECTED) { |
662 | 0 | r = lws_ss_event_helper(h, LWSSSCS_CONNECTING); |
663 | 0 | if (r != LWSSSSRET_OK) |
664 | 0 | return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h); |
665 | 0 | } |
666 | 0 | if (h->prev_ss_state != LWSSSCS_CONNECTED) { |
667 | 0 | r = lws_ss_event_helper(h, LWSSSCS_CONNECTED); |
668 | 0 | if (r != LWSSSSRET_OK) |
669 | 0 | return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h); |
670 | 0 | } |
671 | 0 | } |
672 | | |
673 | | /* |
674 | | * Since it's an http transaction we initiated... this is |
675 | | * proof of connection validity |
676 | | */ |
677 | 0 | lws_validity_confirmed(wsi); |
678 | |
|
679 | 0 | #if defined(LWS_WITH_SS_RIDESHARE) |
680 | | |
681 | | /* |
682 | | * There are two ways we might want to deal with multipart, |
683 | | * one is pass it through raw (although the user code needs |
684 | | * a helping hand for learning the boundary), and the other |
685 | | * is to deframe it and provide basically submessages in the |
686 | | * different parts. |
687 | | */ |
688 | |
|
689 | 0 | if (lws_hdr_copy(wsi, (char *)buf, sizeof(buf), |
690 | 0 | WSI_TOKEN_HTTP_CONTENT_TYPE) > 0 && |
691 | | /* multipart/form-data; |
692 | | * boundary=----WebKitFormBoundarycc7YgAPEIHvgE9Bf */ |
693 | |
|
694 | 0 | (!strncmp((char *)buf, "multipart/form-data", 19) || |
695 | 0 | !strncmp((char *)buf, "multipart/related", 17))) { |
696 | 0 | struct lws_tokenize ts; |
697 | 0 | lws_tokenize_elem e; |
698 | | |
699 | | // puts((const char *)buf); |
700 | |
|
701 | 0 | memset(&ts, 0, sizeof(ts)); |
702 | 0 | ts.start = (char *)buf; |
703 | 0 | ts.len = strlen(ts.start); |
704 | 0 | ts.flags = LWS_TOKENIZE_F_RFC7230_DELIMS | |
705 | 0 | LWS_TOKENIZE_F_SLASH_NONTERM | |
706 | 0 | LWS_TOKENIZE_F_MINUS_NONTERM; |
707 | |
|
708 | 0 | h->u.http.boundary[0] = '\0'; |
709 | 0 | do { |
710 | 0 | e = lws_tokenize(&ts); |
711 | 0 | if (e == LWS_TOKZE_TOKEN_NAME_EQUALS && |
712 | 0 | !strncmp(ts.token, "boundary", 8) && |
713 | 0 | ts.token_len == 8) { |
714 | 0 | e = lws_tokenize(&ts); |
715 | 0 | if (e != LWS_TOKZE_TOKEN) |
716 | 0 | goto malformed; |
717 | 0 | h->u.http.boundary[0] = '\x0d'; |
718 | 0 | h->u.http.boundary[1] = '\x0a'; |
719 | 0 | h->u.http.boundary[2] = '-'; |
720 | 0 | h->u.http.boundary[3] = '-'; |
721 | 0 | lws_strnncpy(h->u.http.boundary + 4, |
722 | 0 | ts.token, ts.token_len, |
723 | 0 | sizeof(h->u.http.boundary) - 4); |
724 | 0 | h->u.http.boundary_len = |
725 | 0 | (uint8_t)(ts.token_len + 4); |
726 | 0 | h->u.http.boundary_seq = 2; |
727 | 0 | h->u.http.boundary_dashes = 0; |
728 | 0 | } |
729 | 0 | } while (e > 0); |
730 | 0 | lwsl_info("%s: multipart boundary '%s' len %d\n", __func__, |
731 | 0 | h->u.http.boundary, h->u.http.boundary_len); |
732 | | |
733 | | /* inform the ss that a related message group begins */ |
734 | |
|
735 | 0 | if ((h->policy->flags & LWSSSPOLF_HTTP_MULTIPART_IN) && |
736 | 0 | h->u.http.boundary[0]) |
737 | 0 | h->info.rx(ss_to_userobj(h), NULL, 0, |
738 | 0 | LWSSS_FLAG_RELATED_START); |
739 | | |
740 | | // lws_header_table_detach(wsi, 0); |
741 | 0 | } |
742 | 0 | break; |
743 | 0 | malformed: |
744 | 0 | lwsl_notice("%s: malformed multipart header\n", __func__); |
745 | 0 | return -1; |
746 | | #else |
747 | | break; |
748 | | #endif |
749 | | |
750 | 0 | case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER: |
751 | 0 | if (!h) |
752 | 0 | return -1; |
753 | 0 | if (h->writeable_len) |
754 | 0 | wsi->http.writeable_len = h->writeable_len; |
755 | |
|
756 | 0 | { |
757 | 0 | uint8_t **p = (uint8_t **)in, *end = (*p) + len, |
758 | 0 | *oin = *(uint8_t **)in; |
759 | | |
760 | | /* |
761 | | * blob-based headers |
762 | | */ |
763 | |
|
764 | 0 | for (m = 0; m < _LWSSS_HBI_COUNT; m++) { |
765 | 0 | lws_system_blob_t *ab; |
766 | 0 | int o = 0, n; |
767 | |
|
768 | 0 | if (!h->policy->u.http.blob_header[m]) |
769 | 0 | continue; |
770 | | |
771 | | /* |
772 | | * To be backward compatible, default is system-wide LWA auth, |
773 | | * and "http_auth_header" is for default LWA auth, current users do not |
774 | | * need any change in their policy. |
775 | | * If user wants different auth/token, need to specify the "use_auth" |
776 | | * and will be handled after metadata headers are applied. |
777 | | */ |
778 | | |
779 | 0 | if (m == LWSSS_HBI_AUTH && |
780 | 0 | h->policy->u.http.auth_preamble) |
781 | 0 | o = lws_snprintf((char *)buf, sizeof(buf), "%s", |
782 | 0 | h->policy->u.http.auth_preamble); |
783 | |
|
784 | 0 | if (o > (int)sizeof(buf) - 2) |
785 | 0 | return -1; |
786 | | |
787 | 0 | ab = lws_system_get_blob(wsi->a.context, blob_idx[m], 0); |
788 | 0 | if (!ab) |
789 | 0 | return -1; |
790 | | |
791 | 0 | buflen = sizeof(buf) - (unsigned int)o - 2u; |
792 | 0 | n = lws_system_blob_get(ab, buf + o, &buflen, 0); |
793 | 0 | if (n < 0) |
794 | 0 | return -1; |
795 | | |
796 | 0 | buf[(unsigned int)o + buflen] = '\0'; |
797 | 0 | lwsl_debug("%s: adding blob %d: %s\n", __func__, m, buf); |
798 | |
|
799 | 0 | if (lws_add_http_header_by_name(wsi, |
800 | 0 | (uint8_t *)h->policy->u.http.blob_header[m], |
801 | 0 | buf, (int)((int)buflen + o), p, end)) |
802 | 0 | return -1; |
803 | 0 | } |
804 | | |
805 | | /* |
806 | | * metadata-based headers |
807 | | */ |
808 | | |
809 | 0 | if (lws_apply_metadata(h, wsi, buf, p, end)) |
810 | 0 | return -1; |
811 | | |
812 | | #if defined(LWS_WITH_SS_DIRECT_PROTOCOL_STR) |
813 | | if (h->policy->flags & LWSSSPOLF_DIRECT_PROTO_STR) { |
814 | | if (lws_apply_instant_metadata(h, wsi, buf, p, end)) |
815 | | return -1; |
816 | | } |
817 | | #endif |
818 | | |
819 | | #if defined(LWS_WITH_SECURE_STREAMS_AUTH_SIGV4) |
820 | | if (h->policy->auth && h->policy->auth->type && |
821 | | !strcmp(h->policy->auth->type, "sigv4")) { |
822 | | |
823 | | if (lws_ss_apply_sigv4(wsi, h, p, end)) |
824 | | return -1; |
825 | | } |
826 | | #endif |
827 | | |
828 | | |
829 | 0 | (void)oin; |
830 | | //if (*p != oin) |
831 | | // lwsl_hexdump_notice(oin, lws_ptr_diff_size_t(*p, oin)); |
832 | |
|
833 | 0 | } |
834 | | |
835 | | /* |
836 | | * So when proxied, for POST we have to synthesize a CONNECTED |
837 | | * state, so it can request a writeable and deliver the POST |
838 | | * body |
839 | | */ |
840 | 0 | if ((h->policy->protocol == LWSSSP_H1 || |
841 | 0 | h->policy->protocol == LWSSSP_H2) && |
842 | 0 | h->being_serialized && ( |
843 | 0 | !strcmp(h->policy->u.http.method, "PUT") || |
844 | 0 | !strcmp(h->policy->u.http.method, "PATCH") || |
845 | 0 | !strcmp(h->policy->u.http.method, "POST"))) { |
846 | |
|
847 | 0 | wsi->client_suppress_CONNECTION_ERROR = 1; |
848 | 0 | if (h->prev_ss_state != LWSSSCS_CONNECTED) { |
849 | 0 | r = lws_ss_event_helper(h, LWSSSCS_CONNECTED); |
850 | 0 | if (r) |
851 | 0 | return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h); |
852 | 0 | } |
853 | 0 | } |
854 | | |
855 | 0 | break; |
856 | | |
857 | | /* chunks of chunked content, with header removed */ |
858 | 0 | case LWS_CALLBACK_HTTP_BODY: |
859 | 0 | case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: |
860 | 0 | lwsl_debug("%s: RECEIVE_CLIENT_HTTP_READ: read %d\n", |
861 | 0 | __func__, (int)len); |
862 | 0 | if (!h || !h->info.rx) |
863 | 0 | return 0; |
864 | | |
865 | 0 | #if defined(LWS_WITH_SS_RIDESHARE) |
866 | 0 | if ((h->policy->flags & LWSSSPOLF_HTTP_MULTIPART_IN) && |
867 | 0 | h->u.http.boundary[0]) |
868 | 0 | return ss_http_multipart_parser(h, in, len); |
869 | 0 | #endif |
870 | | |
871 | 0 | if (!h->subseq) { |
872 | 0 | f |= LWSSS_FLAG_SOM; |
873 | 0 | h->hanging_som = 1; |
874 | 0 | h->subseq = 1; |
875 | 0 | } |
876 | | |
877 | | // lwsl_notice("%s: HTTP_READ: client side sent len %d fl 0x%x\n", |
878 | | // __func__, (int)len, (int)f); |
879 | |
|
880 | 0 | h->wsi = wsi; /* since we accept the wsi is bound to the SS, |
881 | | * ensure the SS feels the same way about the wsi */ |
882 | 0 | r = h->info.rx(ss_to_userobj(h), (const uint8_t *)in, len, f); |
883 | 0 | if (r != LWSSSSRET_OK) |
884 | 0 | return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h); |
885 | | |
886 | 0 | return 0; /* don't passthru */ |
887 | | |
888 | | /* uninterpreted http content */ |
889 | 0 | case LWS_CALLBACK_RECEIVE_CLIENT_HTTP: |
890 | 0 | { |
891 | 0 | char *px = (char *)buf + LWS_PRE; /* guarantees LWS_PRE */ |
892 | 0 | int lenx = sizeof(buf) - LWS_PRE; |
893 | |
|
894 | 0 | m = lws_http_client_read(wsi, &px, &lenx); |
895 | 0 | if (m < 0) |
896 | 0 | return m; |
897 | 0 | } |
898 | 0 | lws_set_timeout(wsi, 99, 30); |
899 | |
|
900 | 0 | return 0; /* don't passthru */ |
901 | | |
902 | 0 | case LWS_CALLBACK_COMPLETED_CLIENT_HTTP: |
903 | | // lwsl_debug("%s: LWS_CALLBACK_COMPLETED_CLIENT_HTTP\n", __func__); |
904 | |
|
905 | 0 | if (!h) |
906 | 0 | return -1; |
907 | | |
908 | 0 | if (h->hanging_som) { |
909 | 0 | h->info.rx(ss_to_userobj(h), NULL, 0, LWSSS_FLAG_EOM); |
910 | 0 | h->hanging_som = 0; |
911 | 0 | h->subseq = 0; |
912 | 0 | } |
913 | |
|
914 | 0 | wsi->http.writeable_len = h->writeable_len = 0; |
915 | 0 | lws_sul_cancel(&h->sul_timeout); |
916 | |
|
917 | 0 | h->txn_ok = 1; |
918 | |
|
919 | | #if defined(LWS_WITH_SYS_METRICS) |
920 | | lws_metrics_tag_ss_add(h, "result", |
921 | | h->u.http.good_respcode ? |
922 | | "SS_ACK_REMOTE" : "SS_NACK_REMOTE"); |
923 | | #endif |
924 | |
|
925 | 0 | r = lws_ss_event_helper(h, h->u.http.good_respcode ? |
926 | 0 | LWSSSCS_QOS_ACK_REMOTE : |
927 | 0 | LWSSSCS_QOS_NACK_REMOTE); |
928 | 0 | if (r != LWSSSSRET_OK) |
929 | 0 | return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h); |
930 | | |
931 | 0 | lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */ |
932 | 0 | break; |
933 | | |
934 | 0 | case LWS_CALLBACK_HTTP_WRITEABLE: |
935 | 0 | case LWS_CALLBACK_CLIENT_HTTP_WRITEABLE: |
936 | |
|
937 | 0 | if (!h || !h->info.tx) { |
938 | 0 | lwsl_debug("%s: no handle / tx\n", __func__); |
939 | 0 | return 0; |
940 | 0 | } |
941 | | |
942 | 0 | #if defined(LWS_WITH_SERVER) |
943 | 0 | if (h->txn_resp_pending) { |
944 | | /* |
945 | | * If we're going to start sending something, we need to |
946 | | * to take care of the http response header for it first |
947 | | */ |
948 | 0 | h->txn_resp_pending = 0; |
949 | |
|
950 | 0 | if (lws_add_http_common_headers(wsi, |
951 | 0 | (unsigned int)(h->txn_resp_set ? |
952 | 0 | (h->txn_resp ? h->txn_resp : 200) : |
953 | 0 | HTTP_STATUS_NOT_FOUND), |
954 | 0 | NULL, |
955 | 0 | h->policy->flags & LWSSSPOLF_HTTP_NO_CONTENT_LENGTH ? |
956 | 0 | LWS_ILLEGAL_HTTP_CONTENT_LEN : |
957 | 0 | h->wsi->http.writeable_len, |
958 | 0 | &p, end)) |
959 | 0 | return 1; |
960 | | |
961 | | /* |
962 | | * metadata-based headers |
963 | | */ |
964 | | |
965 | 0 | if (lws_apply_metadata(h, wsi, buf, &p, end)) |
966 | 0 | return -1; |
967 | | |
968 | 0 | if (lws_finalize_write_http_header(wsi, start, &p, end)) |
969 | 0 | return 1; |
970 | | |
971 | | /* write the body separately */ |
972 | 0 | lws_callback_on_writable(wsi); |
973 | |
|
974 | 0 | return 0; |
975 | 0 | } |
976 | 0 | #endif |
977 | | |
978 | 0 | if ( |
979 | 0 | #if defined(LWS_WITH_SERVER) |
980 | 0 | !(h->info.flags & LWSSSINFLAGS_ACCEPTED) && /* not accepted */ |
981 | 0 | #endif |
982 | 0 | !h->rideshare) |
983 | | |
984 | 0 | h->rideshare = h->policy; |
985 | |
|
986 | 0 | #if defined(LWS_WITH_SS_RIDESHARE) |
987 | 0 | if ( |
988 | 0 | #if defined(LWS_WITH_SERVER) |
989 | 0 | !(h->info.flags & LWSSSINFLAGS_ACCEPTED) && /* not accepted */ |
990 | 0 | #endif |
991 | 0 | !h->inside_msg && h->rideshare->u.http.multipart_name) |
992 | 0 | lws_client_http_multipart(wsi, |
993 | 0 | h->rideshare->u.http.multipart_name, |
994 | 0 | h->rideshare->u.http.multipart_filename, |
995 | 0 | h->rideshare->u.http.multipart_content_type, |
996 | 0 | (char **)&p, (char *)end); |
997 | |
|
998 | 0 | buflen = lws_ptr_diff_size_t(end, p); |
999 | 0 | if (h->policy->u.http.multipart_name) |
1000 | 0 | buflen -= 24; /* allow space for end of multipart */ |
1001 | | #else |
1002 | | buflen = lws_ptr_diff_size_t(end, p); |
1003 | | #endif |
1004 | 0 | r = h->info.tx(ss_to_userobj(h), h->txord++, p, &buflen, &f); |
1005 | 0 | if (r == LWSSSSRET_TX_DONT_SEND) |
1006 | 0 | return 0; |
1007 | 0 | if (r < 0) |
1008 | 0 | return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h); |
1009 | | |
1010 | | // lwsl_notice("%s: WRITEABLE: user tx says len %d fl 0x%x\n", |
1011 | | // __func__, (int)buflen, (int)f); |
1012 | | |
1013 | 0 | p += buflen; |
1014 | |
|
1015 | 0 | if (f & LWSSS_FLAG_EOM) { |
1016 | 0 | #if defined(LWS_WITH_SERVER) |
1017 | 0 | if (!(h->info.flags & LWSSSINFLAGS_ACCEPTED)) { |
1018 | 0 | #endif |
1019 | 0 | conceal_eom = 1; |
1020 | | /* end of rideshares */ |
1021 | 0 | if (!h->rideshare->rideshare_streamtype) { |
1022 | 0 | lws_client_http_body_pending(wsi, 0); |
1023 | 0 | #if defined(LWS_WITH_SS_RIDESHARE) |
1024 | 0 | if (h->rideshare->u.http.multipart_name) |
1025 | 0 | lws_client_http_multipart(wsi, NULL, NULL, NULL, |
1026 | 0 | (char **)&p, (char *)end); |
1027 | 0 | conceal_eom = 0; |
1028 | 0 | #endif |
1029 | 0 | } else { |
1030 | 0 | h->rideshare = lws_ss_policy_lookup(wsi->a.context, |
1031 | 0 | h->rideshare->rideshare_streamtype); |
1032 | 0 | lws_callback_on_writable(wsi); |
1033 | 0 | } |
1034 | 0 | #if defined(LWS_WITH_SERVER) |
1035 | 0 | } |
1036 | 0 | #endif |
1037 | |
|
1038 | 0 | h->inside_msg = 0; |
1039 | 0 | } else { |
1040 | | /* otherwise we can spin with zero length writes */ |
1041 | 0 | if (!f && !lws_ptr_diff(p, buf + LWS_PRE)) |
1042 | 0 | break; |
1043 | 0 | h->inside_msg = 1; |
1044 | 0 | lws_callback_on_writable(wsi); |
1045 | 0 | } |
1046 | | |
1047 | 0 | lwsl_info("%s: lws_write %d %d\n", __func__, |
1048 | 0 | lws_ptr_diff(p, buf + LWS_PRE), f); |
1049 | |
|
1050 | 0 | if (lws_write(wsi, buf + LWS_PRE, lws_ptr_diff_size_t(p, buf + LWS_PRE), |
1051 | 0 | (!conceal_eom && (f & LWSSS_FLAG_EOM)) ? |
1052 | 0 | LWS_WRITE_HTTP_FINAL : LWS_WRITE_HTTP) != |
1053 | 0 | (int)lws_ptr_diff(p, buf + LWS_PRE)) { |
1054 | 0 | lwsl_err("%s: write failed\n", __func__); |
1055 | 0 | return -1; |
1056 | 0 | } |
1057 | | |
1058 | 0 | #if defined(LWS_WITH_SERVER) |
1059 | 0 | if ((h->info.flags & LWSSSINFLAGS_ACCEPTED) /* server */ && |
1060 | 0 | (f & LWSSS_FLAG_EOM) && |
1061 | 0 | lws_http_transaction_completed(wsi)) |
1062 | 0 | return -1; |
1063 | | #else |
1064 | | lws_set_timeout(wsi, 0, 0); |
1065 | | #endif |
1066 | 0 | break; |
1067 | | |
1068 | 0 | #if defined(LWS_WITH_SERVER) |
1069 | 0 | case LWS_CALLBACK_HTTP: |
1070 | |
|
1071 | 0 | if (!h) |
1072 | 0 | return -1; |
1073 | | |
1074 | 0 | if (h->wsi && h->wsi->mount_hit) |
1075 | 0 | break; |
1076 | | |
1077 | 0 | lwsl_info("%s: LWS_CALLBACK_HTTP\n", __func__); |
1078 | 0 | { |
1079 | |
|
1080 | 0 | h->txn_resp_set = 0; |
1081 | 0 | h->txn_resp_pending = 1; |
1082 | 0 | h->writeable_len = 0; |
1083 | |
|
1084 | 0 | #if defined(LWS_ROLE_H2) |
1085 | 0 | m = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_METHOD); |
1086 | 0 | if (m) { |
1087 | 0 | if (lws_ss_alloc_set_metadata(h, "method", |
1088 | 0 | lws_hdr_simple_ptr(wsi, |
1089 | 0 | WSI_TOKEN_HTTP_COLON_METHOD), (unsigned int)m)) |
1090 | 0 | return -1; |
1091 | 0 | m = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_PATH); |
1092 | 0 | if (m && lws_ss_alloc_set_metadata(h, "path", |
1093 | 0 | lws_hdr_simple_ptr(wsi, |
1094 | 0 | WSI_TOKEN_HTTP_COLON_PATH), (unsigned int)m)) |
1095 | 0 | return -1; |
1096 | 0 | } else |
1097 | 0 | #endif |
1098 | 0 | { |
1099 | 0 | m = lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI); |
1100 | 0 | if (m) { |
1101 | 0 | if (lws_ss_alloc_set_metadata(h, "path", |
1102 | 0 | lws_hdr_simple_ptr(wsi, |
1103 | 0 | WSI_TOKEN_GET_URI), (unsigned int)m)) |
1104 | 0 | return -1; |
1105 | 0 | if (lws_ss_alloc_set_metadata(h, "method", "GET", 3)) |
1106 | 0 | return -1; |
1107 | 0 | } else { |
1108 | 0 | m = lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI); |
1109 | 0 | if (m) { |
1110 | 0 | if (lws_ss_alloc_set_metadata(h, "path", |
1111 | 0 | lws_hdr_simple_ptr(wsi, |
1112 | 0 | WSI_TOKEN_POST_URI), (unsigned int)m)) |
1113 | 0 | return -1; |
1114 | 0 | if (lws_ss_alloc_set_metadata(h, "method", "POST", 4)) |
1115 | 0 | return -1; |
1116 | 0 | } else { |
1117 | 0 | m = lws_hdr_total_length(wsi, WSI_TOKEN_PATCH_URI); |
1118 | 0 | if (m) { |
1119 | 0 | if (lws_ss_alloc_set_metadata(h, "path", |
1120 | 0 | lws_hdr_simple_ptr(wsi, |
1121 | 0 | WSI_TOKEN_PATCH_URI), (unsigned int)m)) |
1122 | 0 | return -1; |
1123 | 0 | if (lws_ss_alloc_set_metadata(h, "method", "PATCH", 5)) |
1124 | 0 | return -1; |
1125 | 0 | } |
1126 | 0 | } |
1127 | 0 | } |
1128 | 0 | } |
1129 | 0 | } |
1130 | | |
1131 | 0 | if (!h->ss_dangling_connected) { |
1132 | | #if defined(LWS_WITH_SYS_METRICS) |
1133 | | /* |
1134 | | * If any hanging caliper measurement, dump it, and free any tags |
1135 | | */ |
1136 | | lws_metrics_caliper_report_hist(h->cal_txn, (struct lws *)NULL); |
1137 | | #endif |
1138 | 0 | wsi->client_suppress_CONNECTION_ERROR = 1; |
1139 | 0 | if (h->prev_ss_state != LWSSSCS_CONNECTED) { |
1140 | 0 | r = lws_ss_event_helper(h, LWSSSCS_CONNECTED); |
1141 | 0 | if (r) |
1142 | 0 | return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h); |
1143 | 0 | } |
1144 | 0 | } |
1145 | | |
1146 | 0 | r = lws_ss_event_helper(h, LWSSSCS_SERVER_TXN); |
1147 | 0 | if (r) |
1148 | 0 | return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, |
1149 | 0 | wsi, &h); |
1150 | | |
1151 | 0 | return 0; |
1152 | 0 | #endif |
1153 | | |
1154 | 0 | default: |
1155 | 0 | break; |
1156 | 0 | } |
1157 | | |
1158 | 0 | return lws_callback_http_dummy(wsi, reason, user, in, len); |
1159 | 0 | } |
1160 | | |
1161 | | const struct lws_protocols protocol_secstream_h1 = { |
1162 | | "lws-secstream-h1", |
1163 | | secstream_h1, |
1164 | | 0, 0, 0, NULL, 0 |
1165 | | }; |
1166 | | |
1167 | | /* |
1168 | | * Munge connect info according to protocol-specific considerations... this |
1169 | | * usually means interpreting aux in a protocol-specific way and using the |
1170 | | * pieces at connection setup time, eg, http url pieces. |
1171 | | * |
1172 | | * len bytes of buf can be used for things with scope until after the actual |
1173 | | * connect. |
1174 | | */ |
1175 | | |
1176 | | static int |
1177 | | secstream_connect_munge_h1(lws_ss_handle_t *h, char *buf, size_t len, |
1178 | | struct lws_client_connect_info *i, |
1179 | | union lws_ss_contemp *ct) |
1180 | 0 | { |
1181 | 0 | const char *pbasis = h->policy->u.http.url; |
1182 | 0 | size_t used_in, used_out; |
1183 | 0 | lws_strexp_t exp; |
1184 | | |
1185 | | /* i.path on entry is used to override the policy urlpath if not "" */ |
1186 | |
|
1187 | 0 | if (i->path[0]) |
1188 | 0 | pbasis = i->path; |
1189 | |
|
1190 | 0 | if (!pbasis) |
1191 | 0 | return 0; |
1192 | | |
1193 | | /* uncomment to force h1 */ |
1194 | | // i->alpn = "http/1.1"; |
1195 | | |
1196 | 0 | #if defined(LWS_WITH_SS_RIDESHARE) |
1197 | 0 | if (h->policy->flags & LWSSSPOLF_HTTP_MULTIPART) |
1198 | 0 | i->ssl_connection |= LCCSCF_HTTP_MULTIPART_MIME; |
1199 | |
|
1200 | 0 | if (h->policy->flags & LWSSSPOLF_HTTP_X_WWW_FORM_URLENCODED) |
1201 | 0 | i->ssl_connection |= LCCSCF_HTTP_X_WWW_FORM_URLENCODED; |
1202 | 0 | #endif |
1203 | |
|
1204 | 0 | if (h->policy->flags & LWSSSPOLF_HTTP_CACHE_COOKIES) |
1205 | 0 | i->ssl_connection |= LCCSCF_CACHE_COOKIES; |
1206 | | |
1207 | | /* protocol aux is the path part */ |
1208 | |
|
1209 | 0 | i->path = buf; |
1210 | | |
1211 | | /* skip the unnessary '/' */ |
1212 | 0 | if (*pbasis == '/') |
1213 | 0 | pbasis = pbasis + 1; |
1214 | |
|
1215 | 0 | buf[0] = '/'; |
1216 | |
|
1217 | 0 | lws_strexp_init(&exp, (void *)h, lws_ss_exp_cb_metadata, buf + 1, len - 1); |
1218 | |
|
1219 | 0 | if (lws_strexp_expand(&exp, pbasis, strlen(pbasis), |
1220 | 0 | &used_in, &used_out) != LSTRX_DONE) |
1221 | 0 | return 1; |
1222 | | |
1223 | 0 | return 0; |
1224 | 0 | } |
1225 | | |
1226 | | |
1227 | | const struct ss_pcols ss_pcol_h1 = { |
1228 | | "h1", |
1229 | | "http/1.1", |
1230 | | &protocol_secstream_h1, |
1231 | | secstream_connect_munge_h1, |
1232 | | NULL, NULL |
1233 | | }; |