mirror of
https://gitlab.com/gnuwget/wget2.git
synced 2026-01-14 02:01:39 +00:00
665 lines
18 KiB
C
665 lines
18 KiB
C
#include <config.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <c-ctype.h>
|
|
#include <time.h>
|
|
#include <errno.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <string.h>
|
|
|
|
#ifdef WITH_LIBNGHTTP3
|
|
#include <nghttp3/nghttp3.h>
|
|
|
|
#endif
|
|
|
|
#ifdef WITH_LIBNGTCP2
|
|
#include <ngtcp2/ngtcp2.h>
|
|
|
|
#endif
|
|
|
|
#if ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__)
|
|
# include <sys/ioctl.h>
|
|
#else
|
|
# include <fcntl.h>
|
|
#endif
|
|
|
|
#include <wget.h>
|
|
#include "private.h"
|
|
#include "net.h"
|
|
#include "http.h"
|
|
|
|
static int _stop_sending(ngtcp2_conn *conn,
|
|
int64_t stream_id, uint64_t app_error_code);
|
|
static int _reset_stream(ngtcp2_conn *conn,
|
|
int64_t stream_id, uint64_t app_error_code);
|
|
static int _http3_consume(ngtcp2_conn *conn, uint64_t
|
|
stream_id, size_t nconsumed);
|
|
static int _http3_write_data(wget_quic* quic, int64_t stream_id, const uint8_t *data,
|
|
size_t datalen, uint8_t type);
|
|
static void init_nv(nghttp3_nv *nv, const char *name, const char *value);
|
|
static int _call_data_sender(int64_t stream_id, const nghttp3_vec *vec, size_t veccnt,
|
|
int (*_cb_func)(int64_t, const void*, void *), void *userdata);
|
|
void http3_stream_mark_acked (wget_quic_stream *stream, size_t offset);
|
|
int http3_stream_push(int64_t stream_id, const void* vector, void *userdata);
|
|
|
|
/* Name of the struct does not make a lot of sense as of now.
|
|
It will be changed
|
|
*/
|
|
struct http3_stream_context {
|
|
wget_http_response
|
|
*resp;
|
|
};
|
|
|
|
void
|
|
http3_stream_mark_acked (wget_quic_stream *stream, size_t datalen)
|
|
{
|
|
while (stream) {
|
|
wget_byte *head = (wget_byte *)wget_queue_peek_transmitted_node(stream->buffer);
|
|
if (wget_byte_get_size (head) > datalen)
|
|
break;
|
|
|
|
stream->ack_offset += wget_byte_get_size (head);
|
|
datalen -= wget_byte_get_size (head);
|
|
wget_queue_dequeue_transmitted_node(stream->buffer);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Close read side of a stream abruptly */
|
|
static int _stop_sending(ngtcp2_conn *conn,
|
|
int64_t stream_id, uint64_t app_error_code)
|
|
{
|
|
int ret = ngtcp2_conn_shutdown_stream_read(conn, 0,
|
|
stream_id, app_error_code);
|
|
if (ret < 0) {
|
|
error_printf("ERROR: ngtcp2_conn_shutdown_stream_read: %s\n",
|
|
ngtcp2_strerror(ret));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Close write side of a stream abruptly */
|
|
static int _reset_stream(ngtcp2_conn *conn,
|
|
int64_t stream_id, uint64_t app_error_code)
|
|
{
|
|
int ret = ngtcp2_conn_shutdown_stream_write(conn, 0,
|
|
stream_id, app_error_code);
|
|
if (ret < 0) {
|
|
error_printf("ERROR: ngtcp2_conn_shutdown_stream_write: %s\n",
|
|
ngtcp2_strerror(ret));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int _http3_consume(ngtcp2_conn *conn, uint64_t stream_id, size_t nconsumed)
|
|
{
|
|
int ret = ngtcp2_conn_extend_max_stream_offset(conn, stream_id, nconsumed);
|
|
if (ret < 0)
|
|
return ret;
|
|
ngtcp2_conn_extend_max_offset(conn, nconsumed);
|
|
return 0;
|
|
}
|
|
|
|
static int _http3_write_data(wget_quic* quic, int64_t stream_id, const uint8_t *data,
|
|
size_t datalen, uint8_t type)
|
|
{
|
|
if (!quic){
|
|
return -1;
|
|
}
|
|
wget_quic_stream *stream = wget_quic_stream_find(quic, stream_id);
|
|
if(stream){
|
|
int ret = wget_quic_stream_push(stream, (const char *)data, datalen, type);
|
|
if (ret < 0)
|
|
return ret;
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int recv_header_cb(nghttp3_conn *h3conn __attribute__((unused)),
|
|
int64_t stream_id __attribute__((unused)),
|
|
int32_t token __attribute__((unused)),
|
|
nghttp3_rcbuf *name, nghttp3_rcbuf *value,
|
|
uint8_t flags __attribute__((unused)),
|
|
void *conn_user_data __attribute__((unused)),
|
|
void *stream_user_data)
|
|
{
|
|
nghttp3_vec namevec, valuevec;
|
|
namevec = nghttp3_rcbuf_get_buf(name);
|
|
valuevec = nghttp3_rcbuf_get_buf(value);
|
|
debug_printf("Received header: %.*s: %.*s\n",
|
|
(int)namevec.len, namevec.base, (int)valuevec.len, valuevec.base);
|
|
struct http3_stream_context *ctx = (struct http3_stream_context *)stream_user_data;
|
|
if (!ctx || !ctx->resp){
|
|
return 0;
|
|
}
|
|
|
|
if (ctx->resp->req->response_keepheader || ctx->resp->req->header_callback) {
|
|
if (!ctx->resp->header)
|
|
ctx->resp->header = wget_buffer_alloc(1024);
|
|
}
|
|
|
|
if (ctx->resp->header)
|
|
wget_buffer_printf_append(ctx->resp->header, "%.*s: %.*s\n", (int)namevec.len, (char *) namevec.base, (int)valuevec.len, (char *) valuevec.base);
|
|
|
|
wget_http_parse_header_line(ctx->resp, (char *) namevec.base, (int)namevec.len, (char *) valuevec.base, (int)valuevec.len);
|
|
return 0;
|
|
}
|
|
|
|
static int end_headers_cb(nghttp3_conn *h3conn, int64_t stream_id, int fin,
|
|
void *conn_user_data, void *stream_user_data)
|
|
{
|
|
struct http3_stream_context *ctx = (struct http3_stream_context *) stream_user_data;
|
|
|
|
if (!ctx || !ctx->resp)
|
|
return 0;
|
|
|
|
debug_printf("End headers\n");
|
|
if (ctx->resp->req->header_callback)
|
|
ctx->resp->req->header_callback(ctx->resp, ctx->resp->req->header_user_data);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int deferred_consume_cb(nghttp3_conn *http3 __attribute__((unused)),
|
|
int64_t stream_id, size_t consumed,
|
|
void *conn_user_data,
|
|
void *stream_user_data __attribute__((unused)))
|
|
|
|
{
|
|
ngtcp2_conn *conn = (ngtcp2_conn *)conn_user_data;
|
|
int ret = _http3_consume(conn, stream_id, consumed);
|
|
if (ret < 0){
|
|
error_printf("ERROR: deferred_consume_cb\n");
|
|
return ret;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int recv_data_cb(nghttp3_conn *conn __attribute__((unused)),
|
|
int64_t stream_id, const uint8_t *data,
|
|
size_t datalen,
|
|
void *conn_user_data ,
|
|
void *stream_user_data __attribute__((unused)))
|
|
{
|
|
debug_printf("Recieving data | %s | from stream : %ld\n", data, stream_id);
|
|
wget_http_connection *http3 = (wget_http_connection *)conn_user_data;
|
|
int ret = _http3_write_data(http3->quic, stream_id, data, datalen, RESPONSE_DATA_BYTE);
|
|
if (ret < 0){
|
|
error_printf("ERROR: recv_data_cb : %d\n", ret);
|
|
return ret;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int acked_stream_data_cb(nghttp3_conn *conn __attribute__((unused)),
|
|
int64_t stream_id,
|
|
uint64_t datalen,
|
|
void *conn_user_data __attribute__((unused)),
|
|
void *stream_user_data)
|
|
{
|
|
wget_quic *connection = (wget_quic *)stream_user_data;
|
|
wget_quic_stream *stream = wget_quic_stream_find(connection, stream_id);
|
|
|
|
if (stream) {
|
|
http3_stream_mark_acked (stream, datalen);
|
|
debug_printf("acked %zu bytes on stream #%zd\n", datalen, stream_id);
|
|
} else
|
|
debug_printf("acked %zu bytes on no stream\n", datalen);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* It is called when QUIC STOP_SENDING frame must be sent
|
|
* for a particular stream. Application has to tell QUIC stack
|
|
* to send this frame.
|
|
*/
|
|
static int stop_sending_cb(nghttp3_conn *conn __attribute__((unused)),
|
|
int64_t stream_id, uint64_t app_error_code,
|
|
void *conn_user_data, void *stream_user_data __attribute__((unused)))
|
|
{
|
|
if (_stop_sending((ngtcp2_conn *)conn_user_data, stream_id, app_error_code) < 0){
|
|
error_printf("ERROR: stop_sending_cb\n");
|
|
return NGHTTP3_ERR_CALLBACK_FAILURE;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* It is called when QUIC RESET_STREAM frame must be sent
|
|
* for a particular stream. Application has to tell QUIC stack
|
|
* to send this frame.
|
|
*/
|
|
static int reset_stream_cb(nghttp3_conn *conn __attribute__((unused)),
|
|
int64_t stream_id, uint64_t app_error_code,
|
|
void *conn_user_data,
|
|
void *stream_user_data __attribute__((unused)))
|
|
{
|
|
if (_reset_stream((ngtcp2_conn *)conn_user_data, stream_id, app_error_code) < 0){
|
|
error_printf("ERROR: reset_stream_cb\n");
|
|
return NGHTTP3_ERR_CALLBACK_FAILURE;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static const nghttp3_callbacks callbacks = {
|
|
.acked_stream_data = acked_stream_data_cb,
|
|
.recv_data = recv_data_cb,
|
|
.deferred_consume = deferred_consume_cb,
|
|
.recv_header = recv_header_cb,
|
|
.end_headers = end_headers_cb,
|
|
.stop_sending = stop_sending_cb,
|
|
.reset_stream = reset_stream_cb
|
|
};
|
|
|
|
int http3_stream_push(int64_t stream_id, const void* vector,
|
|
void *userdata)
|
|
{
|
|
int ret;
|
|
wget_quic_stream *stream;
|
|
wget_quic *quic = userdata;
|
|
nghttp3_vec * vec = (nghttp3_vec *)vector;
|
|
|
|
|
|
if ((stream = wget_quic_stream_find(quic, stream_id)) == NULL)
|
|
return -1;
|
|
|
|
if ((ret = wget_quic_stream_push(stream, (const char *)vec->base, vec->len, REQUEST_BYTE)) <= 0)
|
|
return -1;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void init_nv(nghttp3_nv *nv, const char *name, const char *value)
|
|
{
|
|
nv->name = (const uint8_t *) name;
|
|
nv->value = (const uint8_t *) value;
|
|
nv->namelen = strlen(name);
|
|
nv->valuelen = strlen(value);
|
|
nv->flags = NGHTTP3_NV_FLAG_NONE;
|
|
}
|
|
|
|
static void mark_stream_as_fin(wget_quic *quic, int64_t stream_id)
|
|
{
|
|
wget_quic_stream *stream = wget_quic_stream_find(quic, stream_id);
|
|
if (stream)
|
|
wget_quic_stream_set_fin(stream);
|
|
}
|
|
|
|
static int _call_data_sender(int64_t stream_id, const nghttp3_vec *vec, size_t veccnt,
|
|
int (*_cb_func)(int64_t, const void *, void *), void *userdata)
|
|
{
|
|
int ret, ttl_sent = 0;
|
|
|
|
for (unsigned i = 0; i < veccnt; i++) {
|
|
ret = _cb_func(stream_id, (const void *)vec, userdata);
|
|
if (ret > 0)
|
|
ttl_sent += ret;
|
|
else if (ret == 0)
|
|
break;
|
|
else
|
|
return ret;
|
|
}
|
|
|
|
return ttl_sent;
|
|
}
|
|
|
|
|
|
#ifdef WITH_LIBNGHTTP3
|
|
static int http3_write_streams(wget_http_connection *http3)
|
|
{
|
|
wget_quic_stream *streams[] = {
|
|
http3->control_stream,
|
|
http3->qpac_decoder_stream,
|
|
http3->qpac_encoder_stream,
|
|
http3->client_stream,
|
|
NULL
|
|
};
|
|
|
|
return wget_quic_write_multiple(http3->quic, streams, 4);
|
|
}
|
|
|
|
/**
|
|
* \param [in] http3 A `wget_http_connection` connection.
|
|
* \param [in] req A `wget_http_request` structure which stores the information necessary to send request.
|
|
*
|
|
* This function extracts necessary from the `wget_http_request` structure and writes the data over streams using the
|
|
* underlying QUIC connection after subimitting the request. This function also reads all the streams in the
|
|
* QUIC stack to receive the data.
|
|
* Returns error values or WGET_E_SUCCESS.
|
|
*
|
|
* \return int
|
|
*/
|
|
int wget_http3_send_request(wget_http_connection *http3, wget_http_request *req)
|
|
{
|
|
int finish, ret;
|
|
int64_t stream_id;
|
|
size_t nv_len = 0;
|
|
int n = 4 + wget_vector_size(req->headers);
|
|
|
|
nghttp3_nv nv_headers[n], *nvp;
|
|
nghttp3_ssize n_sent;
|
|
nghttp3_vec *vec = wget_malloc(sizeof(nghttp3_vec) * n);
|
|
size_t veccnt = n;
|
|
char resource[req->esc_resource.length + 2];
|
|
|
|
resource[0] = '/';
|
|
memcpy(resource + 1, req->esc_resource.data, req->esc_resource.length + 1);
|
|
|
|
|
|
init_nv(&nv_headers[0],":method", req->method);
|
|
init_nv(&nv_headers[1],":scheme", "https");
|
|
init_nv(&nv_headers[2],":authority", req->esc_host.data);
|
|
init_nv(&nv_headers[3],":path", resource);
|
|
|
|
nv_len = n;
|
|
nvp = &nv_headers[4];
|
|
|
|
for (int it = 0; it < wget_vector_size(req->headers); it++) {
|
|
wget_http_header_param *param = wget_vector_get(req->headers, it);
|
|
|
|
if (!wget_strcasecmp_ascii(param->name, "Host")) {
|
|
nv_len--;
|
|
continue;
|
|
}
|
|
if (!wget_strcasecmp_ascii(param->name, "Connection")) {
|
|
nv_len--;
|
|
continue;
|
|
}
|
|
|
|
init_nv(nvp++, param->name, param->value);
|
|
}
|
|
|
|
struct http3_stream_context *ctx = wget_calloc(1, sizeof(struct http3_stream_context));
|
|
ctx->resp = wget_calloc(1, sizeof(wget_http_response));
|
|
ctx->resp->req = req;
|
|
ctx->resp->major = 3;
|
|
// we do not get a Keep-Alive header in HTTP2 - let's assume the connection stays open
|
|
ctx->resp->keep_alive = 1;
|
|
http3->http3_ctx = ctx;
|
|
|
|
if ((ret = nghttp3_conn_submit_request(http3->conn,
|
|
wget_quic_stream_get_stream_id(http3->client_stream),
|
|
nv_headers, nv_len, NULL, ctx)) < 0) {
|
|
error_printf("ERROR: nghttp3_conn_submit_request: %s\n",
|
|
nghttp3_strerror(ret));
|
|
goto bail;
|
|
}
|
|
|
|
memset(vec, 0, sizeof(nghttp3_vec) * veccnt);
|
|
|
|
do {
|
|
n_sent = nghttp3_conn_writev_stream(http3->conn, &stream_id, &finish, vec, veccnt);
|
|
|
|
if (n_sent > 0) {
|
|
if ((ret = _call_data_sender(stream_id, vec, n_sent, http3_stream_push, http3->quic)) >= 0)
|
|
nghttp3_conn_add_write_offset(http3->conn, stream_id, ret);
|
|
else
|
|
goto bail;
|
|
}
|
|
|
|
if (finish == 1) {
|
|
mark_stream_as_fin(http3->quic, stream_id);
|
|
}
|
|
|
|
/* ret = wget_quic_write(http3->quic, wget_quic_stream_find(http3->quic, stream_id)); */
|
|
/* if (ret < 0) */
|
|
/* goto bail; */
|
|
|
|
} while (finish == 0);
|
|
|
|
xfree(vec);
|
|
|
|
ret = http3_write_streams(http3);
|
|
if (ret < 0) {
|
|
error_printf("Error in http3_write_streams\n");
|
|
return -1;
|
|
}
|
|
|
|
wget_quic_ack(http3->quic);
|
|
|
|
return WGET_E_SUCCESS;
|
|
|
|
bail:
|
|
error_printf("ERROR: Sender callback failed: %d\n", ret);
|
|
return WGET_E_UNKNOWN;
|
|
}
|
|
#else
|
|
int wget_http3_send_request(wget_http_connection *http3, wget_http_request *req)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#ifdef WITH_LIBNGHTTP3
|
|
/**
|
|
* \param [in] h3 A initialised `wget_http_connection` double pointer.
|
|
*
|
|
* This function deletes the the nghttp3_conn and destroys the underlying
|
|
* `wget_quic` structure as well as the `wget_http_connection`
|
|
* structure.
|
|
*
|
|
*/
|
|
void wget_http3_close(wget_http_connection **h3)
|
|
{
|
|
wget_http_connection *http3 = *h3;
|
|
if (http3) {
|
|
if (http3->conn) {
|
|
nghttp3_conn_del(http3->conn);
|
|
http3->conn = NULL;
|
|
}
|
|
|
|
if (http3->quic) {
|
|
wget_quic_close(http3->quic);
|
|
wget_quic_deinit(&http3->quic);
|
|
http3->quic = NULL;
|
|
}
|
|
|
|
xfree(http3->http3_ctx);
|
|
}
|
|
}
|
|
#else
|
|
void wget_http3_close(wget_http_connection **h3)
|
|
{
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef WITH_LIBNGHTTP3
|
|
/**
|
|
* \param [in] h3 A `wget_http_connection` double pointer.
|
|
* \param [in] iri Internal representation of URI/IRI
|
|
*
|
|
* This function initialises the `wget_http_connection` structure, creates
|
|
* a HTTP3 client, creates a QUIC connection over socket and creates all the
|
|
* streams necessary over QUIC to support working of HTTP3 as per NGHTTP3 library.
|
|
*
|
|
* \return int
|
|
*/
|
|
int wget_http3_open(wget_http_connection **h3, const wget_iri *iri)
|
|
{
|
|
int ret;
|
|
wget_http_connection *http3;
|
|
const char
|
|
*hostname;
|
|
uint16_t
|
|
port;
|
|
|
|
hostname = iri->host;
|
|
port = iri->port;
|
|
|
|
http3 = *h3 = wget_calloc(1, sizeof(wget_http_connection));
|
|
if (!http3){
|
|
return WGET_E_MEMORY;
|
|
}
|
|
|
|
http3->protocol = WGET_PROTOCOL_HTTP_3_0;
|
|
|
|
nghttp3_settings_default(&http3->settings);
|
|
|
|
http3->mem = nghttp3_mem_default();
|
|
if (!http3->mem){
|
|
xfree(http3);
|
|
return WGET_E_UNKNOWN;
|
|
}
|
|
|
|
http3->quic = wget_quic_init();
|
|
if (!http3->quic){
|
|
xfree(http3);
|
|
return WGET_E_UNKNOWN;
|
|
}
|
|
|
|
wget_quic_set_ssl_hostname(http3->quic, hostname);
|
|
|
|
ret = nghttp3_conn_client_new(
|
|
&http3->conn, &callbacks, &http3->settings, http3->mem, http3);
|
|
if (ret < 0) {
|
|
error_printf("Error in nghttp3_conn_client_new\n");
|
|
wget_http3_close(&http3);
|
|
return WGET_E_UNKNOWN;
|
|
}
|
|
|
|
wget_quic_set_http3_conn(http3->quic, http3->conn);
|
|
|
|
ret = wget_quic_connect(http3->quic, hostname, port);
|
|
if (ret < 0) {
|
|
error_printf("Error in wget_quic_connect()\n");
|
|
wget_http3_close(&http3);
|
|
return WGET_E_CONNECT;
|
|
}
|
|
|
|
if ((http3->control_stream = wget_quic_stream_init_unidirectional(http3->quic)) == NULL){
|
|
return WGET_E_UNKNOWN;
|
|
}
|
|
if ((http3->qpac_encoder_stream = wget_quic_stream_init_unidirectional(http3->quic)) == NULL){
|
|
return WGET_E_UNKNOWN;
|
|
}
|
|
if ((http3->qpac_decoder_stream = wget_quic_stream_init_unidirectional(http3->quic)) == NULL){
|
|
return WGET_E_UNKNOWN;
|
|
}
|
|
if ((http3->client_stream = wget_quic_stream_init_bidirectional(http3->quic)) == NULL){
|
|
return WGET_E_UNKNOWN;
|
|
}
|
|
|
|
if ((ret = nghttp3_conn_bind_control_stream(http3->conn, wget_quic_stream_get_stream_id(http3->control_stream))) < 0) {
|
|
error_printf("ERROR: nghttp3_conn_bind_control_stream: %s\n",
|
|
nghttp3_strerror(ret));
|
|
wget_http3_close(&http3);
|
|
return WGET_E_UNKNOWN;
|
|
}
|
|
if ((ret = nghttp3_conn_bind_qpack_streams(http3->conn,
|
|
wget_quic_stream_get_stream_id(http3->qpac_encoder_stream), wget_quic_stream_get_stream_id(http3->qpac_decoder_stream))) < 0) {
|
|
error_printf("ERROR: nghttp3_conn_bind_qpack_streams: %s\n",
|
|
nghttp3_strerror(ret));
|
|
wget_http3_close(&http3);
|
|
return WGET_E_UNKNOWN;
|
|
}
|
|
|
|
return WGET_E_SUCCESS;
|
|
}
|
|
#else
|
|
int wget_http3_open(wget_http_connection **h3, const wget_iri *iri)
|
|
{
|
|
return WGET_E_UNSUPPORTED;
|
|
}
|
|
#endif
|
|
|
|
#ifdef WITH_LIBNGHTTP3
|
|
/**
|
|
* \param [in] http3 A `wget_http_connection` structure representing HTTP3 connection
|
|
*
|
|
* Data incoming from server is stored in the `client_stream` using `wget_byte` struct.
|
|
* This function iteratively dequeue's all the bytes with type data and
|
|
* returns it using a `wegt_http_response` structure.
|
|
*
|
|
* \return wget_http_response *
|
|
*/
|
|
wget_http_response *wget_http3_get_response(wget_http_connection *http3)
|
|
{
|
|
char *data = NULL;
|
|
size_t offset = 0;
|
|
wget_http_response *resp;
|
|
|
|
if (!http3 || !http3->http3_ctx)
|
|
return NULL;
|
|
|
|
while (wget_quic_read(http3->quic) >= 0 && !wget_quic_get_is_closed(http3->quic)) {
|
|
wget_quic_ack(http3->quic);
|
|
}
|
|
|
|
resp = ((struct http3_stream_context *) http3->http3_ctx)->resp;
|
|
|
|
wget_queue_node *node = wget_queue_dequeue_data_node(
|
|
wget_quic_stream_get_buffer(http3->client_stream));
|
|
wget_byte *byte = (wget_byte *) node->data;
|
|
while (byte) {
|
|
data = wget_realloc(data, offset + wget_byte_get_size(byte));
|
|
memcpy(data + offset, wget_byte_get_data(byte), wget_byte_get_size(byte));
|
|
offset += wget_byte_get_size(byte);
|
|
|
|
wget_queue_free_node(node, (void (*)(void *)) wget_byte_free);
|
|
|
|
node = wget_queue_dequeue_data_node(wget_quic_stream_get_buffer(http3->client_stream));
|
|
if (!node)
|
|
break;
|
|
|
|
byte = (wget_byte *) node->data;
|
|
}
|
|
|
|
wget_buffer *buff = wget_calloc(1, sizeof(wget_buffer));
|
|
if (!buff) {
|
|
xfree(resp);
|
|
return NULL;
|
|
}
|
|
|
|
buff->data = data;
|
|
buff->length = offset;
|
|
buff->size = offset;
|
|
|
|
resp->body = buff;
|
|
return resp;
|
|
}
|
|
#else
|
|
wget_http_response *wget_http3_get_response(wget_http_connection *http3)
|
|
{
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
#ifdef WITH_LIBNGHTTP3
|
|
wget_http_response *wget_http3_get_response_cb(wget_http_connection *conn)
|
|
{
|
|
wget_decompressor *dc = NULL;
|
|
wget_http_response *resp = NULL;
|
|
|
|
resp = wget_http3_get_response(conn);
|
|
if (!resp)
|
|
goto cleanup;
|
|
|
|
dc = wget_decompress_open(resp->content_encoding, http_get_body_cb, resp);
|
|
wget_decompress_set_error_handler(dc, http_decompress_error_handler_cb);
|
|
wget_decompress(dc, resp->body->data, resp->body->length);
|
|
wget_decompress_close(dc);
|
|
|
|
return resp;
|
|
|
|
cleanup:
|
|
wget_decompress_close(dc);
|
|
return NULL;
|
|
}
|
|
#else
|
|
wget_http_response *wget_http3_get_response_cb(wget_http_connection *conn)
|
|
{
|
|
return NULL;
|
|
}
|
|
#endif
|