mirror of
https://github.com/curl/curl.git
synced 2026-08-25 04:23:36 +03:00
msh3: add support for QUIC and HTTP/3 using msh3
Considered experimental, as the other HTTP/3 backends. Closes #8517
This commit is contained in:
parent
7befbe9ce9
commit
37492ebbfa
18 changed files with 850 additions and 7 deletions
|
|
@ -76,11 +76,13 @@ LIB_VTLS_HFILES = \
|
|||
vtls/x509asn1.h
|
||||
|
||||
LIB_VQUIC_CFILES = \
|
||||
vquic/msh3.c \
|
||||
vquic/ngtcp2.c \
|
||||
vquic/quiche.c \
|
||||
vquic/vquic.c
|
||||
|
||||
LIB_VQUIC_HFILES = \
|
||||
vquic/msh3.h \
|
||||
vquic/ngtcp2.h \
|
||||
vquic/quiche.h \
|
||||
vquic/vquic.h
|
||||
|
|
|
|||
|
|
@ -54,6 +54,8 @@
|
|||
#define H3VERSION "h3-29"
|
||||
#elif defined(USE_NGTCP2) && !defined(UNITTESTS)
|
||||
#define H3VERSION "h3-29"
|
||||
#elif defined(USE_MSH3) && !defined(UNITTESTS)
|
||||
#define H3VERSION "h3-29"
|
||||
#else
|
||||
#define H3VERSION "h3"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
* Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
|
|
@ -371,7 +371,8 @@
|
|||
/* Define if you can safely include both <sys/time.h> and <time.h>. */
|
||||
#define TIME_WITH_SYS_TIME
|
||||
|
||||
/* Define to enable HTTP3 support (experimental, requires NGTCP2 or QUICHE) */
|
||||
/* Define to enable HTTP3 support (experimental, requires NGTCP2, QUICHE or
|
||||
MSH3) */
|
||||
#undef ENABLE_QUIC
|
||||
|
||||
/* Version number of package */
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
* Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
|
|
@ -949,6 +949,9 @@ ${SIZEOF_TIME_T_CODE}
|
|||
/* Define to 1 if you have the quiche_conn_set_qlog_fd function. */
|
||||
#cmakedefine HAVE_QUICHE_CONN_SET_QLOG_FD 1
|
||||
|
||||
/* to enable msh3 */
|
||||
#cmakedefine USE_MSH3 1
|
||||
|
||||
/* if Unix domain sockets are enabled */
|
||||
#cmakedefine USE_UNIX_SOCKETS
|
||||
|
||||
|
|
|
|||
|
|
@ -794,7 +794,7 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf,
|
|||
#define USE_HTTP2
|
||||
#endif
|
||||
|
||||
#if defined(USE_NGTCP2) || defined(USE_QUICHE)
|
||||
#if defined(USE_NGTCP2) || defined(USE_QUICHE) || defined(USE_MSH3)
|
||||
#define ENABLE_QUIC
|
||||
#endif
|
||||
|
||||
|
|
|
|||
44
lib/http.h
44
lib/http.h
|
|
@ -38,6 +38,10 @@ typedef enum {
|
|||
#include <nghttp2/nghttp2.h>
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && defined(ENABLE_QUIC)
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
extern const struct Curl_handler Curl_handler_http;
|
||||
|
||||
#ifdef USE_SSL
|
||||
|
|
@ -163,6 +167,29 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data);
|
|||
struct h3out; /* see ngtcp2 */
|
||||
#endif
|
||||
|
||||
#ifdef USE_MSH3
|
||||
#ifdef _WIN32
|
||||
#define msh3_lock CRITICAL_SECTION
|
||||
#define msh3_lock_initialize(lock) InitializeCriticalSection(lock)
|
||||
#define msh3_lock_uninitialize(lock) DeleteCriticalSection(lock)
|
||||
#define msh3_lock_acquire(lock) EnterCriticalSection(lock)
|
||||
#define msh3_lock_release(lock) LeaveCriticalSection(lock)
|
||||
#else /* !_WIN32 */
|
||||
#include <pthread.h>
|
||||
#define msh3_lock pthread_mutex_t
|
||||
#define msh3_lock_initialize(lock) { \
|
||||
pthread_mutexattr_t attr; \
|
||||
pthread_mutexattr_init(&attr); \
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \
|
||||
pthread_mutex_init(lock, &attr); \
|
||||
pthread_mutexattr_destroy(&attr); \
|
||||
}
|
||||
#define msh3_lock_uninitialize(lock) pthread_mutex_destroy(lock)
|
||||
#define msh3_lock_acquire(lock) pthread_mutex_lock(lock)
|
||||
#define msh3_lock_release(lock) pthread_mutex_unlock(lock)
|
||||
#endif /* _WIN32 */
|
||||
#endif /* USE_MSH3 */
|
||||
|
||||
/****************************************************************************
|
||||
* HTTP unique setup
|
||||
***************************************************************************/
|
||||
|
|
@ -228,11 +255,13 @@ struct HTTP {
|
|||
#endif
|
||||
|
||||
#ifdef ENABLE_QUIC
|
||||
#ifndef USE_MSH3
|
||||
/*********** for HTTP/3 we store stream-local data here *************/
|
||||
int64_t stream3_id; /* stream we are interested in */
|
||||
bool firstheader; /* FALSE until headers arrive */
|
||||
bool firstbody; /* FALSE until body arrives */
|
||||
bool h3req; /* FALSE until request is issued */
|
||||
#endif
|
||||
bool upload_done;
|
||||
#endif
|
||||
#ifdef USE_NGHTTP3
|
||||
|
|
@ -240,6 +269,21 @@ struct HTTP {
|
|||
struct h3out *h3out; /* per-stream buffers for upload */
|
||||
struct dynbuf overflow; /* excess data received during a single Curl_read */
|
||||
#endif
|
||||
#ifdef USE_MSH3
|
||||
struct MSH3_REQUEST *req;
|
||||
msh3_lock recv_lock;
|
||||
/* Receive Buffer (Headers and Data) */
|
||||
uint8_t* recv_buf;
|
||||
size_t recv_buf_alloc;
|
||||
/* Receive Headers */
|
||||
size_t recv_header_len;
|
||||
bool recv_header_complete;
|
||||
/* Receive Data */
|
||||
size_t recv_data_len;
|
||||
bool recv_data_complete;
|
||||
/* General Receive Error */
|
||||
CURLcode recv_error;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef USE_NGHTTP2
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
* Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
|
|
@ -31,6 +31,9 @@
|
|||
#ifdef USE_QUICHE
|
||||
#include "vquic/quiche.h"
|
||||
#endif
|
||||
#ifdef USE_MSH3
|
||||
#include "vquic/msh3.h"
|
||||
#endif
|
||||
|
||||
#include "urldata.h"
|
||||
|
||||
|
|
|
|||
498
lib/vquic/msh3.c
Normal file
498
lib/vquic/msh3.c
Normal file
|
|
@ -0,0 +1,498 @@
|
|||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at https://curl.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#include "curl_setup.h"
|
||||
|
||||
#ifdef USE_MSH3
|
||||
|
||||
#include "urldata.h"
|
||||
#include "curl_printf.h"
|
||||
#include "timeval.h"
|
||||
#include "multiif.h"
|
||||
#include "sendf.h"
|
||||
#include "connect.h"
|
||||
#include "h2h3.h"
|
||||
#include "msh3.h"
|
||||
|
||||
/* #define DEBUG_HTTP3 1 */
|
||||
#ifdef DEBUG_HTTP3
|
||||
#define H3BUGF(x) x
|
||||
#else
|
||||
#define H3BUGF(x) do { } while(0)
|
||||
#endif
|
||||
|
||||
#define MSH3_REQ_INIT_BUF_LEN 8192
|
||||
|
||||
static CURLcode msh3_do_it(struct Curl_easy *data, bool *done);
|
||||
static int msh3_getsock(struct Curl_easy *data,
|
||||
struct connectdata *conn, curl_socket_t *socks);
|
||||
static CURLcode msh3_disconnect(struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
bool dead_connection);
|
||||
static unsigned int msh3_conncheck(struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
unsigned int checks_to_perform);
|
||||
static Curl_recv msh3_stream_recv;
|
||||
static Curl_send msh3_stream_send;
|
||||
static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
|
||||
void *IfContext,
|
||||
const MSH3_HEADER *Header);
|
||||
static void MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
|
||||
void *IfContext, uint32_t Length,
|
||||
const uint8_t *Data);
|
||||
static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
|
||||
bool Aborted, uint64_t AbortError);
|
||||
static void MSH3_CALL msh3_shutdown(MSH3_REQUEST *Request, void *IfContext);
|
||||
|
||||
static const struct Curl_handler msh3_curl_handler_http3 = {
|
||||
"HTTPS", /* scheme */
|
||||
ZERO_NULL, /* setup_connection */
|
||||
msh3_do_it, /* do_it */
|
||||
Curl_http_done, /* done */
|
||||
ZERO_NULL, /* do_more */
|
||||
ZERO_NULL, /* connect_it */
|
||||
ZERO_NULL, /* connecting */
|
||||
ZERO_NULL, /* doing */
|
||||
msh3_getsock, /* proto_getsock */
|
||||
msh3_getsock, /* doing_getsock */
|
||||
ZERO_NULL, /* domore_getsock */
|
||||
msh3_getsock, /* perform_getsock */
|
||||
msh3_disconnect, /* disconnect */
|
||||
ZERO_NULL, /* readwrite */
|
||||
msh3_conncheck, /* connection_check */
|
||||
ZERO_NULL, /* attach connection */
|
||||
PORT_HTTP, /* defport */
|
||||
CURLPROTO_HTTPS, /* protocol */
|
||||
CURLPROTO_HTTP, /* family */
|
||||
PROTOPT_SSL | PROTOPT_STREAM /* flags */
|
||||
};
|
||||
|
||||
static const MSH3_REQUEST_IF msh3_request_if = {
|
||||
msh3_header_received,
|
||||
msh3_data_received,
|
||||
msh3_complete,
|
||||
msh3_shutdown
|
||||
};
|
||||
|
||||
void Curl_quic_ver(char *p, size_t len)
|
||||
{
|
||||
(void)msnprintf(p, len, "msh3/%s", "0.0.1");
|
||||
}
|
||||
|
||||
CURLcode Curl_quic_connect(struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
curl_socket_t sockfd,
|
||||
int sockindex,
|
||||
const struct sockaddr *addr,
|
||||
socklen_t addrlen)
|
||||
{
|
||||
struct quicsocket *qs = &conn->hequic[sockindex];
|
||||
bool unsecure = !conn->ssl_config.verifypeer;
|
||||
memset(qs, 0, sizeof(*qs));
|
||||
|
||||
(void)sockfd;
|
||||
(void)addr; /* TODO - Pass address along */
|
||||
(void)addrlen;
|
||||
|
||||
H3BUGF(infof(data, "creating new api/connection"));
|
||||
|
||||
qs->api = MsH3ApiOpen();
|
||||
if(!qs->api) {
|
||||
failf(data, "can't create msh3 api");
|
||||
return CURLE_FAILED_INIT;
|
||||
}
|
||||
|
||||
qs->conn = MsH3ConnectionOpen(qs->api, conn->host.name, unsecure);
|
||||
if(!qs->conn) {
|
||||
failf(data, "can't create msh3 connection");
|
||||
if(qs->api) {
|
||||
MsH3ApiClose(qs->api);
|
||||
}
|
||||
return CURLE_FAILED_INIT;
|
||||
}
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
CURLcode Curl_quic_is_connected(struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
int sockindex,
|
||||
bool *connected)
|
||||
{
|
||||
struct quicsocket *qs = &conn->hequic[sockindex];
|
||||
MSH3_CONNECTION_STATE state;
|
||||
|
||||
state = MsH3ConnectionGetState(qs->conn, false);
|
||||
if(state == MSH3_CONN_HANDSHAKE_FAILED || state == MSH3_CONN_DISCONNECTED) {
|
||||
failf(data, "failed to connect, state=%u", (uint32_t)state);
|
||||
return CURLE_COULDNT_CONNECT;
|
||||
}
|
||||
|
||||
if(state == MSH3_CONN_CONNECTED) {
|
||||
H3BUGF(infof(data, "connection connected"));
|
||||
*connected = true;
|
||||
conn->quic = qs;
|
||||
conn->recv[sockindex] = msh3_stream_recv;
|
||||
conn->send[sockindex] = msh3_stream_send;
|
||||
conn->handler = &msh3_curl_handler_http3;
|
||||
conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
|
||||
conn->httpversion = 30;
|
||||
conn->bundle->multiuse = BUNDLE_MULTIPLEX;
|
||||
/* TODO - Clean up other happy-eyeballs connection(s)? */
|
||||
}
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static int msh3_getsock(struct Curl_easy *data,
|
||||
struct connectdata *conn, curl_socket_t *socks)
|
||||
{
|
||||
struct HTTP *stream = data->req.p.http;
|
||||
int bitmap = GETSOCK_BLANK;
|
||||
|
||||
socks[0] = conn->sock[FIRSTSOCKET];
|
||||
|
||||
if(stream->recv_error) {
|
||||
bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
|
||||
data->state.drain++;
|
||||
}
|
||||
else if(stream->recv_header_len || stream->recv_data_len) {
|
||||
bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
|
||||
data->state.drain++;
|
||||
}
|
||||
|
||||
H3BUGF(infof(data, "msh3_getsock %u", (uint32_t)data->state.drain));
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
static CURLcode msh3_do_it(struct Curl_easy *data, bool *done)
|
||||
{
|
||||
struct HTTP *stream = data->req.p.http;
|
||||
H3BUGF(infof(data, "msh3_do_it"));
|
||||
stream->recv_buf = malloc(MSH3_REQ_INIT_BUF_LEN);
|
||||
if(!stream->recv_buf) {
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
stream->req = ZERO_NULL;
|
||||
msh3_lock_initialize(&stream->recv_lock);
|
||||
stream->recv_buf_alloc = MSH3_REQ_INIT_BUF_LEN;
|
||||
stream->recv_header_len = 0;
|
||||
stream->recv_header_complete = false;
|
||||
stream->recv_data_len = 0;
|
||||
stream->recv_data_complete = false;
|
||||
stream->recv_error = CURLE_OK;
|
||||
return Curl_http(data, done);
|
||||
}
|
||||
|
||||
static unsigned int msh3_conncheck(struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
unsigned int checks_to_perform)
|
||||
{
|
||||
(void)data;
|
||||
(void)conn;
|
||||
(void)checks_to_perform;
|
||||
H3BUGF(infof(data, "msh3_conncheck"));
|
||||
return CONNRESULT_NONE;
|
||||
}
|
||||
|
||||
static void msh3_cleanup(struct quicsocket *qs, struct HTTP *stream)
|
||||
{
|
||||
if(stream && stream->recv_buf) {
|
||||
free(stream->recv_buf);
|
||||
stream->recv_buf = ZERO_NULL;
|
||||
msh3_lock_uninitialize(&stream->recv_lock);
|
||||
}
|
||||
if(qs->conn) {
|
||||
MsH3ConnectionClose(qs->conn);
|
||||
qs->conn = ZERO_NULL;
|
||||
}
|
||||
if(qs->api) {
|
||||
MsH3ApiClose(qs->api);
|
||||
qs->api = ZERO_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static CURLcode msh3_disconnect(struct Curl_easy *data,
|
||||
struct connectdata *conn, bool dead_connection)
|
||||
{
|
||||
(void)dead_connection;
|
||||
H3BUGF(infof(data, "disconnecting (msh3)"));
|
||||
msh3_cleanup(conn->quic, data->req.p.http);
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
void Curl_quic_disconnect(struct Curl_easy *data, struct connectdata *conn,
|
||||
int tempindex)
|
||||
{
|
||||
if(conn->transport == TRNSPRT_QUIC) {
|
||||
H3BUGF(infof(data, "disconnecting (curl)"));
|
||||
msh3_cleanup(&conn->hequic[tempindex], data->req.p.http);
|
||||
}
|
||||
}
|
||||
|
||||
/* Requires stream->recv_lock to be held */
|
||||
static bool msh3request_ensure_room(struct HTTP *stream, size_t len)
|
||||
{
|
||||
uint8_t *new_recv_buf;
|
||||
const size_t cur_recv_len = stream->recv_header_len + stream->recv_data_len;
|
||||
if(cur_recv_len + len > stream->recv_buf_alloc) {
|
||||
size_t new_recv_buf_alloc_len = stream->recv_buf_alloc;
|
||||
do {
|
||||
new_recv_buf_alloc_len <<= 1; /* TODO - handle overflow */
|
||||
} while(cur_recv_len + len > new_recv_buf_alloc_len);
|
||||
new_recv_buf = malloc(new_recv_buf_alloc_len);
|
||||
if(!new_recv_buf) {
|
||||
return false;
|
||||
}
|
||||
if(cur_recv_len) {
|
||||
memcpy(new_recv_buf, stream->recv_buf, cur_recv_len);
|
||||
}
|
||||
stream->recv_buf_alloc = new_recv_buf_alloc_len;
|
||||
free(stream->recv_buf);
|
||||
stream->recv_buf = new_recv_buf;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
|
||||
void *IfContext,
|
||||
const MSH3_HEADER *Header)
|
||||
{
|
||||
struct HTTP *stream = IfContext;
|
||||
size_t total_len;
|
||||
(void)Request;
|
||||
H3BUGF(printf("* msh3_header_received\n"));
|
||||
|
||||
if(stream->recv_header_complete) {
|
||||
H3BUGF(printf("* ignoring header after data\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
msh3_lock_acquire(&stream->recv_lock);
|
||||
|
||||
if((Header->NameLength == 7) &&
|
||||
!strncmp(H2H3_PSEUDO_STATUS, (char *)Header->Name, 7)) {
|
||||
total_len = 9 + Header->ValueLength;
|
||||
if(!msh3request_ensure_room(stream, total_len)) {
|
||||
/* TODO - handle error */
|
||||
goto release_lock;
|
||||
}
|
||||
msnprintf((char *)stream->recv_buf + stream->recv_header_len,
|
||||
stream->recv_buf_alloc - stream->recv_header_len,
|
||||
"HTTP/3 %.*s\n", (int)Header->ValueLength, Header->Value);
|
||||
}
|
||||
else {
|
||||
total_len = Header->NameLength + 4 + Header->ValueLength;
|
||||
if(!msh3request_ensure_room(stream, total_len)) {
|
||||
/* TODO - handle error */
|
||||
goto release_lock;
|
||||
}
|
||||
msnprintf((char *)stream->recv_buf + stream->recv_header_len,
|
||||
stream->recv_buf_alloc - stream->recv_header_len,
|
||||
"%.*s: %.*s\n",
|
||||
(int)Header->NameLength, Header->Name,
|
||||
(int)Header->ValueLength, Header->Value);
|
||||
}
|
||||
|
||||
stream->recv_header_len += total_len - 1; /* don't include null-terminator */
|
||||
|
||||
release_lock:
|
||||
msh3_lock_release(&stream->recv_lock);
|
||||
}
|
||||
|
||||
static void MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
|
||||
void *IfContext, uint32_t Length,
|
||||
const uint8_t *Data)
|
||||
{
|
||||
struct HTTP *stream = IfContext;
|
||||
size_t cur_recv_len = stream->recv_header_len + stream->recv_data_len;
|
||||
(void)Request;
|
||||
H3BUGF(printf("* msh3_data_received %u. %zu buffered, %zu allocated\n",
|
||||
Length, cur_recv_len, stream->recv_buf_alloc));
|
||||
msh3_lock_acquire(&stream->recv_lock);
|
||||
if(!stream->recv_header_complete) {
|
||||
H3BUGF(printf("* Headers complete!\n"));
|
||||
if(!msh3request_ensure_room(stream, 2)) {
|
||||
/* TODO - handle error */
|
||||
goto release_lock;
|
||||
}
|
||||
stream->recv_buf[stream->recv_header_len++] = '\r';
|
||||
stream->recv_buf[stream->recv_header_len++] = '\n';
|
||||
stream->recv_header_complete = true;
|
||||
cur_recv_len += 2;
|
||||
}
|
||||
if(!msh3request_ensure_room(stream, Length)) {
|
||||
/* TODO - handle error */
|
||||
goto release_lock;
|
||||
}
|
||||
memcpy(stream->recv_buf + cur_recv_len, Data, Length);
|
||||
stream->recv_data_len += (size_t)Length;
|
||||
release_lock:
|
||||
msh3_lock_release(&stream->recv_lock);
|
||||
}
|
||||
|
||||
static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
|
||||
bool Aborted, uint64_t AbortError)
|
||||
{
|
||||
struct HTTP *stream = IfContext;
|
||||
(void)Request;
|
||||
(void)AbortError;
|
||||
H3BUGF(printf("* msh3_complete, aborted=%hhu\n", Aborted));
|
||||
msh3_lock_acquire(&stream->recv_lock);
|
||||
if(Aborted) {
|
||||
stream->recv_error = CURLE_HTTP3; /* TODO - how do we pass AbortError? */
|
||||
}
|
||||
stream->recv_header_complete = true;
|
||||
stream->recv_data_complete = true;
|
||||
msh3_lock_release(&stream->recv_lock);
|
||||
}
|
||||
|
||||
static void MSH3_CALL msh3_shutdown(MSH3_REQUEST *Request, void *IfContext)
|
||||
{
|
||||
struct HTTP *stream = IfContext;
|
||||
(void)Request;
|
||||
(void)stream;
|
||||
}
|
||||
|
||||
static_assert(sizeof(MSH3_HEADER) == sizeof(struct h2h3pseudo),
|
||||
"Sizes must match for cast below to work");
|
||||
|
||||
static ssize_t msh3_stream_send(struct Curl_easy *data,
|
||||
int sockindex,
|
||||
const void *mem,
|
||||
size_t len,
|
||||
CURLcode *curlcode)
|
||||
{
|
||||
struct connectdata *conn = data->conn;
|
||||
struct HTTP *stream = data->req.p.http;
|
||||
struct quicsocket *qs = conn->quic;
|
||||
struct h2h3req *hreq;
|
||||
|
||||
(void)sockindex;
|
||||
H3BUGF(infof(data, "msh3_stream_send %zu", len));
|
||||
|
||||
if(!stream->req) {
|
||||
*curlcode = Curl_pseudo_headers(data, mem, len, &hreq);
|
||||
if(*curlcode) {
|
||||
failf(data, "Curl_pseudo_headers failed");
|
||||
return -1;
|
||||
}
|
||||
H3BUGF(infof(data, "starting request with %zu headers", hreq->entries));
|
||||
stream->req = MsH3RequestOpen(qs->conn, &msh3_request_if, stream,
|
||||
(MSH3_HEADER*)hreq->header, hreq->entries);
|
||||
Curl_pseudo_free(hreq);
|
||||
if(!stream->req) {
|
||||
failf(data, "request open failed");
|
||||
*curlcode = CURLE_SEND_ERROR;
|
||||
return -1;
|
||||
}
|
||||
*curlcode = CURLE_OK;
|
||||
return len;
|
||||
}
|
||||
H3BUGF(infof(data, "send %zd body bytes on request %p", len,
|
||||
(void *)stream->req));
|
||||
*curlcode = CURLE_SEND_ERROR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static ssize_t msh3_stream_recv(struct Curl_easy *data,
|
||||
int sockindex,
|
||||
char *buf,
|
||||
size_t buffersize,
|
||||
CURLcode *curlcode)
|
||||
{
|
||||
struct HTTP *stream = data->req.p.http;
|
||||
size_t outsize = 0;
|
||||
(void)sockindex;
|
||||
H3BUGF(infof(data, "msh3_stream_recv %zu", buffersize));
|
||||
|
||||
if(stream->recv_error) {
|
||||
failf(data, "request aborted");
|
||||
*curlcode = stream->recv_error;
|
||||
return -1;
|
||||
}
|
||||
|
||||
msh3_lock_acquire(&stream->recv_lock);
|
||||
|
||||
if(stream->recv_header_len) {
|
||||
outsize = buffersize;
|
||||
if(stream->recv_header_len < outsize) {
|
||||
outsize = stream->recv_header_len;
|
||||
}
|
||||
memcpy(buf, stream->recv_buf, outsize);
|
||||
if(outsize < stream->recv_header_len + stream->recv_data_len) {
|
||||
memmove(stream->recv_buf, stream->recv_buf + outsize,
|
||||
stream->recv_header_len + stream->recv_data_len - outsize);
|
||||
}
|
||||
stream->recv_header_len -= outsize;
|
||||
H3BUGF(infof(data, "returned %zu bytes of headers", outsize));
|
||||
}
|
||||
else if(stream->recv_data_len) {
|
||||
outsize = buffersize;
|
||||
if(stream->recv_data_len < outsize) {
|
||||
outsize = stream->recv_data_len;
|
||||
}
|
||||
memcpy(buf, stream->recv_buf, outsize);
|
||||
if(outsize < stream->recv_data_len) {
|
||||
memmove(stream->recv_buf, stream->recv_buf + outsize,
|
||||
stream->recv_data_len - outsize);
|
||||
}
|
||||
stream->recv_data_len -= outsize;
|
||||
H3BUGF(infof(data, "returned %zu bytes of data", outsize));
|
||||
}
|
||||
else if(stream->recv_data_complete) {
|
||||
H3BUGF(infof(data, "receive complete"));
|
||||
}
|
||||
|
||||
msh3_lock_release(&stream->recv_lock);
|
||||
|
||||
return (ssize_t)outsize;
|
||||
}
|
||||
|
||||
CURLcode Curl_quic_done_sending(struct Curl_easy *data)
|
||||
{
|
||||
struct connectdata *conn = data->conn;
|
||||
H3BUGF(infof(data, "Curl_quic_done_sending"));
|
||||
if(conn->handler == &msh3_curl_handler_http3) {
|
||||
struct HTTP *stream = data->req.p.http;
|
||||
stream->upload_done = TRUE;
|
||||
}
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
void Curl_quic_done(struct Curl_easy *data, bool premature)
|
||||
{
|
||||
(void)data;
|
||||
(void)premature;
|
||||
H3BUGF(infof(data, "Curl_quic_done"));
|
||||
}
|
||||
|
||||
bool Curl_quic_data_pending(const struct Curl_easy *data)
|
||||
{
|
||||
struct HTTP *stream = data->req.p.http;
|
||||
H3BUGF(infof((struct Curl_easy *)data, "Curl_quic_data_pending"));
|
||||
return stream->recv_header_len || stream->recv_data_len;
|
||||
}
|
||||
|
||||
#endif /* USE_MSH3 */
|
||||
38
lib/vquic/msh3.h
Normal file
38
lib/vquic/msh3.h
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#ifndef HEADER_CURL_VQUIC_MSH3_H
|
||||
#define HEADER_CURL_VQUIC_MSH3_H
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at https://curl.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#include "curl_setup.h"
|
||||
|
||||
#ifdef USE_MSH3
|
||||
|
||||
#include <msh3.h>
|
||||
|
||||
struct quicsocket {
|
||||
MSH3_API* api;
|
||||
MSH3_CONNECTION* conn;
|
||||
};
|
||||
|
||||
#endif /* USE_MSQUIC */
|
||||
|
||||
#endif /* HEADER_CURL_VQUIC_MSH3_H */
|
||||
Loading…
Add table
Add a link
Reference in a new issue