1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
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.
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ***************************************************************************/
23 #include "curl_setup.h"
28 #include "curl_printf.h"
36 /* #define DEBUG_HTTP3 1 */
40 #define H3BUGF(x) do { } while(0)
43 #define MSH3_REQ_INIT_BUF_LEN 8192
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,
58 const MSH3_HEADER *Header);
59 static void MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
60 void *IfContext, uint32_t Length,
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);
66 static const struct Curl_handler msh3_curl_handler_http3 = {
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 */
89 static const MSH3_REQUEST_IF msh3_request_if = {
96 void Curl_quic_ver(char *p, size_t len)
100 (void)msnprintf(p, len, "msh3/%d.%d.%d.%d", v[0], v[1], v[2], v[3]);
103 CURLcode Curl_quic_connect(struct Curl_easy *data,
104 struct connectdata *conn,
105 curl_socket_t sockfd,
107 const struct sockaddr *addr,
110 struct quicsocket *qs = &conn->hequic[sockindex];
111 bool unsecure = !conn->ssl_config.verifypeer;
112 memset(qs, 0, sizeof(*qs));
115 (void)addr; /* TODO - Pass address along */
118 H3BUGF(infof(data, "creating new api/connection"));
120 qs->api = MsH3ApiOpen();
122 failf(data, "can't create msh3 api");
123 return CURLE_FAILED_INIT;
126 qs->conn = MsH3ConnectionOpen(qs->api,
128 (uint16_t)conn->remote_port,
131 failf(data, "can't create msh3 connection");
133 MsH3ApiClose(qs->api);
135 return CURLE_FAILED_INIT;
141 CURLcode Curl_quic_is_connected(struct Curl_easy *data,
142 struct connectdata *conn,
146 struct quicsocket *qs = &conn->hequic[sockindex];
147 MSH3_CONNECTION_STATE state;
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;
155 if(state == MSH3_CONN_CONNECTED) {
156 H3BUGF(infof(data, "connection connected"));
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)? */
171 static int msh3_getsock(struct Curl_easy *data,
172 struct connectdata *conn, curl_socket_t *socks)
174 struct HTTP *stream = data->req.p.http;
175 int bitmap = GETSOCK_BLANK;
177 socks[0] = conn->sock[FIRSTSOCKET];
179 if(stream->recv_error) {
180 bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
183 else if(stream->recv_header_len || stream->recv_data_len) {
184 bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
188 H3BUGF(infof(data, "msh3_getsock %u", (uint32_t)data->state.drain));
193 static CURLcode msh3_do_it(struct Curl_easy *data, bool *done)
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;
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);
212 static unsigned int msh3_conncheck(struct Curl_easy *data,
213 struct connectdata *conn,
214 unsigned int checks_to_perform)
218 (void)checks_to_perform;
219 H3BUGF(infof(data, "msh3_conncheck"));
220 return CONNRESULT_NONE;
223 static void msh3_cleanup(struct quicsocket *qs, struct HTTP *stream)
225 if(stream && stream->recv_buf) {
226 free(stream->recv_buf);
227 stream->recv_buf = ZERO_NULL;
228 msh3_lock_uninitialize(&stream->recv_lock);
231 MsH3ConnectionClose(qs->conn);
232 qs->conn = ZERO_NULL;
235 MsH3ApiClose(qs->api);
240 static CURLcode msh3_disconnect(struct Curl_easy *data,
241 struct connectdata *conn, bool dead_connection)
243 (void)dead_connection;
244 H3BUGF(infof(data, "disconnecting (msh3)"));
245 msh3_cleanup(conn->quic, data->req.p.http);
249 void Curl_quic_disconnect(struct Curl_easy *data, struct connectdata *conn,
252 if(conn->transport == TRNSPRT_QUIC) {
253 H3BUGF(infof(data, "disconnecting (curl)"));
254 msh3_cleanup(&conn->hequic[tempindex], data->req.p.http);
258 /* Requires stream->recv_lock to be held */
259 static bool msh3request_ensure_room(struct HTTP *stream, size_t len)
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;
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);
273 memcpy(new_recv_buf, stream->recv_buf, cur_recv_len);
275 stream->recv_buf_alloc = new_recv_buf_alloc_len;
276 free(stream->recv_buf);
277 stream->recv_buf = new_recv_buf;
282 static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
284 const MSH3_HEADER *Header)
286 struct HTTP *stream = IfContext;
289 H3BUGF(printf("* msh3_header_received\n"));
291 if(stream->recv_header_complete) {
292 H3BUGF(printf("* ignoring header after data\n"));
296 msh3_lock_acquire(&stream->recv_lock);
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 */
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);
310 total_len = Header->NameLength + 4 + Header->ValueLength;
311 if(!msh3request_ensure_room(stream, total_len)) {
312 /* TODO - handle error */
315 msnprintf((char *)stream->recv_buf + stream->recv_header_len,
316 stream->recv_buf_alloc - stream->recv_header_len,
318 (int)Header->NameLength, Header->Name,
319 (int)Header->ValueLength, Header->Value);
322 stream->recv_header_len += total_len - 1; /* don't include null-terminator */
325 msh3_lock_release(&stream->recv_lock);
328 static void MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
329 void *IfContext, uint32_t Length,
332 struct HTTP *stream = IfContext;
333 size_t cur_recv_len = stream->recv_header_len + stream->recv_data_len;
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 */
344 stream->recv_buf[stream->recv_header_len++] = '\r';
345 stream->recv_buf[stream->recv_header_len++] = '\n';
346 stream->recv_header_complete = true;
349 if(!msh3request_ensure_room(stream, Length)) {
350 /* TODO - handle error */
353 memcpy(stream->recv_buf + cur_recv_len, Data, Length);
354 stream->recv_data_len += (size_t)Length;
356 msh3_lock_release(&stream->recv_lock);
359 static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
360 bool Aborted, uint64_t AbortError)
362 struct HTTP *stream = IfContext;
365 H3BUGF(printf("* msh3_complete, aborted=%s\n", Aborted ? "true" : "false"));
366 msh3_lock_acquire(&stream->recv_lock);
368 stream->recv_error = CURLE_HTTP3; /* TODO - how do we pass AbortError? */
370 stream->recv_header_complete = true;
371 stream->recv_data_complete = true;
372 msh3_lock_release(&stream->recv_lock);
375 static void MSH3_CALL msh3_shutdown(MSH3_REQUEST *Request, void *IfContext)
377 struct HTTP *stream = IfContext;
382 static_assert(sizeof(MSH3_HEADER) == sizeof(struct h2h3pseudo),
383 "Sizes must match for cast below to work");
385 static ssize_t msh3_stream_send(struct Curl_easy *data,
391 struct connectdata *conn = data->conn;
392 struct HTTP *stream = data->req.p.http;
393 struct quicsocket *qs = conn->quic;
394 struct h2h3req *hreq;
397 H3BUGF(infof(data, "msh3_stream_send %zu", len));
400 *curlcode = Curl_pseudo_headers(data, mem, len, &hreq);
402 failf(data, "Curl_pseudo_headers failed");
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);
410 failf(data, "request open failed");
411 *curlcode = CURLE_SEND_ERROR;
414 *curlcode = CURLE_OK;
417 H3BUGF(infof(data, "send %zd body bytes on request %p", len,
418 (void *)stream->req));
419 *curlcode = CURLE_SEND_ERROR;
423 static ssize_t msh3_stream_recv(struct Curl_easy *data,
429 struct HTTP *stream = data->req.p.http;
432 H3BUGF(infof(data, "msh3_stream_recv %zu", buffersize));
434 if(stream->recv_error) {
435 failf(data, "request aborted");
436 *curlcode = stream->recv_error;
440 msh3_lock_acquire(&stream->recv_lock);
442 if(stream->recv_header_len) {
443 outsize = buffersize;
444 if(stream->recv_header_len < outsize) {
445 outsize = stream->recv_header_len;
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);
452 stream->recv_header_len -= outsize;
453 H3BUGF(infof(data, "returned %zu bytes of headers", outsize));
455 else if(stream->recv_data_len) {
456 outsize = buffersize;
457 if(stream->recv_data_len < outsize) {
458 outsize = stream->recv_data_len;
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);
465 stream->recv_data_len -= outsize;
466 H3BUGF(infof(data, "returned %zu bytes of data", outsize));
468 else if(stream->recv_data_complete) {
469 H3BUGF(infof(data, "receive complete"));
472 msh3_lock_release(&stream->recv_lock);
474 return (ssize_t)outsize;
477 CURLcode Curl_quic_done_sending(struct Curl_easy *data)
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;
489 void Curl_quic_done(struct Curl_easy *data, bool premature)
493 H3BUGF(infof(data, "Curl_quic_done"));
496 bool Curl_quic_data_pending(const struct Curl_easy *data)
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;
503 #endif /* USE_MSH3 */