/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 | #if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_HTTP_HEADERS_ALL) |
256 | 0 | !strcmp(h->policy->u.http.method, "PATCH") || |
257 | 0 | !strcmp(h->policy->u.http.method, "PUT") || |
258 | 0 | #endif |
259 | 0 | (!strcmp(h->policy->u.http.method, "POST"))) && |
260 | 0 | wsi->http.writeable_len) { |
261 | 0 | if (!(h->policy->flags & |
262 | 0 | LWSSSPOLF_HTTP_NO_CONTENT_LENGTH)) { |
263 | 0 | int n = lws_snprintf((char *)buf, 20, "%u", |
264 | 0 | (unsigned int)wsi->http.writeable_len); |
265 | 0 | if (lws_add_http_header_by_token(wsi, |
266 | 0 | WSI_TOKEN_HTTP_CONTENT_LENGTH, |
267 | 0 | buf, n, pp, end)) |
268 | 0 | return -1; |
269 | 0 | } |
270 | 0 | lws_client_http_body_pending(wsi, 1); |
271 | 0 | } |
272 | | |
273 | 0 | return 0; |
274 | 0 | } |
275 | | |
276 | | |
277 | | #if defined(LWS_WITH_SS_DIRECT_PROTOCOL_STR) |
278 | | static int |
279 | | lws_apply_instant_metadata(lws_ss_handle_t *h, struct lws *wsi, uint8_t *buf, |
280 | | uint8_t **pp, uint8_t *end) |
281 | | { |
282 | | lws_ss_metadata_t *imd = h->instant_metadata; |
283 | | |
284 | | while (imd) { |
285 | | if (imd->name && imd->value__may_own_heap) { |
286 | | lwsl_debug("%s add header %s %s %d\n", __func__, |
287 | | imd->name, |
288 | | (char *)imd->value__may_own_heap, |
289 | | (int)imd->length); |
290 | | if (lws_add_http_header_by_name(wsi, |
291 | | (const unsigned char *)imd->name, |
292 | | (const unsigned char *)imd->value__may_own_heap, |
293 | | (int)imd->length, pp, end)) |
294 | | return -1; |
295 | | |
296 | | /* it's possible user set content-length directly */ |
297 | | if (!strncmp(imd->name, |
298 | | "content-length", 14) && |
299 | | atoi(imd->value__may_own_heap)) |
300 | | lws_client_http_body_pending(wsi, 1); |
301 | | |
302 | | } |
303 | | |
304 | | imd = imd->next; |
305 | | } |
306 | | |
307 | | return 0; |
308 | | } |
309 | | #endif |
310 | | /* |
311 | | * Check if any metadata headers present in the server headers, and record |
312 | | * them into the associated metadata item if so. |
313 | | */ |
314 | | |
315 | | static int |
316 | | lws_extract_metadata(lws_ss_handle_t *h, struct lws *wsi) |
317 | 0 | { |
318 | 0 | lws_ss_metadata_t *polmd = h->policy->metadata, *omd; |
319 | 0 | int n; |
320 | |
|
321 | 0 | while (polmd) { |
322 | |
|
323 | 0 | if (polmd->value_is_http_token != LWS_HTTP_NO_KNOWN_HEADER) { |
324 | | |
325 | | /* it's a well-known header token */ |
326 | |
|
327 | 0 | n = lws_hdr_total_length(wsi, polmd->value_is_http_token); |
328 | 0 | if (n) { |
329 | 0 | const char *cp = lws_hdr_simple_ptr(wsi, |
330 | 0 | polmd->value_is_http_token); |
331 | 0 | omd = lws_ss_get_handle_metadata(h, polmd->name); |
332 | 0 | if (!omd || !cp) |
333 | 0 | return 1; |
334 | | |
335 | 0 | assert(!strcmp(omd->name, polmd->name)); |
336 | | |
337 | | /* |
338 | | * it's present on the wsi, we want to |
339 | | * set the related metadata name to it then |
340 | | */ |
341 | | |
342 | 0 | _lws_ss_alloc_set_metadata(omd, polmd->name, cp, |
343 | 0 | (unsigned int)n); |
344 | |
|
345 | | #if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) |
346 | | /* |
347 | | * ...and because we are doing it from parsing |
348 | | * onward rx, we want to mark the metadata as |
349 | | * needing passing to the client |
350 | | */ |
351 | | omd->pending_onward = 1; |
352 | | #endif |
353 | 0 | } |
354 | 0 | } |
355 | | |
356 | 0 | #if defined(LWS_WITH_CUSTOM_HEADERS) |
357 | 0 | else |
358 | | |
359 | | /* has to have a non-empty header string */ |
360 | | |
361 | 0 | if (polmd->value__may_own_heap && |
362 | 0 | ((uint8_t *)polmd->value__may_own_heap)[0]) { |
363 | 0 | char *p; |
364 | | |
365 | | /* |
366 | | * Can it be a custom header? |
367 | | */ |
368 | |
|
369 | 0 | n = lws_hdr_custom_length(wsi, (const char *) |
370 | 0 | polmd->value__may_own_heap, |
371 | 0 | polmd->value_length); |
372 | 0 | if (n > 0) { |
373 | |
|
374 | 0 | p = lws_malloc((unsigned int)n + 1, __func__); |
375 | 0 | if (!p) |
376 | 0 | return 1; |
377 | | |
378 | | /* if needed, free any previous value */ |
379 | | |
380 | 0 | if (polmd->value_on_lws_heap) { |
381 | 0 | lws_free( |
382 | 0 | polmd->value__may_own_heap); |
383 | 0 | polmd->value_on_lws_heap = 0; |
384 | 0 | } |
385 | | |
386 | | /* |
387 | | * copy the named custom header value |
388 | | * into the malloc'd buffer |
389 | | */ |
390 | |
|
391 | 0 | if (lws_hdr_custom_copy(wsi, p, n + 1, |
392 | 0 | (const char *) |
393 | 0 | polmd->value__may_own_heap, |
394 | 0 | polmd->value_length) < 0) { |
395 | 0 | lws_free(p); |
396 | |
|
397 | 0 | return 1; |
398 | 0 | } |
399 | | |
400 | 0 | omd = lws_ss_get_handle_metadata(h, |
401 | 0 | polmd->name); |
402 | 0 | if (omd) { |
403 | |
|
404 | 0 | _lws_ss_set_metadata(omd, |
405 | 0 | polmd->name, p, (size_t)n); |
406 | 0 | omd->value_on_lws_heap = 1; |
407 | |
|
408 | | #if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) |
409 | | omd->pending_onward = 1; |
410 | | #endif |
411 | 0 | } |
412 | 0 | } |
413 | 0 | } |
414 | 0 | #endif |
415 | | |
416 | 0 | polmd = polmd->next; |
417 | 0 | } |
418 | | |
419 | 0 | return 0; |
420 | 0 | } |
421 | | |
422 | | static const uint8_t blob_idx[] = { |
423 | | LWS_SYSBLOB_TYPE_AUTH, |
424 | | LWS_SYSBLOB_TYPE_DEVICE_SERIAL, |
425 | | LWS_SYSBLOB_TYPE_DEVICE_FW_VERSION, |
426 | | LWS_SYSBLOB_TYPE_DEVICE_TYPE, |
427 | | }; |
428 | | |
429 | | int |
430 | | secstream_h1(struct lws *wsi, enum lws_callback_reasons reason, void *user, |
431 | | void *in, size_t len) |
432 | 0 | { |
433 | 0 | #if defined(LWS_WITH_SERVER) |
434 | 0 | struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi]; |
435 | 0 | #endif |
436 | 0 | lws_ss_handle_t *h = (lws_ss_handle_t *)lws_get_opaque_user_data(wsi); |
437 | 0 | uint8_t buf[LWS_PRE + 1520], *p = &buf[LWS_PRE], |
438 | 0 | #if defined(LWS_WITH_SERVER) |
439 | 0 | *start = p, |
440 | 0 | #endif |
441 | 0 | *end = &buf[sizeof(buf) - 1]; |
442 | 0 | lws_ss_state_return_t r; |
443 | 0 | int f = 0, m, status; |
444 | 0 | char conceal_eom = 0; |
445 | 0 | lws_usec_t inter; |
446 | 0 | size_t buflen; |
447 | |
|
448 | 0 | switch (reason) { |
449 | | |
450 | 0 | case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: |
451 | 0 | if (!h) { |
452 | 0 | lwsl_err("%s: CCE with no ss handle %s\n", __func__, lws_wsi_tag(wsi)); |
453 | 0 | break; |
454 | 0 | } |
455 | | |
456 | 0 | lws_ss_assert_extant(wsi->a.context, wsi->tsi, h); |
457 | |
|
458 | 0 | assert(h->policy); |
459 | | |
460 | 0 | #if defined(LWS_WITH_CONMON) |
461 | 0 | lws_conmon_ss_json(h); |
462 | 0 | #endif |
463 | |
|
464 | 0 | lws_metrics_caliper_report_hist(h->cal_txn, wsi); |
465 | 0 | lwsl_info("%s: %s CLIENT_CONNECTION_ERROR: %s\n", __func__, |
466 | 0 | h->lc.gutag, in ? (const char *)in : "none"); |
467 | 0 | if (h->ss_dangling_connected) { |
468 | | /* already disconnected, no action for DISCONNECT_ME */ |
469 | 0 | r = lws_ss_event_helper(h, LWSSSCS_DISCONNECTED); |
470 | 0 | if (r != LWSSSSRET_OK) |
471 | 0 | return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h); |
472 | 0 | } else { |
473 | | /* already disconnected, no action for DISCONNECT_ME */ |
474 | 0 | r = lws_ss_event_helper(h, LWSSSCS_UNREACHABLE); |
475 | 0 | if (r) { |
476 | 0 | if (h->inside_connect) { |
477 | 0 | h->pending_ret = r; |
478 | 0 | break; |
479 | 0 | } |
480 | | |
481 | 0 | return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h); |
482 | 0 | } |
483 | 0 | } |
484 | | |
485 | 0 | h->wsi = NULL; |
486 | 0 | r = lws_ss_backoff(h); |
487 | 0 | if (r != LWSSSSRET_OK) { |
488 | 0 | if (h->inside_connect) { |
489 | 0 | h->pending_ret = r; |
490 | 0 | break; |
491 | 0 | } |
492 | 0 | return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h); |
493 | 0 | } |
494 | 0 | break; |
495 | | |
496 | 0 | case LWS_CALLBACK_CLIENT_HTTP_REDIRECT: |
497 | |
|
498 | 0 | if (!h) |
499 | 0 | return -1; |
500 | | |
501 | 0 | if (h->policy->u.http.fail_redirect) |
502 | 0 | lws_system_cpd_set(lws_get_context(wsi), |
503 | 0 | LWS_CPD_CAPTIVE_PORTAL); |
504 | | /* unless it's explicitly allowed, reject to follow it */ |
505 | 0 | return !(h->policy->flags & LWSSSPOLF_ALLOW_REDIRECTS); |
506 | | |
507 | 0 | case LWS_CALLBACK_CLOSED_HTTP: /* server */ |
508 | 0 | case LWS_CALLBACK_CLOSED_CLIENT_HTTP: |
509 | 0 | if (!h) |
510 | 0 | break; |
511 | | |
512 | 0 | h->txn_n_acked = 0; |
513 | 0 | lws_sul_cancel(&h->sul_timeout); |
514 | |
|
515 | 0 | lws_ss_assert_extant(wsi->a.context, wsi->tsi, h); |
516 | |
|
517 | 0 | #if defined(LWS_WITH_CONMON) |
518 | 0 | if (wsi->conmon.pcol == LWSCONMON_PCOL_NONE) { |
519 | 0 | wsi->conmon.pcol = LWSCONMON_PCOL_HTTP; |
520 | 0 | wsi->conmon.protocol_specific.http.response = |
521 | 0 | (int)lws_http_client_http_response(wsi); |
522 | 0 | } |
523 | |
|
524 | 0 | lws_conmon_ss_json(h); |
525 | 0 | #endif |
526 | |
|
527 | 0 | lws_metrics_caliper_report_hist(h->cal_txn, wsi); |
528 | | //lwsl_notice("%s: %s LWS_CALLBACK_CLOSED_CLIENT_HTTP\n", |
529 | | // __func__, wsi->lc.gutag); |
530 | |
|
531 | 0 | h->wsi = NULL; |
532 | 0 | h->hanging_som = 0; |
533 | 0 | h->subseq = 0; |
534 | |
|
535 | 0 | #if defined(LWS_WITH_SERVER) |
536 | 0 | lws_pt_lock(pt, __func__); |
537 | 0 | lws_dll2_remove(&h->cli_list); |
538 | 0 | lws_pt_unlock(pt); |
539 | 0 | #endif |
540 | |
|
541 | 0 | if (h->policy && !(h->policy->flags & LWSSSPOLF_OPPORTUNISTIC) && |
542 | 0 | #if defined(LWS_WITH_SERVER) |
543 | 0 | !(h->info.flags & LWSSSINFLAGS_ACCEPTED) && /* not server */ |
544 | 0 | #endif |
545 | 0 | !h->txn_ok && !wsi->a.context->being_destroyed) { |
546 | 0 | r = lws_ss_backoff(h); |
547 | 0 | if (r != LWSSSSRET_OK) |
548 | 0 | return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h); |
549 | 0 | break; |
550 | 0 | } else |
551 | 0 | h->seqstate = SSSEQ_IDLE; |
552 | | |
553 | 0 | if (h->ss_dangling_connected) { |
554 | | /* already disconnected, no action for DISCONNECT_ME */ |
555 | 0 | r = lws_ss_event_helper(h, LWSSSCS_DISCONNECTED); |
556 | 0 | if (r == LWSSSSRET_DESTROY_ME) |
557 | 0 | return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h); |
558 | 0 | } |
559 | 0 | break; |
560 | | |
561 | 0 | case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: |
562 | |
|
563 | 0 | if (!h) |
564 | 0 | return -1; |
565 | | |
566 | 0 | lws_ss_assert_extant(wsi->a.context, wsi->tsi, h); |
567 | 0 | h->wsi = wsi; /* since we accept the wsi is bound to the SS, |
568 | | * ensure the SS feels the same way about the wsi */ |
569 | |
|
570 | 0 | #if defined(LWS_WITH_CONMON) |
571 | 0 | if (wsi->conmon.pcol == LWSCONMON_PCOL_NONE) { |
572 | 0 | wsi->conmon.pcol = LWSCONMON_PCOL_HTTP; |
573 | 0 | wsi->conmon.protocol_specific.http.response = |
574 | 0 | (int)lws_http_client_http_response(wsi); |
575 | 0 | } |
576 | |
|
577 | 0 | lws_conmon_ss_json(h); |
578 | 0 | #endif |
579 | |
|
580 | 0 | status = (int)lws_http_client_http_response(wsi); |
581 | 0 | lwsl_info("%s: LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: %d\n", __func__, status); |
582 | | // if (!status) |
583 | | /* it's just telling use we connected / joined the nwsi */ |
584 | | // break; |
585 | |
|
586 | | #if defined(LWS_WITH_SYS_METRICS) |
587 | | if (status) { |
588 | | lws_snprintf((char *)buf, 10, "%d", status); |
589 | | lws_metrics_tag_ss_add(h, "http_resp", (char *)buf); |
590 | | } |
591 | | #endif |
592 | |
|
593 | 0 | if (status == HTTP_STATUS_SERVICE_UNAVAILABLE /* 503 */ || |
594 | 0 | status == 429 /* Too many requests */) { |
595 | | /* |
596 | | * We understand this attempt failed, and that we should |
597 | | * conceal this attempt. If there's a specified |
598 | | * retry-after, we should use that if larger than our |
599 | | * computed backoff |
600 | | */ |
601 | |
|
602 | 0 | inter = 0; |
603 | 0 | lws_http_check_retry_after(wsi, &inter); |
604 | |
|
605 | 0 | r = _lws_ss_backoff(h, inter); |
606 | 0 | if (r != LWSSSSRET_OK) |
607 | 0 | return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h); |
608 | | |
609 | 0 | return -1; /* end this stream */ |
610 | 0 | } |
611 | | |
612 | 0 | if (h->policy->u.http.resp_expect) |
613 | 0 | h->u.http.good_respcode = |
614 | 0 | status == h->policy->u.http.resp_expect; |
615 | 0 | else |
616 | 0 | h->u.http.good_respcode = (status >= 200 && status < 300); |
617 | | // lwsl_err("%s: good resp %d %d\n", __func__, status, h->u.http.good_respcode); |
618 | |
|
619 | 0 | if (lws_extract_metadata(h, wsi)) { |
620 | 0 | lwsl_info("%s: rx metadata extract failed\n", __func__); |
621 | |
|
622 | 0 | return -1; |
623 | 0 | } |
624 | | |
625 | 0 | if (status) { |
626 | | /* |
627 | | * Check and see if it's something from the response |
628 | | * map, if so, generate the requested status. If we're |
629 | | * the proxy onward connection, metadata has priority |
630 | | * over state updates on the serialization, so the |
631 | | * state callback will see the right metadata. |
632 | | */ |
633 | 0 | int n = lws_ss_http_resp_to_state(h, status); |
634 | 0 | if (n) { |
635 | 0 | r = lws_ss_event_helper(h, (lws_ss_constate_t)n); |
636 | 0 | if (r != LWSSSSRET_OK) |
637 | 0 | return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, |
638 | 0 | &h); |
639 | 0 | } |
640 | 0 | } |
641 | | |
642 | 0 | if (h->u.http.good_respcode) |
643 | 0 | lwsl_info("%s: Connected streamtype %s, %d\n", __func__, |
644 | 0 | h->policy->streamtype, status); |
645 | 0 | else |
646 | 0 | if (h->u.http.good_respcode) |
647 | 0 | lwsl_warn("%s: Connected streamtype %s, BAD %d\n", |
648 | 0 | __func__, h->policy->streamtype, |
649 | 0 | status); |
650 | |
|
651 | 0 | h->hanging_som = 0; |
652 | |
|
653 | 0 | h->retry = 0; |
654 | 0 | h->seqstate = SSSEQ_CONNECTED; |
655 | 0 | lws_sul_cancel(&h->sul); |
656 | |
|
657 | 0 | if (h->prev_ss_state != LWSSSCS_CONNECTED) { |
658 | 0 | wsi->client_suppress_CONNECTION_ERROR = 1; |
659 | | /* |
660 | | * back-to-back http transactions otherwise go |
661 | | * DISCONNECTED -> CONNECTED, we should insert |
662 | | * CONNECTING inbetween |
663 | | */ |
664 | 0 | if (h->prev_ss_state == LWSSSCS_DISCONNECTED) { |
665 | 0 | r = lws_ss_event_helper(h, LWSSSCS_CONNECTING); |
666 | 0 | if (r != LWSSSSRET_OK) |
667 | 0 | return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h); |
668 | 0 | } |
669 | 0 | if (h->prev_ss_state != LWSSSCS_CONNECTED && |
670 | 0 | h->prev_ss_state != LWSSSCS_QOS_ACK_REMOTE && |
671 | 0 | h->prev_ss_state != LWSSSCS_QOS_NACK_REMOTE) { |
672 | | // lwsl_ss_notice(h, "HTTP_ESTABLISHED"); |
673 | 0 | r = lws_ss_event_helper(h, LWSSSCS_CONNECTED); |
674 | 0 | if (r != LWSSSSRET_OK) |
675 | 0 | return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h); |
676 | 0 | } |
677 | 0 | } |
678 | | |
679 | | /* |
680 | | * Since it's an http transaction we initiated... this is |
681 | | * proof of connection validity |
682 | | */ |
683 | 0 | lws_validity_confirmed(wsi); |
684 | |
|
685 | 0 | #if defined(LWS_WITH_SS_RIDESHARE) |
686 | | |
687 | | /* |
688 | | * There are two ways we might want to deal with multipart, |
689 | | * one is pass it through raw (although the user code needs |
690 | | * a helping hand for learning the boundary), and the other |
691 | | * is to deframe it and provide basically submessages in the |
692 | | * different parts. |
693 | | */ |
694 | |
|
695 | 0 | if (lws_hdr_copy(wsi, (char *)buf, sizeof(buf), |
696 | 0 | WSI_TOKEN_HTTP_CONTENT_TYPE) > 0 && |
697 | | /* multipart/form-data; |
698 | | * boundary=----WebKitFormBoundarycc7YgAPEIHvgE9Bf */ |
699 | |
|
700 | 0 | (!strncmp((char *)buf, "multipart/form-data", 19) || |
701 | 0 | !strncmp((char *)buf, "multipart/related", 17))) { |
702 | 0 | struct lws_tokenize ts; |
703 | 0 | lws_tokenize_elem e; |
704 | | |
705 | | // puts((const char *)buf); |
706 | |
|
707 | 0 | memset(&ts, 0, sizeof(ts)); |
708 | 0 | ts.start = (char *)buf; |
709 | 0 | ts.len = strlen(ts.start); |
710 | 0 | ts.flags = LWS_TOKENIZE_F_RFC7230_DELIMS | |
711 | 0 | LWS_TOKENIZE_F_SLASH_NONTERM | |
712 | 0 | LWS_TOKENIZE_F_MINUS_NONTERM; |
713 | |
|
714 | 0 | h->u.http.boundary[0] = '\0'; |
715 | 0 | do { |
716 | 0 | e = lws_tokenize(&ts); |
717 | 0 | if (e == LWS_TOKZE_TOKEN_NAME_EQUALS && |
718 | 0 | !strncmp(ts.token, "boundary", 8) && |
719 | 0 | ts.token_len == 8) { |
720 | 0 | e = lws_tokenize(&ts); |
721 | 0 | if (e != LWS_TOKZE_TOKEN) |
722 | 0 | goto malformed; |
723 | 0 | h->u.http.boundary[0] = '\x0d'; |
724 | 0 | h->u.http.boundary[1] = '\x0a'; |
725 | 0 | h->u.http.boundary[2] = '-'; |
726 | 0 | h->u.http.boundary[3] = '-'; |
727 | 0 | lws_strnncpy(h->u.http.boundary + 4, |
728 | 0 | ts.token, ts.token_len, |
729 | 0 | sizeof(h->u.http.boundary) - 4); |
730 | 0 | h->u.http.boundary_len = |
731 | 0 | (uint8_t)(ts.token_len + 4); |
732 | 0 | h->u.http.boundary_seq = 2; |
733 | 0 | h->u.http.boundary_dashes = 0; |
734 | 0 | } |
735 | 0 | } while (e > 0); |
736 | 0 | lwsl_info("%s: multipart boundary '%s' len %d\n", __func__, |
737 | 0 | h->u.http.boundary, h->u.http.boundary_len); |
738 | | |
739 | | /* inform the ss that a related message group begins */ |
740 | |
|
741 | 0 | if ((h->policy->flags & LWSSSPOLF_HTTP_MULTIPART_IN) && |
742 | 0 | h->u.http.boundary[0]) |
743 | 0 | h->info.rx(ss_to_userobj(h), NULL, 0, |
744 | 0 | LWSSS_FLAG_RELATED_START); |
745 | | |
746 | | // lws_header_table_detach(wsi, 0); |
747 | 0 | } |
748 | 0 | break; |
749 | 0 | malformed: |
750 | 0 | lwsl_notice("%s: malformed multipart header\n", __func__); |
751 | 0 | return -1; |
752 | | #else |
753 | | break; |
754 | | #endif |
755 | | |
756 | 0 | case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER: |
757 | 0 | if (!h) |
758 | 0 | return -1; |
759 | 0 | if (h->writeable_len) |
760 | 0 | wsi->http.writeable_len = h->writeable_len; |
761 | |
|
762 | 0 | { |
763 | 0 | uint8_t **p = (uint8_t **)in, *end = (*p) + len, |
764 | 0 | *oin = *(uint8_t **)in; |
765 | | |
766 | | /* |
767 | | * blob-based headers |
768 | | */ |
769 | |
|
770 | 0 | for (m = 0; m < _LWSSS_HBI_COUNT; m++) { |
771 | 0 | lws_system_blob_t *ab; |
772 | 0 | int o = 0, n; |
773 | |
|
774 | 0 | if (!h->policy->u.http.blob_header[m]) |
775 | 0 | continue; |
776 | | |
777 | | /* |
778 | | * To be backward compatible, default is system-wide LWA auth, |
779 | | * and "http_auth_header" is for default LWA auth, current users do not |
780 | | * need any change in their policy. |
781 | | * If user wants different auth/token, need to specify the "use_auth" |
782 | | * and will be handled after metadata headers are applied. |
783 | | */ |
784 | | |
785 | 0 | if (m == LWSSS_HBI_AUTH && |
786 | 0 | h->policy->u.http.auth_preamble) |
787 | 0 | o = lws_snprintf((char *)buf, sizeof(buf), "%s", |
788 | 0 | h->policy->u.http.auth_preamble); |
789 | |
|
790 | 0 | if (o > (int)sizeof(buf) - 2) |
791 | 0 | return -1; |
792 | | |
793 | 0 | ab = lws_system_get_blob(wsi->a.context, blob_idx[m], 0); |
794 | 0 | if (!ab) |
795 | 0 | return -1; |
796 | | |
797 | 0 | buflen = sizeof(buf) - (unsigned int)o - 2u; |
798 | 0 | n = lws_system_blob_get(ab, buf + o, &buflen, 0); |
799 | 0 | if (n < 0) |
800 | 0 | return -1; |
801 | | |
802 | 0 | buf[(unsigned int)o + buflen] = '\0'; |
803 | 0 | lwsl_debug("%s: adding blob %d: %s\n", __func__, m, buf); |
804 | |
|
805 | 0 | if (lws_add_http_header_by_name(wsi, |
806 | 0 | (uint8_t *)h->policy->u.http.blob_header[m], |
807 | 0 | buf, (int)((int)buflen + o), p, end)) |
808 | 0 | return -1; |
809 | 0 | } |
810 | | |
811 | | /* |
812 | | * metadata-based headers |
813 | | */ |
814 | | |
815 | 0 | if (lws_apply_metadata(h, wsi, buf, p, end)) |
816 | 0 | return -1; |
817 | | |
818 | | #if defined(LWS_WITH_SS_DIRECT_PROTOCOL_STR) |
819 | | if (h->policy->flags & LWSSSPOLF_DIRECT_PROTO_STR) { |
820 | | if (lws_apply_instant_metadata(h, wsi, buf, p, end)) |
821 | | return -1; |
822 | | } |
823 | | #endif |
824 | | |
825 | | #if defined(LWS_WITH_SECURE_STREAMS_AUTH_SIGV4) |
826 | | if (h->policy->auth && h->policy->auth->type && |
827 | | !strcmp(h->policy->auth->type, "sigv4")) { |
828 | | |
829 | | if (lws_ss_apply_sigv4(wsi, h, p, end)) |
830 | | return -1; |
831 | | } |
832 | | #endif |
833 | | |
834 | | |
835 | 0 | (void)oin; |
836 | | //if (*p != oin) |
837 | | // lwsl_hexdump_notice(oin, lws_ptr_diff_size_t(*p, oin)); |
838 | |
|
839 | 0 | } |
840 | | |
841 | | /* |
842 | | * So when proxied, for POST we have to synthesize a CONNECTED |
843 | | * state, so it can request a writeable and deliver the POST |
844 | | * body |
845 | | */ |
846 | 0 | if ((h->policy->protocol == LWSSSP_H1 || |
847 | 0 | h->policy->protocol == LWSSSP_H2) && |
848 | 0 | h->being_serialized && ( |
849 | 0 | #if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_HTTP_HEADERS_ALL) |
850 | 0 | !strcmp(h->policy->u.http.method, "PUT") || |
851 | 0 | !strcmp(h->policy->u.http.method, "PATCH") || |
852 | 0 | #endif |
853 | 0 | !strcmp(h->policy->u.http.method, "POST"))) { |
854 | |
|
855 | 0 | wsi->client_suppress_CONNECTION_ERROR = 1; |
856 | 0 | if (h->prev_ss_state != LWSSSCS_CONNECTED) { |
857 | 0 | r = lws_ss_event_helper(h, LWSSSCS_CONNECTED); |
858 | 0 | if (r) |
859 | 0 | return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h); |
860 | 0 | } |
861 | 0 | } |
862 | | |
863 | 0 | break; |
864 | | |
865 | | /* chunks of chunked content, with header removed */ |
866 | 0 | case LWS_CALLBACK_HTTP_BODY: |
867 | 0 | case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: |
868 | 0 | lwsl_debug("%s: RECEIVE_CLIENT_HTTP_READ: read %d\n", |
869 | 0 | __func__, (int)len); |
870 | 0 | if (!h || !h->info.rx) |
871 | 0 | return 0; |
872 | | |
873 | 0 | #if defined(LWS_WITH_SS_RIDESHARE) |
874 | 0 | if ((h->policy->flags & LWSSSPOLF_HTTP_MULTIPART_IN) && |
875 | 0 | h->u.http.boundary[0]) |
876 | 0 | return ss_http_multipart_parser(h, in, len); |
877 | 0 | #endif |
878 | | |
879 | 0 | if (!h->subseq) { |
880 | 0 | f |= LWSSS_FLAG_SOM; |
881 | 0 | h->hanging_som = 1; |
882 | 0 | h->subseq = 1; |
883 | 0 | } |
884 | | |
885 | | // lwsl_notice("%s: HTTP_READ: client side sent len %d fl 0x%x\n", |
886 | | // __func__, (int)len, (int)f); |
887 | |
|
888 | 0 | h->wsi = wsi; /* since we accept the wsi is bound to the SS, |
889 | | * ensure the SS feels the same way about the wsi */ |
890 | 0 | r = h->info.rx(ss_to_userobj(h), (const uint8_t *)in, len, f); |
891 | 0 | if (r != LWSSSSRET_OK) |
892 | 0 | return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h); |
893 | | |
894 | 0 | return 0; /* don't passthru */ |
895 | | |
896 | | /* uninterpreted http content */ |
897 | 0 | case LWS_CALLBACK_RECEIVE_CLIENT_HTTP: |
898 | 0 | { |
899 | 0 | char *px = (char *)buf + LWS_PRE; /* guarantees LWS_PRE */ |
900 | 0 | int lenx = sizeof(buf) - LWS_PRE; |
901 | |
|
902 | 0 | m = lws_http_client_read(wsi, &px, &lenx); |
903 | 0 | if (m < 0) |
904 | 0 | return m; |
905 | 0 | } |
906 | 0 | lws_set_timeout(wsi, 99, 30); |
907 | |
|
908 | 0 | return 0; /* don't passthru */ |
909 | | |
910 | 0 | case LWS_CALLBACK_COMPLETED_CLIENT_HTTP: |
911 | | // lwsl_debug("%s: LWS_CALLBACK_COMPLETED_CLIENT_HTTP\n", __func__); |
912 | |
|
913 | 0 | if (!h) |
914 | 0 | return -1; |
915 | | |
916 | 0 | if (h->hanging_som) { |
917 | 0 | h->info.rx(ss_to_userobj(h), NULL, 0, LWSSS_FLAG_EOM); |
918 | 0 | h->hanging_som = 0; |
919 | 0 | h->subseq = 0; |
920 | 0 | } |
921 | |
|
922 | 0 | wsi->http.writeable_len = h->writeable_len = 0; |
923 | 0 | lws_sul_cancel(&h->sul_timeout); |
924 | |
|
925 | 0 | h->txn_ok = 1; |
926 | |
|
927 | | #if defined(LWS_WITH_SYS_METRICS) |
928 | | lws_metrics_tag_ss_add(h, "result", |
929 | | h->u.http.good_respcode ? |
930 | | "SS_ACK_REMOTE" : "SS_NACK_REMOTE"); |
931 | | #endif |
932 | |
|
933 | 0 | if (!h->ss_dangling_connected) { |
934 | 0 | r = lws_ss_event_helper(h, LWSSSCS_CONNECTED); |
935 | 0 | if (r != LWSSSSRET_OK) |
936 | 0 | return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h); |
937 | 0 | } |
938 | | |
939 | 0 | if (!h->txn_n_acked) { |
940 | 0 | h->txn_n_acked = 1; |
941 | 0 | r = lws_ss_event_helper(h, h->u.http.good_respcode ? |
942 | 0 | LWSSSCS_QOS_ACK_REMOTE : |
943 | 0 | LWSSSCS_QOS_NACK_REMOTE); |
944 | 0 | if (r != LWSSSSRET_OK) |
945 | 0 | return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h); |
946 | 0 | } |
947 | 0 | lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */ |
948 | 0 | break; |
949 | | |
950 | 0 | case LWS_CALLBACK_HTTP_WRITEABLE: |
951 | 0 | case LWS_CALLBACK_CLIENT_HTTP_WRITEABLE: |
952 | |
|
953 | 0 | if (!h || !h->info.tx) { |
954 | 0 | lwsl_debug("%s: no handle / tx\n", __func__); |
955 | 0 | return 0; |
956 | 0 | } |
957 | | |
958 | 0 | #if defined(LWS_WITH_SERVER) |
959 | 0 | if (h->txn_resp_pending) { |
960 | | /* |
961 | | * If we're going to start sending something, we need to |
962 | | * to take care of the http response header for it first |
963 | | */ |
964 | 0 | h->txn_resp_pending = 0; |
965 | |
|
966 | 0 | if (lws_add_http_common_headers(wsi, |
967 | 0 | (unsigned int)(h->txn_resp_set ? |
968 | 0 | (h->txn_resp ? h->txn_resp : 200) : |
969 | 0 | HTTP_STATUS_NOT_FOUND), |
970 | 0 | NULL, |
971 | 0 | h->policy->flags & LWSSSPOLF_HTTP_NO_CONTENT_LENGTH ? |
972 | 0 | LWS_ILLEGAL_HTTP_CONTENT_LEN : |
973 | 0 | h->wsi->http.writeable_len, |
974 | 0 | &p, end)) |
975 | 0 | return 1; |
976 | | |
977 | | /* |
978 | | * metadata-based headers |
979 | | */ |
980 | | |
981 | 0 | if (lws_apply_metadata(h, wsi, buf, &p, end)) |
982 | 0 | return -1; |
983 | | |
984 | 0 | if (lws_finalize_write_http_header(wsi, start, &p, end)) |
985 | 0 | return 1; |
986 | | |
987 | | /* write the body separately */ |
988 | 0 | lws_callback_on_writable(wsi); |
989 | |
|
990 | 0 | return 0; |
991 | 0 | } |
992 | 0 | #endif |
993 | | |
994 | 0 | if ( |
995 | 0 | #if defined(LWS_WITH_SERVER) |
996 | 0 | !(h->info.flags & LWSSSINFLAGS_ACCEPTED) && /* not accepted */ |
997 | 0 | #endif |
998 | 0 | !h->rideshare) |
999 | | |
1000 | 0 | h->rideshare = h->policy; |
1001 | |
|
1002 | 0 | #if defined(LWS_WITH_SS_RIDESHARE) |
1003 | 0 | if ( |
1004 | 0 | #if defined(LWS_WITH_SERVER) |
1005 | 0 | !(h->info.flags & LWSSSINFLAGS_ACCEPTED) && /* not accepted */ |
1006 | 0 | #endif |
1007 | 0 | !h->inside_msg && h->rideshare->u.http.multipart_name) |
1008 | 0 | lws_client_http_multipart(wsi, |
1009 | 0 | h->rideshare->u.http.multipart_name, |
1010 | 0 | h->rideshare->u.http.multipart_filename, |
1011 | 0 | h->rideshare->u.http.multipart_content_type, |
1012 | 0 | (char **)&p, (char *)end); |
1013 | |
|
1014 | 0 | buflen = lws_ptr_diff_size_t(end, p); |
1015 | 0 | if (h->policy->u.http.multipart_name) |
1016 | 0 | buflen -= 24; /* allow space for end of multipart */ |
1017 | | #else |
1018 | | buflen = lws_ptr_diff_size_t(end, p); |
1019 | | #endif |
1020 | 0 | r = h->info.tx(ss_to_userobj(h), h->txord++, p, &buflen, &f); |
1021 | 0 | if (r == LWSSSSRET_TX_DONT_SEND) |
1022 | 0 | return 0; |
1023 | 0 | if (r < 0) |
1024 | 0 | return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h); |
1025 | | |
1026 | | // lwsl_notice("%s: WRITEABLE: user tx says len %d fl 0x%x\n", |
1027 | | // __func__, (int)buflen, (int)f); |
1028 | | |
1029 | 0 | p += buflen; |
1030 | |
|
1031 | 0 | if (f & LWSSS_FLAG_EOM) { |
1032 | 0 | #if defined(LWS_WITH_SERVER) |
1033 | 0 | if (!(h->info.flags & LWSSSINFLAGS_ACCEPTED)) { |
1034 | 0 | #endif |
1035 | 0 | conceal_eom = 1; |
1036 | | /* end of rideshares */ |
1037 | 0 | if (!h->rideshare->rideshare_streamtype) { |
1038 | 0 | lws_client_http_body_pending(wsi, 0); |
1039 | 0 | #if defined(LWS_WITH_SS_RIDESHARE) |
1040 | 0 | if (h->rideshare->u.http.multipart_name) |
1041 | 0 | lws_client_http_multipart(wsi, NULL, NULL, NULL, |
1042 | 0 | (char **)&p, (char *)end); |
1043 | 0 | conceal_eom = 0; |
1044 | 0 | #endif |
1045 | 0 | } else { |
1046 | 0 | h->rideshare = lws_ss_policy_lookup(wsi->a.context, |
1047 | 0 | h->rideshare->rideshare_streamtype); |
1048 | 0 | lws_callback_on_writable(wsi); |
1049 | 0 | } |
1050 | 0 | #if defined(LWS_WITH_SERVER) |
1051 | 0 | } |
1052 | 0 | #endif |
1053 | |
|
1054 | 0 | h->inside_msg = 0; |
1055 | 0 | } else { |
1056 | | /* otherwise we can spin with zero length writes */ |
1057 | 0 | if (!f && !lws_ptr_diff(p, buf + LWS_PRE)) |
1058 | 0 | break; |
1059 | 0 | h->inside_msg = 1; |
1060 | 0 | lws_callback_on_writable(wsi); |
1061 | 0 | } |
1062 | | |
1063 | 0 | lwsl_info("%s: lws_write %d %d\n", __func__, |
1064 | 0 | lws_ptr_diff(p, buf + LWS_PRE), f); |
1065 | |
|
1066 | 0 | if (lws_write(wsi, buf + LWS_PRE, lws_ptr_diff_size_t(p, buf + LWS_PRE), |
1067 | 0 | (!conceal_eom && (f & LWSSS_FLAG_EOM)) ? |
1068 | 0 | LWS_WRITE_HTTP_FINAL : LWS_WRITE_HTTP) != |
1069 | 0 | (int)lws_ptr_diff(p, buf + LWS_PRE)) { |
1070 | 0 | lwsl_err("%s: write failed\n", __func__); |
1071 | 0 | return -1; |
1072 | 0 | } |
1073 | | |
1074 | 0 | #if defined(LWS_WITH_SERVER) |
1075 | 0 | if ((h->info.flags & LWSSSINFLAGS_ACCEPTED) /* server */ && |
1076 | 0 | (f & LWSSS_FLAG_EOM) && |
1077 | 0 | lws_http_transaction_completed(wsi)) |
1078 | 0 | return -1; |
1079 | | #else |
1080 | | lws_set_timeout(wsi, 0, 0); |
1081 | | #endif |
1082 | 0 | break; |
1083 | | |
1084 | 0 | #if defined(LWS_WITH_SERVER) |
1085 | 0 | case LWS_CALLBACK_HTTP: |
1086 | |
|
1087 | 0 | if (!h) |
1088 | 0 | return -1; |
1089 | | |
1090 | 0 | if (h->wsi && h->wsi->mount_hit) |
1091 | 0 | break; |
1092 | | |
1093 | 0 | lwsl_info("%s: LWS_CALLBACK_HTTP\n", __func__); |
1094 | 0 | { |
1095 | |
|
1096 | 0 | h->txn_resp_set = 0; |
1097 | 0 | h->txn_resp_pending = 1; |
1098 | 0 | h->writeable_len = 0; |
1099 | |
|
1100 | 0 | #if defined(LWS_ROLE_H2) |
1101 | 0 | m = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_METHOD); |
1102 | 0 | if (m) { |
1103 | 0 | if (lws_ss_alloc_set_metadata(h, "method", |
1104 | 0 | lws_hdr_simple_ptr(wsi, |
1105 | 0 | WSI_TOKEN_HTTP_COLON_METHOD), (unsigned int)m)) |
1106 | 0 | return -1; |
1107 | 0 | m = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_PATH); |
1108 | 0 | if (m && lws_ss_alloc_set_metadata(h, "path", |
1109 | 0 | lws_hdr_simple_ptr(wsi, |
1110 | 0 | WSI_TOKEN_HTTP_COLON_PATH), (unsigned int)m)) |
1111 | 0 | return -1; |
1112 | 0 | } else |
1113 | 0 | #endif |
1114 | 0 | { |
1115 | 0 | m = lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI); |
1116 | 0 | if (m) { |
1117 | 0 | if (lws_ss_alloc_set_metadata(h, "path", |
1118 | 0 | lws_hdr_simple_ptr(wsi, |
1119 | 0 | WSI_TOKEN_GET_URI), (unsigned int)m)) |
1120 | 0 | return -1; |
1121 | 0 | if (lws_ss_alloc_set_metadata(h, "method", "GET", 3)) |
1122 | 0 | return -1; |
1123 | 0 | } else { |
1124 | 0 | m = lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI); |
1125 | 0 | if (m) { |
1126 | 0 | if (lws_ss_alloc_set_metadata(h, "path", |
1127 | 0 | lws_hdr_simple_ptr(wsi, |
1128 | 0 | WSI_TOKEN_POST_URI), (unsigned int)m)) |
1129 | 0 | return -1; |
1130 | 0 | if (lws_ss_alloc_set_metadata(h, "method", "POST", 4)) |
1131 | 0 | return -1; |
1132 | 0 | } else { |
1133 | 0 | #if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_HTTP_HEADERS_ALL) |
1134 | 0 | m = lws_hdr_total_length(wsi, WSI_TOKEN_PATCH_URI); |
1135 | 0 | if (m) { |
1136 | 0 | if (lws_ss_alloc_set_metadata(h, "path", |
1137 | 0 | lws_hdr_simple_ptr(wsi, |
1138 | 0 | WSI_TOKEN_PATCH_URI), (unsigned int)m)) |
1139 | 0 | return -1; |
1140 | 0 | if (lws_ss_alloc_set_metadata(h, "method", "PATCH", 5)) |
1141 | 0 | return -1; |
1142 | 0 | } |
1143 | 0 | #endif |
1144 | 0 | } |
1145 | 0 | } |
1146 | 0 | } |
1147 | 0 | } |
1148 | | |
1149 | 0 | if (!h->ss_dangling_connected) { |
1150 | | #if defined(LWS_WITH_SYS_METRICS) |
1151 | | /* |
1152 | | * If any hanging caliper measurement, dump it, and free any tags |
1153 | | */ |
1154 | | lws_metrics_caliper_report_hist(h->cal_txn, (struct lws *)NULL); |
1155 | | #endif |
1156 | 0 | wsi->client_suppress_CONNECTION_ERROR = 1; |
1157 | 0 | if (h->prev_ss_state != LWSSSCS_CONNECTED) { |
1158 | 0 | r = lws_ss_event_helper(h, LWSSSCS_CONNECTED); |
1159 | 0 | if (r) |
1160 | 0 | return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, wsi, &h); |
1161 | 0 | } |
1162 | 0 | } |
1163 | | |
1164 | 0 | r = lws_ss_event_helper(h, LWSSSCS_SERVER_TXN); |
1165 | 0 | if (r) |
1166 | 0 | return _lws_ss_handle_state_ret_CAN_DESTROY_HANDLE(r, |
1167 | 0 | wsi, &h); |
1168 | | |
1169 | 0 | return 0; |
1170 | 0 | #endif |
1171 | | |
1172 | 0 | default: |
1173 | 0 | break; |
1174 | 0 | } |
1175 | | |
1176 | 0 | return lws_callback_http_dummy(wsi, reason, user, in, len); |
1177 | 0 | } |
1178 | | |
1179 | | const struct lws_protocols protocol_secstream_h1 = { |
1180 | | "lws-secstream-h1", |
1181 | | secstream_h1, |
1182 | | 0, 0, 0, NULL, 0 |
1183 | | }; |
1184 | | |
1185 | | /* |
1186 | | * Munge connect info according to protocol-specific considerations... this |
1187 | | * usually means interpreting aux in a protocol-specific way and using the |
1188 | | * pieces at connection setup time, eg, http url pieces. |
1189 | | * |
1190 | | * len bytes of buf can be used for things with scope until after the actual |
1191 | | * connect. |
1192 | | */ |
1193 | | |
1194 | | static int |
1195 | | secstream_connect_munge_h1(lws_ss_handle_t *h, char *buf, size_t len, |
1196 | | struct lws_client_connect_info *i, |
1197 | | union lws_ss_contemp *ct) |
1198 | 0 | { |
1199 | 0 | const char *pbasis = h->policy->u.http.url; |
1200 | 0 | size_t used_in, used_out; |
1201 | 0 | lws_strexp_t exp; |
1202 | | |
1203 | | /* i.path on entry is used to override the policy urlpath if not "" */ |
1204 | |
|
1205 | 0 | if (i->path[0]) |
1206 | 0 | pbasis = i->path; |
1207 | |
|
1208 | 0 | if (!pbasis) |
1209 | 0 | return 0; |
1210 | | |
1211 | | /* uncomment to force h1 */ |
1212 | | // i->alpn = "http/1.1"; |
1213 | | |
1214 | 0 | #if defined(LWS_WITH_SS_RIDESHARE) |
1215 | 0 | if (h->policy->flags & LWSSSPOLF_HTTP_MULTIPART) |
1216 | 0 | i->ssl_connection |= LCCSCF_HTTP_MULTIPART_MIME; |
1217 | |
|
1218 | 0 | if (h->policy->flags & LWSSSPOLF_HTTP_X_WWW_FORM_URLENCODED) |
1219 | 0 | i->ssl_connection |= LCCSCF_HTTP_X_WWW_FORM_URLENCODED; |
1220 | 0 | #endif |
1221 | |
|
1222 | 0 | if (h->policy->flags & LWSSSPOLF_HTTP_CACHE_COOKIES) |
1223 | 0 | i->ssl_connection |= LCCSCF_CACHE_COOKIES; |
1224 | | |
1225 | | /* protocol aux is the path part */ |
1226 | |
|
1227 | 0 | i->path = buf; |
1228 | | |
1229 | | /* skip the unnessary '/' */ |
1230 | 0 | if (*pbasis == '/') |
1231 | 0 | pbasis = pbasis + 1; |
1232 | |
|
1233 | 0 | buf[0] = '/'; |
1234 | |
|
1235 | 0 | lws_strexp_init(&exp, (void *)h, lws_ss_exp_cb_metadata, buf + 1, len - 1); |
1236 | |
|
1237 | 0 | if (lws_strexp_expand(&exp, pbasis, strlen(pbasis), |
1238 | 0 | &used_in, &used_out) != LSTRX_DONE) |
1239 | 0 | return 1; |
1240 | | |
1241 | 0 | if (used_out + 1 < len - 1) |
1242 | 0 | buf[used_out + 1] = '\0'; |
1243 | |
|
1244 | 0 | return 0; |
1245 | 0 | } |
1246 | | |
1247 | | |
1248 | | const struct ss_pcols ss_pcol_h1 = { |
1249 | | "h1", |
1250 | | "http/1.1", |
1251 | | &protocol_secstream_h1, |
1252 | | secstream_connect_munge_h1, |
1253 | | NULL, NULL |
1254 | | }; |