1 | /***************************************************************************
|
---|
2 | * _ _ ____ _
|
---|
3 | * Project ___| | | | _ \| |
|
---|
4 | * / __| | | | |_) | |
|
---|
5 | * | (__| |_| | _ <| |___
|
---|
6 | * \___|\___/|_| \_\_____|
|
---|
7 | *
|
---|
8 | * Copyright (C) 1998 - 2022, Daniel Stenberg, <[email protected]>, et al.
|
---|
9 | *
|
---|
10 | * This software is licensed as described in the file COPYING, which
|
---|
11 | * you should have received as part of this distribution. The terms
|
---|
12 | * are also available at https://curl.se/docs/copyright.html.
|
---|
13 | *
|
---|
14 | * You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
---|
15 | * copies of the Software, and permit persons to whom the Software is
|
---|
16 | * furnished to do so, under the terms of the COPYING file.
|
---|
17 | *
|
---|
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
---|
19 | * KIND, either express or implied.
|
---|
20 | *
|
---|
21 | ***************************************************************************/
|
---|
22 |
|
---|
23 | #include "curl_setup.h"
|
---|
24 |
|
---|
25 | #ifdef USE_MSH3
|
---|
26 |
|
---|
27 | #include "urldata.h"
|
---|
28 | #include "curl_printf.h"
|
---|
29 | #include "timeval.h"
|
---|
30 | #include "multiif.h"
|
---|
31 | #include "sendf.h"
|
---|
32 | #include "connect.h"
|
---|
33 | #include "h2h3.h"
|
---|
34 | #include "msh3.h"
|
---|
35 |
|
---|
36 | /* #define DEBUG_HTTP3 1 */
|
---|
37 | #ifdef DEBUG_HTTP3
|
---|
38 | #define H3BUGF(x) x
|
---|
39 | #else
|
---|
40 | #define H3BUGF(x) do { } while(0)
|
---|
41 | #endif
|
---|
42 |
|
---|
43 | #define MSH3_REQ_INIT_BUF_LEN 8192
|
---|
44 |
|
---|
45 | static CURLcode msh3_do_it(struct Curl_easy *data, bool *done);
|
---|
46 | static int msh3_getsock(struct Curl_easy *data,
|
---|
47 | struct connectdata *conn, curl_socket_t *socks);
|
---|
48 | static CURLcode msh3_disconnect(struct Curl_easy *data,
|
---|
49 | struct connectdata *conn,
|
---|
50 | bool dead_connection);
|
---|
51 | static unsigned int msh3_conncheck(struct Curl_easy *data,
|
---|
52 | struct connectdata *conn,
|
---|
53 | unsigned int checks_to_perform);
|
---|
54 | static Curl_recv msh3_stream_recv;
|
---|
55 | static Curl_send msh3_stream_send;
|
---|
56 | static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
|
---|
57 | void *IfContext,
|
---|
58 | const MSH3_HEADER *Header);
|
---|
59 | static void MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
|
---|
60 | void *IfContext, uint32_t Length,
|
---|
61 | const uint8_t *Data);
|
---|
62 | static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
|
---|
63 | bool Aborted, uint64_t AbortError);
|
---|
64 | static void MSH3_CALL msh3_shutdown(MSH3_REQUEST *Request, void *IfContext);
|
---|
65 |
|
---|
66 | static const struct Curl_handler msh3_curl_handler_http3 = {
|
---|
67 | "HTTPS", /* scheme */
|
---|
68 | ZERO_NULL, /* setup_connection */
|
---|
69 | msh3_do_it, /* do_it */
|
---|
70 | Curl_http_done, /* done */
|
---|
71 | ZERO_NULL, /* do_more */
|
---|
72 | ZERO_NULL, /* connect_it */
|
---|
73 | ZERO_NULL, /* connecting */
|
---|
74 | ZERO_NULL, /* doing */
|
---|
75 | msh3_getsock, /* proto_getsock */
|
---|
76 | msh3_getsock, /* doing_getsock */
|
---|
77 | ZERO_NULL, /* domore_getsock */
|
---|
78 | msh3_getsock, /* perform_getsock */
|
---|
79 | msh3_disconnect, /* disconnect */
|
---|
80 | ZERO_NULL, /* readwrite */
|
---|
81 | msh3_conncheck, /* connection_check */
|
---|
82 | ZERO_NULL, /* attach connection */
|
---|
83 | PORT_HTTP, /* defport */
|
---|
84 | CURLPROTO_HTTPS, /* protocol */
|
---|
85 | CURLPROTO_HTTP, /* family */
|
---|
86 | PROTOPT_SSL | PROTOPT_STREAM /* flags */
|
---|
87 | };
|
---|
88 |
|
---|
89 | static const MSH3_REQUEST_IF msh3_request_if = {
|
---|
90 | msh3_header_received,
|
---|
91 | msh3_data_received,
|
---|
92 | msh3_complete,
|
---|
93 | msh3_shutdown
|
---|
94 | };
|
---|
95 |
|
---|
96 | void Curl_quic_ver(char *p, size_t len)
|
---|
97 | {
|
---|
98 | uint32_t v[4];
|
---|
99 | MsH3Version(v);
|
---|
100 | (void)msnprintf(p, len, "msh3/%d.%d.%d.%d", v[0], v[1], v[2], v[3]);
|
---|
101 | }
|
---|
102 |
|
---|
103 | CURLcode Curl_quic_connect(struct Curl_easy *data,
|
---|
104 | struct connectdata *conn,
|
---|
105 | curl_socket_t sockfd,
|
---|
106 | int sockindex,
|
---|
107 | const struct sockaddr *addr,
|
---|
108 | socklen_t addrlen)
|
---|
109 | {
|
---|
110 | struct quicsocket *qs = &conn->hequic[sockindex];
|
---|
111 | bool unsecure = !conn->ssl_config.verifypeer;
|
---|
112 | memset(qs, 0, sizeof(*qs));
|
---|
113 |
|
---|
114 | (void)sockfd;
|
---|
115 | (void)addr; /* TODO - Pass address along */
|
---|
116 | (void)addrlen;
|
---|
117 |
|
---|
118 | H3BUGF(infof(data, "creating new api/connection"));
|
---|
119 |
|
---|
120 | qs->api = MsH3ApiOpen();
|
---|
121 | if(!qs->api) {
|
---|
122 | failf(data, "can't create msh3 api");
|
---|
123 | return CURLE_FAILED_INIT;
|
---|
124 | }
|
---|
125 |
|
---|
126 | qs->conn = MsH3ConnectionOpen(qs->api,
|
---|
127 | conn->host.name,
|
---|
128 | (uint16_t)conn->remote_port,
|
---|
129 | unsecure);
|
---|
130 | if(!qs->conn) {
|
---|
131 | failf(data, "can't create msh3 connection");
|
---|
132 | if(qs->api) {
|
---|
133 | MsH3ApiClose(qs->api);
|
---|
134 | }
|
---|
135 | return CURLE_FAILED_INIT;
|
---|
136 | }
|
---|
137 |
|
---|
138 | return CURLE_OK;
|
---|
139 | }
|
---|
140 |
|
---|
141 | CURLcode Curl_quic_is_connected(struct Curl_easy *data,
|
---|
142 | struct connectdata *conn,
|
---|
143 | int sockindex,
|
---|
144 | bool *connected)
|
---|
145 | {
|
---|
146 | struct quicsocket *qs = &conn->hequic[sockindex];
|
---|
147 | MSH3_CONNECTION_STATE state;
|
---|
148 |
|
---|
149 | state = MsH3ConnectionGetState(qs->conn, false);
|
---|
150 | if(state == MSH3_CONN_HANDSHAKE_FAILED || state == MSH3_CONN_DISCONNECTED) {
|
---|
151 | failf(data, "failed to connect, state=%u", (uint32_t)state);
|
---|
152 | return CURLE_COULDNT_CONNECT;
|
---|
153 | }
|
---|
154 |
|
---|
155 | if(state == MSH3_CONN_CONNECTED) {
|
---|
156 | H3BUGF(infof(data, "connection connected"));
|
---|
157 | *connected = true;
|
---|
158 | conn->quic = qs;
|
---|
159 | conn->recv[sockindex] = msh3_stream_recv;
|
---|
160 | conn->send[sockindex] = msh3_stream_send;
|
---|
161 | conn->handler = &msh3_curl_handler_http3;
|
---|
162 | conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
|
---|
163 | conn->httpversion = 30;
|
---|
164 | conn->bundle->multiuse = BUNDLE_MULTIPLEX;
|
---|
165 | /* TODO - Clean up other happy-eyeballs connection(s)? */
|
---|
166 | }
|
---|
167 |
|
---|
168 | return CURLE_OK;
|
---|
169 | }
|
---|
170 |
|
---|
171 | static int msh3_getsock(struct Curl_easy *data,
|
---|
172 | struct connectdata *conn, curl_socket_t *socks)
|
---|
173 | {
|
---|
174 | struct HTTP *stream = data->req.p.http;
|
---|
175 | int bitmap = GETSOCK_BLANK;
|
---|
176 |
|
---|
177 | socks[0] = conn->sock[FIRSTSOCKET];
|
---|
178 |
|
---|
179 | if(stream->recv_error) {
|
---|
180 | bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
|
---|
181 | data->state.drain++;
|
---|
182 | }
|
---|
183 | else if(stream->recv_header_len || stream->recv_data_len) {
|
---|
184 | bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
|
---|
185 | data->state.drain++;
|
---|
186 | }
|
---|
187 |
|
---|
188 | H3BUGF(infof(data, "msh3_getsock %u", (uint32_t)data->state.drain));
|
---|
189 |
|
---|
190 | return bitmap;
|
---|
191 | }
|
---|
192 |
|
---|
193 | static CURLcode msh3_do_it(struct Curl_easy *data, bool *done)
|
---|
194 | {
|
---|
195 | struct HTTP *stream = data->req.p.http;
|
---|
196 | H3BUGF(infof(data, "msh3_do_it"));
|
---|
197 | stream->recv_buf = malloc(MSH3_REQ_INIT_BUF_LEN);
|
---|
198 | if(!stream->recv_buf) {
|
---|
199 | return CURLE_OUT_OF_MEMORY;
|
---|
200 | }
|
---|
201 | stream->req = ZERO_NULL;
|
---|
202 | msh3_lock_initialize(&stream->recv_lock);
|
---|
203 | stream->recv_buf_alloc = MSH3_REQ_INIT_BUF_LEN;
|
---|
204 | stream->recv_header_len = 0;
|
---|
205 | stream->recv_header_complete = false;
|
---|
206 | stream->recv_data_len = 0;
|
---|
207 | stream->recv_data_complete = false;
|
---|
208 | stream->recv_error = CURLE_OK;
|
---|
209 | return Curl_http(data, done);
|
---|
210 | }
|
---|
211 |
|
---|
212 | static unsigned int msh3_conncheck(struct Curl_easy *data,
|
---|
213 | struct connectdata *conn,
|
---|
214 | unsigned int checks_to_perform)
|
---|
215 | {
|
---|
216 | (void)data;
|
---|
217 | (void)conn;
|
---|
218 | (void)checks_to_perform;
|
---|
219 | H3BUGF(infof(data, "msh3_conncheck"));
|
---|
220 | return CONNRESULT_NONE;
|
---|
221 | }
|
---|
222 |
|
---|
223 | static void msh3_cleanup(struct quicsocket *qs, struct HTTP *stream)
|
---|
224 | {
|
---|
225 | if(stream && stream->recv_buf) {
|
---|
226 | free(stream->recv_buf);
|
---|
227 | stream->recv_buf = ZERO_NULL;
|
---|
228 | msh3_lock_uninitialize(&stream->recv_lock);
|
---|
229 | }
|
---|
230 | if(qs->conn) {
|
---|
231 | MsH3ConnectionClose(qs->conn);
|
---|
232 | qs->conn = ZERO_NULL;
|
---|
233 | }
|
---|
234 | if(qs->api) {
|
---|
235 | MsH3ApiClose(qs->api);
|
---|
236 | qs->api = ZERO_NULL;
|
---|
237 | }
|
---|
238 | }
|
---|
239 |
|
---|
240 | static CURLcode msh3_disconnect(struct Curl_easy *data,
|
---|
241 | struct connectdata *conn, bool dead_connection)
|
---|
242 | {
|
---|
243 | (void)dead_connection;
|
---|
244 | H3BUGF(infof(data, "disconnecting (msh3)"));
|
---|
245 | msh3_cleanup(conn->quic, data->req.p.http);
|
---|
246 | return CURLE_OK;
|
---|
247 | }
|
---|
248 |
|
---|
249 | void Curl_quic_disconnect(struct Curl_easy *data, struct connectdata *conn,
|
---|
250 | int tempindex)
|
---|
251 | {
|
---|
252 | if(conn->transport == TRNSPRT_QUIC) {
|
---|
253 | H3BUGF(infof(data, "disconnecting (curl)"));
|
---|
254 | msh3_cleanup(&conn->hequic[tempindex], data->req.p.http);
|
---|
255 | }
|
---|
256 | }
|
---|
257 |
|
---|
258 | /* Requires stream->recv_lock to be held */
|
---|
259 | static bool msh3request_ensure_room(struct HTTP *stream, size_t len)
|
---|
260 | {
|
---|
261 | uint8_t *new_recv_buf;
|
---|
262 | const size_t cur_recv_len = stream->recv_header_len + stream->recv_data_len;
|
---|
263 | if(cur_recv_len + len > stream->recv_buf_alloc) {
|
---|
264 | size_t new_recv_buf_alloc_len = stream->recv_buf_alloc;
|
---|
265 | do {
|
---|
266 | new_recv_buf_alloc_len <<= 1; /* TODO - handle overflow */
|
---|
267 | } while(cur_recv_len + len > new_recv_buf_alloc_len);
|
---|
268 | new_recv_buf = malloc(new_recv_buf_alloc_len);
|
---|
269 | if(!new_recv_buf) {
|
---|
270 | return false;
|
---|
271 | }
|
---|
272 | if(cur_recv_len) {
|
---|
273 | memcpy(new_recv_buf, stream->recv_buf, cur_recv_len);
|
---|
274 | }
|
---|
275 | stream->recv_buf_alloc = new_recv_buf_alloc_len;
|
---|
276 | free(stream->recv_buf);
|
---|
277 | stream->recv_buf = new_recv_buf;
|
---|
278 | }
|
---|
279 | return true;
|
---|
280 | }
|
---|
281 |
|
---|
282 | static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
|
---|
283 | void *IfContext,
|
---|
284 | const MSH3_HEADER *Header)
|
---|
285 | {
|
---|
286 | struct HTTP *stream = IfContext;
|
---|
287 | size_t total_len;
|
---|
288 | (void)Request;
|
---|
289 | H3BUGF(printf("* msh3_header_received\n"));
|
---|
290 |
|
---|
291 | if(stream->recv_header_complete) {
|
---|
292 | H3BUGF(printf("* ignoring header after data\n"));
|
---|
293 | return;
|
---|
294 | }
|
---|
295 |
|
---|
296 | msh3_lock_acquire(&stream->recv_lock);
|
---|
297 |
|
---|
298 | if((Header->NameLength == 7) &&
|
---|
299 | !strncmp(H2H3_PSEUDO_STATUS, (char *)Header->Name, 7)) {
|
---|
300 | total_len = 9 + Header->ValueLength;
|
---|
301 | if(!msh3request_ensure_room(stream, total_len)) {
|
---|
302 | /* TODO - handle error */
|
---|
303 | goto release_lock;
|
---|
304 | }
|
---|
305 | msnprintf((char *)stream->recv_buf + stream->recv_header_len,
|
---|
306 | stream->recv_buf_alloc - stream->recv_header_len,
|
---|
307 | "HTTP/3 %.*s\n", (int)Header->ValueLength, Header->Value);
|
---|
308 | }
|
---|
309 | else {
|
---|
310 | total_len = Header->NameLength + 4 + Header->ValueLength;
|
---|
311 | if(!msh3request_ensure_room(stream, total_len)) {
|
---|
312 | /* TODO - handle error */
|
---|
313 | goto release_lock;
|
---|
314 | }
|
---|
315 | msnprintf((char *)stream->recv_buf + stream->recv_header_len,
|
---|
316 | stream->recv_buf_alloc - stream->recv_header_len,
|
---|
317 | "%.*s: %.*s\n",
|
---|
318 | (int)Header->NameLength, Header->Name,
|
---|
319 | (int)Header->ValueLength, Header->Value);
|
---|
320 | }
|
---|
321 |
|
---|
322 | stream->recv_header_len += total_len - 1; /* don't include null-terminator */
|
---|
323 |
|
---|
324 | release_lock:
|
---|
325 | msh3_lock_release(&stream->recv_lock);
|
---|
326 | }
|
---|
327 |
|
---|
328 | static void MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
|
---|
329 | void *IfContext, uint32_t Length,
|
---|
330 | const uint8_t *Data)
|
---|
331 | {
|
---|
332 | struct HTTP *stream = IfContext;
|
---|
333 | size_t cur_recv_len = stream->recv_header_len + stream->recv_data_len;
|
---|
334 | (void)Request;
|
---|
335 | H3BUGF(printf("* msh3_data_received %u. %zu buffered, %zu allocated\n",
|
---|
336 | Length, cur_recv_len, stream->recv_buf_alloc));
|
---|
337 | msh3_lock_acquire(&stream->recv_lock);
|
---|
338 | if(!stream->recv_header_complete) {
|
---|
339 | H3BUGF(printf("* Headers complete!\n"));
|
---|
340 | if(!msh3request_ensure_room(stream, 2)) {
|
---|
341 | /* TODO - handle error */
|
---|
342 | goto release_lock;
|
---|
343 | }
|
---|
344 | stream->recv_buf[stream->recv_header_len++] = '\r';
|
---|
345 | stream->recv_buf[stream->recv_header_len++] = '\n';
|
---|
346 | stream->recv_header_complete = true;
|
---|
347 | cur_recv_len += 2;
|
---|
348 | }
|
---|
349 | if(!msh3request_ensure_room(stream, Length)) {
|
---|
350 | /* TODO - handle error */
|
---|
351 | goto release_lock;
|
---|
352 | }
|
---|
353 | memcpy(stream->recv_buf + cur_recv_len, Data, Length);
|
---|
354 | stream->recv_data_len += (size_t)Length;
|
---|
355 | release_lock:
|
---|
356 | msh3_lock_release(&stream->recv_lock);
|
---|
357 | }
|
---|
358 |
|
---|
359 | static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
|
---|
360 | bool Aborted, uint64_t AbortError)
|
---|
361 | {
|
---|
362 | struct HTTP *stream = IfContext;
|
---|
363 | (void)Request;
|
---|
364 | (void)AbortError;
|
---|
365 | H3BUGF(printf("* msh3_complete, aborted=%s\n", Aborted ? "true" : "false"));
|
---|
366 | msh3_lock_acquire(&stream->recv_lock);
|
---|
367 | if(Aborted) {
|
---|
368 | stream->recv_error = CURLE_HTTP3; /* TODO - how do we pass AbortError? */
|
---|
369 | }
|
---|
370 | stream->recv_header_complete = true;
|
---|
371 | stream->recv_data_complete = true;
|
---|
372 | msh3_lock_release(&stream->recv_lock);
|
---|
373 | }
|
---|
374 |
|
---|
375 | static void MSH3_CALL msh3_shutdown(MSH3_REQUEST *Request, void *IfContext)
|
---|
376 | {
|
---|
377 | struct HTTP *stream = IfContext;
|
---|
378 | (void)Request;
|
---|
379 | (void)stream;
|
---|
380 | }
|
---|
381 |
|
---|
382 | static_assert(sizeof(MSH3_HEADER) == sizeof(struct h2h3pseudo),
|
---|
383 | "Sizes must match for cast below to work");
|
---|
384 |
|
---|
385 | static ssize_t msh3_stream_send(struct Curl_easy *data,
|
---|
386 | int sockindex,
|
---|
387 | const void *mem,
|
---|
388 | size_t len,
|
---|
389 | CURLcode *curlcode)
|
---|
390 | {
|
---|
391 | struct connectdata *conn = data->conn;
|
---|
392 | struct HTTP *stream = data->req.p.http;
|
---|
393 | struct quicsocket *qs = conn->quic;
|
---|
394 | struct h2h3req *hreq;
|
---|
395 |
|
---|
396 | (void)sockindex;
|
---|
397 | H3BUGF(infof(data, "msh3_stream_send %zu", len));
|
---|
398 |
|
---|
399 | if(!stream->req) {
|
---|
400 | *curlcode = Curl_pseudo_headers(data, mem, len, &hreq);
|
---|
401 | if(*curlcode) {
|
---|
402 | failf(data, "Curl_pseudo_headers failed");
|
---|
403 | return -1;
|
---|
404 | }
|
---|
405 | H3BUGF(infof(data, "starting request with %zu headers", hreq->entries));
|
---|
406 | stream->req = MsH3RequestOpen(qs->conn, &msh3_request_if, stream,
|
---|
407 | (MSH3_HEADER*)hreq->header, hreq->entries);
|
---|
408 | Curl_pseudo_free(hreq);
|
---|
409 | if(!stream->req) {
|
---|
410 | failf(data, "request open failed");
|
---|
411 | *curlcode = CURLE_SEND_ERROR;
|
---|
412 | return -1;
|
---|
413 | }
|
---|
414 | *curlcode = CURLE_OK;
|
---|
415 | return len;
|
---|
416 | }
|
---|
417 | H3BUGF(infof(data, "send %zd body bytes on request %p", len,
|
---|
418 | (void *)stream->req));
|
---|
419 | *curlcode = CURLE_SEND_ERROR;
|
---|
420 | return -1;
|
---|
421 | }
|
---|
422 |
|
---|
423 | static ssize_t msh3_stream_recv(struct Curl_easy *data,
|
---|
424 | int sockindex,
|
---|
425 | char *buf,
|
---|
426 | size_t buffersize,
|
---|
427 | CURLcode *curlcode)
|
---|
428 | {
|
---|
429 | struct HTTP *stream = data->req.p.http;
|
---|
430 | size_t outsize = 0;
|
---|
431 | (void)sockindex;
|
---|
432 | H3BUGF(infof(data, "msh3_stream_recv %zu", buffersize));
|
---|
433 |
|
---|
434 | if(stream->recv_error) {
|
---|
435 | failf(data, "request aborted");
|
---|
436 | *curlcode = stream->recv_error;
|
---|
437 | return -1;
|
---|
438 | }
|
---|
439 |
|
---|
440 | msh3_lock_acquire(&stream->recv_lock);
|
---|
441 |
|
---|
442 | if(stream->recv_header_len) {
|
---|
443 | outsize = buffersize;
|
---|
444 | if(stream->recv_header_len < outsize) {
|
---|
445 | outsize = stream->recv_header_len;
|
---|
446 | }
|
---|
447 | memcpy(buf, stream->recv_buf, outsize);
|
---|
448 | if(outsize < stream->recv_header_len + stream->recv_data_len) {
|
---|
449 | memmove(stream->recv_buf, stream->recv_buf + outsize,
|
---|
450 | stream->recv_header_len + stream->recv_data_len - outsize);
|
---|
451 | }
|
---|
452 | stream->recv_header_len -= outsize;
|
---|
453 | H3BUGF(infof(data, "returned %zu bytes of headers", outsize));
|
---|
454 | }
|
---|
455 | else if(stream->recv_data_len) {
|
---|
456 | outsize = buffersize;
|
---|
457 | if(stream->recv_data_len < outsize) {
|
---|
458 | outsize = stream->recv_data_len;
|
---|
459 | }
|
---|
460 | memcpy(buf, stream->recv_buf, outsize);
|
---|
461 | if(outsize < stream->recv_data_len) {
|
---|
462 | memmove(stream->recv_buf, stream->recv_buf + outsize,
|
---|
463 | stream->recv_data_len - outsize);
|
---|
464 | }
|
---|
465 | stream->recv_data_len -= outsize;
|
---|
466 | H3BUGF(infof(data, "returned %zu bytes of data", outsize));
|
---|
467 | }
|
---|
468 | else if(stream->recv_data_complete) {
|
---|
469 | H3BUGF(infof(data, "receive complete"));
|
---|
470 | }
|
---|
471 |
|
---|
472 | msh3_lock_release(&stream->recv_lock);
|
---|
473 |
|
---|
474 | return (ssize_t)outsize;
|
---|
475 | }
|
---|
476 |
|
---|
477 | CURLcode Curl_quic_done_sending(struct Curl_easy *data)
|
---|
478 | {
|
---|
479 | struct connectdata *conn = data->conn;
|
---|
480 | H3BUGF(infof(data, "Curl_quic_done_sending"));
|
---|
481 | if(conn->handler == &msh3_curl_handler_http3) {
|
---|
482 | struct HTTP *stream = data->req.p.http;
|
---|
483 | stream->upload_done = TRUE;
|
---|
484 | }
|
---|
485 |
|
---|
486 | return CURLE_OK;
|
---|
487 | }
|
---|
488 |
|
---|
489 | void Curl_quic_done(struct Curl_easy *data, bool premature)
|
---|
490 | {
|
---|
491 | (void)data;
|
---|
492 | (void)premature;
|
---|
493 | H3BUGF(infof(data, "Curl_quic_done"));
|
---|
494 | }
|
---|
495 |
|
---|
496 | bool Curl_quic_data_pending(const struct Curl_easy *data)
|
---|
497 | {
|
---|
498 | struct HTTP *stream = data->req.p.http;
|
---|
499 | H3BUGF(infof((struct Curl_easy *)data, "Curl_quic_data_pending"));
|
---|
500 | return stream->recv_header_len || stream->recv_data_len;
|
---|
501 | }
|
---|
502 |
|
---|
503 | #endif /* USE_MSH3 */
|
---|