1
0
mirror of https://github.com/moparisthebest/curl synced 2024-12-23 08:38:49 -05:00

lib: introduce c-hyper for using Hyper

... as an alternative HTTP backend within libcurl.
This commit is contained in:
Daniel Stenberg 2020-12-14 14:10:33 +01:00
parent cd7bc174ce
commit 58974d25d8
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
7 changed files with 2582 additions and 1246 deletions

View File

@ -44,43 +44,244 @@ LIB_VSSH_CFILES = vssh/libssh.c vssh/libssh2.c vssh/wolfssh.c
LIB_VSSH_HFILES = vssh/ssh.h
LIB_CFILES = altsvc.c amigaos.c asyn-ares.c asyn-thread.c base64.c \
conncache.c connect.c content_encoding.c cookie.c curl_addrinfo.c \
curl_ctype.c curl_des.c curl_endian.c curl_fnmatch.c curl_get_line.c \
curl_gethostname.c curl_gssapi.c curl_memrchr.c curl_multibyte.c \
curl_ntlm_core.c curl_ntlm_wb.c curl_path.c curl_range.c curl_rtmp.c \
curl_sasl.c curl_sspi.c curl_threads.c dict.c dotdot.c easy.c escape.c \
file.c fileinfo.c formdata.c ftp.c url.c ftplistparser.c getenv.c getinfo.c \
gopher.c hash.c hmac.c hostasyn.c hostcheck.c hostip.c hostip4.c hostip6.c \
hostsyn.c http.c http2.c http_chunks.c http_digest.c http_negotiate.c \
http_ntlm.c http_proxy.c idn_win32.c if2ip.c imap.c inet_ntop.c inet_pton.c \
krb5.c ldap.c llist.c md4.c md5.c memdebug.c mime.c mprintf.c mqtt.c \
multi.c netrc.c non-ascii.c nonblock.c openldap.c parsedate.c pingpong.c \
pop3.c progress.c psl.c doh.c rand.c rename.c rtsp.c select.c \
sendf.c setopt.c sha256.c share.c slist.c smb.c smtp.c socketpair.c socks.c \
socks_gssapi.c socks_sspi.c speedcheck.c splay.c strcase.c strdup.c \
strerror.c strtok.c strtoofft.c system_win32.c telnet.c tftp.c timeval.c \
transfer.c urlapi.c version.c warnless.c wildcard.c x509asn1.c dynbuf.c \
version_win32.c easyoptions.c easygetopt.c hsts.c
LIB_CFILES = \
altsvc.c \
amigaos.c \
asyn-ares.c \
asyn-thread.c \
base64.c \
c-hyper.c \
conncache.c \
connect.c \
content_encoding.c \
cookie.c \
curl_addrinfo.c \
curl_ctype.c \
curl_des.c \
curl_endian.c \
curl_fnmatch.c \
curl_get_line.c \
curl_gethostname.c \
curl_gssapi.c \
curl_memrchr.c \
curl_multibyte.c \
curl_ntlm_core.c \
curl_ntlm_wb.c \
curl_path.c \
curl_range.c \
curl_rtmp.c \
curl_sasl.c \
curl_sspi.c \
curl_threads.c \
dict.c \
doh.c \
dotdot.c \
dynbuf.c \
easy.c \
easygetopt.c \
easyoptions.c \
escape.c \
file.c \
fileinfo.c \
formdata.c \
ftp.c \
ftplistparser.c \
getenv.c \
getinfo.c \
gopher.c \
hash.c \
hmac.c \
hostasyn.c \
hostcheck.c \
hostip.c \
hostip4.c \
hostip6.c \
hostsyn.c \
hsts.c \
http.c \
http2.c \
http_chunks.c \
http_digest.c \
http_negotiate.c \
http_ntlm.c \
http_proxy.c \
idn_win32.c \
if2ip.c \
imap.c \
inet_ntop.c \
inet_pton.c \
krb5.c \
ldap.c \
llist.c \
md4.c \
md5.c \
memdebug.c \
mime.c \
mprintf.c \
mqtt.c \
multi.c \
netrc.c \
non-ascii.c \
nonblock.c \
openldap.c \
parsedate.c \
pingpong.c \
pop3.c \
progress.c \
psl.c \
rand.c \
rename.c \
rtsp.c \
select.c \
sendf.c \
setopt.c \
sha256.c \
share.c \
slist.c \
smb.c \
smtp.c \
socketpair.c \
socks.c \
socks_gssapi.c \
socks_sspi.c \
speedcheck.c \
splay.c \
strcase.c \
strdup.c \
strerror.c \
strtok.c \
strtoofft.c \
system_win32.c \
telnet.c \
tftp.c \
timeval.c \
transfer.c \
url.c \
urlapi.c \
version.c \
version_win32.c \
warnless.c \
wildcard.c \
x509asn1.c
LIB_HFILES = altsvc.h amigaos.h arpa_telnet.h asyn.h conncache.h connect.h \
content_encoding.h cookie.h curl_addrinfo.h curl_base64.h curl_ctype.h \
curl_des.h curl_endian.h curl_fnmatch.h curl_get_line.h curl_gethostname.h \
curl_gssapi.h curl_hmac.h curl_ldap.h curl_md4.h curl_md5.h curl_memory.h \
curl_memrchr.h curl_multibyte.h curl_ntlm_core.h curl_ntlm_wb.h curl_path.h \
curl_printf.h curl_range.h curl_rtmp.h curl_sasl.h curl_krb5.h curl_setup.h \
curl_setup_once.h curl_sha256.h curl_sspi.h curl_threads.h curlx.h dict.h \
dotdot.h easyif.h escape.h file.h fileinfo.h formdata.h ftp.h url.h \
ftplistparser.h getinfo.h gopher.h hash.h hostcheck.h hostip.h http.h \
http2.h http_chunks.h http_digest.h http_negotiate.h http_ntlm.h \
http_proxy.h if2ip.h imap.h inet_ntop.h inet_pton.h llist.h memdebug.h \
mime.h mqtt.h multihandle.h multiif.h netrc.h non-ascii.h nonblock.h \
parsedate.h pingpong.h pop3.h progress.h psl.h doh.h quic.h rand.h rename.h \
rtsp.h select.h sendf.h setopt.h setup-vms.h share.h sigpipe.h slist.h \
smb.h smtp.h sockaddr.h socketpair.h socks.h speedcheck.h splay.h strcase.h \
strdup.h strerror.h strtok.h strtoofft.h system_win32.h telnet.h tftp.h \
timeval.h transfer.h urlapi-int.h urldata.h warnless.h wildcard.h \
x509asn1.h dynbuf.h version_win32.h easyoptions.h hsts.h
LIB_HFILES = \
altsvc.h \
amigaos.h \
arpa_telnet.h \
asyn.h \
c-hyper.h \
conncache.h \
connect.h \
content_encoding.h \
cookie.h \
curl_addrinfo.h \
curl_base64.h \
curl_ctype.h \
curl_des.h \
curl_endian.h \
curl_fnmatch.h \
curl_get_line.h \
curl_gethostname.h \
curl_gssapi.h \
curl_hmac.h \
curl_krb5.h \
curl_ldap.h \
curl_md4.h \
curl_md5.h \
curl_memory.h \
curl_memrchr.h \
curl_multibyte.h \
curl_ntlm_core.h \
curl_ntlm_wb.h \
curl_path.h \
curl_printf.h \
curl_range.h \
curl_rtmp.h \
curl_sasl.h \
curl_setup.h \
curl_setup_once.h \
curl_sha256.h \
curl_sspi.h \
curl_threads.h \
curlx.h \
dict.h \
doh.h \
dotdot.h \
dynbuf.h \
easyif.h \
easyoptions.h \
escape.h \
file.h \
fileinfo.h \
formdata.h \
ftp.h \
ftplistparser.h \
getinfo.h \
gopher.h \
hash.h \
hostcheck.h \
hostip.h \
hsts.h \
http.h \
http2.h \
http_chunks.h \
http_digest.h \
http_negotiate.h \
http_ntlm.h \
http_proxy.h \
if2ip.h \
imap.h \
inet_ntop.h \
inet_pton.h \
llist.h \
memdebug.h \
mime.h \
mqtt.h \
multihandle.h \
multiif.h \
netrc.h \
non-ascii.h \
nonblock.h \
parsedate.h \
pingpong.h \
pop3.h \
progress.h \
psl.h \
quic.h \
rand.h \
rename.h \
rtsp.h \
select.h \
sendf.h \
setopt.h \
setup-vms.h \
share.h \
sigpipe.h \
slist.h \
smb.h \
smtp.h \
sockaddr.h \
socketpair.h \
socks.h \
speedcheck.h \
splay.h \
strcase.h \
strdup.h \
strerror.h \
strtok.h \
strtoofft.h \
system_win32.h \
telnet.h \
tftp.h \
timeval.h \
transfer.h \
url.h \
urlapi-int.h \
urldata.h \
version_win32.h \
warnless.h \
wildcard.h \
x509asn1.h
LIB_RCFILES = libcurl.rc

901
lib/c-hyper.c Normal file
View File

@ -0,0 +1,901 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2020, 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.haxx.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"
#if !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER)
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_NET_IF_H
#include <net/if.h>
#endif
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#include <hyper.h>
#include "urldata.h"
#include "sendf.h"
#include "transfer.h"
#include "multiif.h"
#include "progress.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
#include "memdebug.h"
static size_t read_cb(void *userp, hyper_context *ctx,
uint8_t *buf, size_t buflen)
{
struct connectdata *conn = (struct connectdata *)userp;
struct Curl_easy *data = conn->data;
CURLcode result;
ssize_t nread;
(void)ctx;
result = Curl_read(conn, conn->sockfd, (char *)buf, buflen, &nread);
if(result == CURLE_AGAIN) {
/* would block, register interest */
if(data->hyp.read_waker)
hyper_waker_free(data->hyp.read_waker);
data->hyp.read_waker = hyper_context_waker(ctx);
if(!data->hyp.read_waker) {
failf(data, "Couldn't make the read hyper_context_waker");
return HYPER_IO_ERROR;
}
return HYPER_IO_PENDING;
}
else if(result) {
failf(data, "Curl_read failed");
return HYPER_IO_ERROR;
}
return (size_t)nread;
}
static size_t write_cb(void *userp, hyper_context *ctx,
const uint8_t *buf, size_t buflen)
{
struct connectdata *conn = (struct connectdata *)userp;
struct Curl_easy *data = conn->data;
CURLcode result;
ssize_t nwrote;
result = Curl_write(conn, conn->sockfd, (void *)buf, buflen, &nwrote);
if(result == CURLE_AGAIN) {
/* would block, register interest */
if(data->hyp.write_waker)
hyper_waker_free(data->hyp.write_waker);
data->hyp.write_waker = hyper_context_waker(ctx);
if(!data->hyp.write_waker) {
failf(data, "Couldn't make the write hyper_context_waker");
return HYPER_IO_ERROR;
}
return HYPER_IO_PENDING;
}
else if(result) {
failf(data, "Curl_write failed");
return HYPER_IO_ERROR;
}
return (size_t)nwrote;
}
static int hyper_each_header(void *userdata,
const uint8_t *name,
size_t name_len,
const uint8_t *value,
size_t value_len)
{
struct Curl_easy *data = (struct Curl_easy *)userdata;
size_t wrote;
size_t len;
char *headp;
CURLcode result;
curl_write_callback writeheader =
data->set.fwrite_header? data->set.fwrite_header: data->set.fwrite_func;
Curl_dyn_reset(&data->state.headerb);
if(name_len) {
if(Curl_dyn_addf(&data->state.headerb, "%.*s: %.*s\r\n",
(int) name_len, name, (int) value_len, value))
return HYPER_ITER_BREAK;
}
else {
if(Curl_dyn_add(&data->state.headerb, "\r\n"))
return HYPER_ITER_BREAK;
}
len = Curl_dyn_len(&data->state.headerb);
headp = Curl_dyn_ptr(&data->state.headerb);
result = Curl_http_header(data, data->conn, headp);
if(result) {
data->state.hresult = result;
return HYPER_ITER_BREAK;
}
Curl_debug(data, CURLINFO_HEADER_IN, headp, len);
Curl_set_in_callback(data, true);
wrote = writeheader(headp, 1, len, data->set.writeheader);
Curl_set_in_callback(data, false);
if(wrote != len) {
data->state.hresult = CURLE_ABORTED_BY_CALLBACK;
return HYPER_ITER_BREAK;
}
data->info.header_size += (long)len;
data->req.headerbytecount += (long)len;
return HYPER_ITER_CONTINUE;
}
static int hyper_body_chunk(void *userdata, const hyper_buf *chunk)
{
char *buf = (char *)hyper_buf_bytes(chunk);
size_t len = hyper_buf_len(chunk);
struct Curl_easy *data = (struct Curl_easy *)userdata;
curl_write_callback writebody = data->set.fwrite_func;
struct SingleRequest *k = &data->req;
size_t wrote;
if(0 == k->bodywrites++) {
bool done = FALSE;
CURLcode result = Curl_http_firstwrite(data, data->conn, &done);
if(result || done) {
infof(data, "Return early from hyper_body_chunk\n");
data->state.hresult = result;
return HYPER_ITER_BREAK;
}
}
if(k->ignorebody)
return HYPER_ITER_CONTINUE;
Curl_debug(data, CURLINFO_DATA_IN, buf, len);
Curl_set_in_callback(data, true);
wrote = writebody(buf, 1, len, data->set.out);
Curl_set_in_callback(data, false);
if(wrote != len) {
data->state.hresult = CURLE_WRITE_ERROR;
return HYPER_ITER_BREAK;
}
data->req.bytecount += len;
Curl_pgrsSetDownloadCounter(data, data->req.bytecount);
return HYPER_ITER_CONTINUE;
}
/*
* Hyper does not consider the status line, the first line in a HTTP/1
* response, to be a header. The libcurl API does. This function sends the
* status line in the header callback. */
static CURLcode status_line(struct Curl_easy *data,
struct connectdata *conn,
uint16_t http_status,
int http_version,
const uint8_t *reason, size_t rlen)
{
CURLcode result;
size_t wrote;
size_t len;
const char *vstr;
curl_write_callback writeheader =
data->set.fwrite_header? data->set.fwrite_header: data->set.fwrite_func;
vstr = http_version == HYPER_HTTP_VERSION_1_1 ? "1.1" :
(http_version == HYPER_HTTP_VERSION_2 ? "2" : "1.0");
conn->httpversion =
http_version == HYPER_HTTP_VERSION_1_1 ? 11 :
(http_version == HYPER_HTTP_VERSION_2 ? 20 : 10);
data->req.httpcode = http_status;
result = Curl_http_statusline(data, conn);
if(result)
return result;
Curl_dyn_reset(&data->state.headerb);
result = Curl_dyn_addf(&data->state.headerb, "HTTP/%s %03d %.*s\r\n",
vstr,
(int)http_status,
(int)rlen, reason);
if(result)
return result;
len = Curl_dyn_len(&data->state.headerb);
Curl_debug(data, CURLINFO_HEADER_IN, Curl_dyn_ptr(&data->state.headerb),
len);
Curl_set_in_callback(data, true);
wrote = writeheader(Curl_dyn_ptr(&data->state.headerb), 1, len,
data->set.writeheader);
Curl_set_in_callback(data, false);
if(wrote != len)
return CURLE_WRITE_ERROR;
data->info.header_size += (long)len;
data->req.headerbytecount += (long)len;
data->req.httpcode = http_status;
return CURLE_OK;
}
/*
* Hyper does not pass on the last empty response header. The libcurl API
* does. This function sends an empty header in the header callback.
*/
static CURLcode empty_header(struct Curl_easy *data)
{
return hyper_each_header(data, NULL, 0, NULL, 0) ?
CURLE_WRITE_ERROR : CURLE_OK;
}
static CURLcode hyperstream(struct Curl_easy *data,
struct connectdata *conn,
int *didwhat,
bool *done,
int select_res)
{
hyper_response *resp = NULL;
uint16_t http_status;
int http_version;
hyper_headers *headers = NULL;
hyper_body *resp_body = NULL;
struct hyptransfer *h = &data->hyp;
hyper_task *task;
hyper_task *foreach;
hyper_error *hypererr = NULL;
const uint8_t *reasonp;
size_t reason_len;
CURLcode result = CURLE_OK;
(void)conn;
if(select_res & CURL_CSELECT_IN) {
if(h->read_waker)
hyper_waker_wake(h->read_waker);
h->read_waker = NULL;
}
if(select_res & CURL_CSELECT_OUT) {
if(h->write_waker)
hyper_waker_wake(h->write_waker);
h->write_waker = NULL;
}
*done = FALSE;
do {
hyper_task_return_type t;
task = hyper_executor_poll(h->exec);
if(!task) {
*didwhat = KEEP_RECV;
break;
}
t = hyper_task_type(task);
switch(t) {
case HYPER_TASK_ERROR:
hypererr = hyper_task_value(task);
break;
case HYPER_TASK_RESPONSE:
resp = hyper_task_value(task);
break;
default:
break;
}
hyper_task_free(task);
if(t == HYPER_TASK_ERROR) {
hyper_code errnum = hyper_error_code(hypererr);
if(errnum == HYPERE_ABORTED_BY_CALLBACK) {
/* override Hyper's view, might not even be an error */
result = data->state.hresult;
infof(data, "hyperstream is done (by early callback)\n");
}
else {
uint8_t errbuf[256];
size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf));
failf(data, "Hyper: %.*s", (int)errlen, errbuf);
result = CURLE_RECV_ERROR; /* not a very good return code */
}
*done = TRUE;
hyper_error_free(hypererr);
break;
}
else if(h->init) {
/* end of transfer */
*done = TRUE;
infof(data, "hyperstream is done!\n");
break;
}
else if(t != HYPER_TASK_RESPONSE) {
*didwhat = KEEP_RECV;
break;
}
/* HYPER_TASK_RESPONSE */
h->init = TRUE;
*didwhat = KEEP_RECV;
if(!resp) {
failf(data, "hyperstream: couldn't get response\n");
return CURLE_RECV_ERROR;
}
http_status = hyper_response_status(resp);
http_version = hyper_response_version(resp);
reasonp = hyper_response_reason_phrase(resp);
reason_len = hyper_response_reason_phrase_len(resp);
result = status_line(data, conn,
http_status, http_version, reasonp, reason_len);
if(result)
break;
headers = hyper_response_headers(resp);
if(!headers) {
failf(data, "hyperstream: couldn't get response headers\n");
result = CURLE_RECV_ERROR;
break;
}
/* the headers are already received */
hyper_headers_foreach(headers, hyper_each_header, data);
if(data->state.hresult) {
result = data->state.hresult;
break;
}
if(empty_header(data)) {
failf(data, "hyperstream: couldn't pass blank header\n");
result = CURLE_OUT_OF_MEMORY;
break;
}
resp_body = hyper_response_body(resp);
if(!resp_body) {
failf(data, "hyperstream: couldn't get response body\n");
result = CURLE_RECV_ERROR;
break;
}
foreach = hyper_body_foreach(resp_body, hyper_body_chunk, data);
if(!foreach) {
failf(data, "hyperstream: body foreach failed\n");
result = CURLE_OUT_OF_MEMORY;
break;
}
DEBUGASSERT(hyper_task_type(foreach) == HYPER_TASK_EMPTY);
if(HYPERE_OK != hyper_executor_push(h->exec, foreach)) {
failf(data, "Couldn't hyper_executor_push the body-foreach");
result = CURLE_OUT_OF_MEMORY;
break;
}
hyper_response_free(resp);
resp = NULL;
} while(1);
if(resp)
hyper_response_free(resp);
return result;
}
static CURLcode debug_request(struct Curl_easy *data,
const char *method,
const char *path,
bool h2)
{
char *req = aprintf("%s %s HTTP/%s\r\n", method, path,
h2?"2":"1.1");
if(!req)
return CURLE_OUT_OF_MEMORY;
Curl_debug(data, CURLINFO_HEADER_OUT, req, strlen(req));
free(req);
return CURLE_OK;
}
/*
* Given a full header line "name: value" (optional CRLF in the input, should
* be in the output), add to Hyper and send to the debug callback.
*
* Supports multiple headers.
*/
CURLcode Curl_hyper_header(struct Curl_easy *data, hyper_headers *headers,
const char *line)
{
const char *p;
const char *n;
size_t nlen;
const char *v;
size_t vlen;
bool newline = TRUE;
int numh = 0;
if(!line)
return CURLE_OK;
n = line;
do {
size_t linelen = 0;
p = strchr(n, ':');
if(!p)
/* this is fine if we already added at least one header */
return numh ? CURLE_OK : CURLE_BAD_FUNCTION_ARGUMENT;
nlen = p - n;
p++; /* move past the colon */
while(*p == ' ')
p++;
v = p;
p = strchr(v, '\r');
if(!p) {
p = strchr(v, '\n');
if(p)
linelen = 1; /* LF only */
else {
p = strchr(v, '\0');
newline = FALSE; /* no newline */
}
}
else
linelen = 2; /* CRLF ending */
linelen += (p - n);
if(!n)
return CURLE_BAD_FUNCTION_ARGUMENT;
vlen = p - v;
if(HYPERE_OK != hyper_headers_add(headers, (uint8_t *)n, nlen,
(uint8_t *)v, vlen)) {
failf(data, "hyper_headers_add host\n");
return CURLE_OUT_OF_MEMORY;
}
if(data->set.verbose) {
char *ptr = NULL;
if(!newline) {
ptr = aprintf("%.*s\r\n", (int)linelen, line);
if(!ptr)
return CURLE_OUT_OF_MEMORY;
Curl_debug(data, CURLINFO_HEADER_OUT, ptr, linelen + 2);
free(ptr);
}
else
Curl_debug(data, CURLINFO_HEADER_OUT, (char *)line, linelen);
}
numh++;
n += linelen;
} while(newline);
return CURLE_OK;
}
static CURLcode request_target(struct Curl_easy *data,
struct connectdata *conn,
const char *method,
bool h2,
hyper_request *req)
{
CURLcode result;
struct dynbuf r;
Curl_dyn_init(&r, DYN_HTTP_REQUEST);
result = Curl_http_target(data, conn, &r);
if(result)
return result;
if(hyper_request_set_uri(req, (uint8_t *)Curl_dyn_uptr(&r),
Curl_dyn_len(&r))) {
failf(data, "error setting path\n");
result = CURLE_OUT_OF_MEMORY;
}
else
result = debug_request(data, method, Curl_dyn_ptr(&r), h2);
Curl_dyn_free(&r);
return result;
}
static int uploadpostfields(void *userdata, hyper_context *ctx,
hyper_buf **chunk)
{
struct Curl_easy *data = (struct Curl_easy *)userdata;
(void)ctx;
if(data->req.upload_done)
*chunk = NULL; /* nothing more to deliver */
else {
/* send everything off in a single go */
*chunk = hyper_buf_copy(data->set.postfields, data->req.p.http->postsize);
data->req.upload_done = TRUE;
}
return HYPER_POLL_READY;
}
static int uploadstreamed(void *userdata, hyper_context *ctx,
hyper_buf **chunk)
{
size_t fillcount;
struct Curl_easy *data = (struct Curl_easy *)userdata;
CURLcode result =
Curl_fillreadbuffer(data->conn, data->set.upload_buffer_size,
&fillcount);
(void)ctx;
if(result)
return HYPER_POLL_ERROR;
if(!fillcount)
/* done! */
*chunk = NULL;
else
*chunk = hyper_buf_copy((uint8_t *)data->state.ulbuf, fillcount);
return HYPER_POLL_READY;
}
/*
* bodysend() sets up headers in the outgoing request for a HTTP transfer that
* sends a body
*/
static CURLcode bodysend(struct Curl_easy *data,
struct connectdata *conn,
hyper_headers *headers,
hyper_request *hyperreq,
Curl_HttpReq httpreq)
{
CURLcode result;
struct dynbuf req;
if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD))
Curl_pgrsSetUploadSize(data, 0); /* no request body */
else {
hyper_body *body;
Curl_dyn_init(&req, DYN_HTTP_REQUEST);
result = Curl_http_bodysend(data, conn, &req, httpreq);
if(!result)
result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&req));
Curl_dyn_free(&req);
body = hyper_body_new();
hyper_body_set_userdata(body, data);
if(data->set.postfields)
hyper_body_set_data_func(body, uploadpostfields);
else {
result = Curl_get_upload_buffer(data);
if(result)
return result;
/* init the "upload from here" pointer */
data->req.upload_fromhere = data->state.ulbuf;
hyper_body_set_data_func(body, uploadstreamed);
}
if(HYPERE_OK != hyper_request_set_body(hyperreq, body)) {
/* fail */
hyper_body_free(body);
result = CURLE_OUT_OF_MEMORY;
}
}
return result;
}
static CURLcode cookies(struct Curl_easy *data,
struct connectdata *conn,
hyper_headers *headers)
{
struct dynbuf req;
CURLcode result;
Curl_dyn_init(&req, DYN_HTTP_REQUEST);
result = Curl_http_cookies(data, conn, &req);
if(!result)
result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&req));
Curl_dyn_free(&req);
return result;
}
/*
* Curl_http() gets called from the generic multi_do() function when a HTTP
* request is to be performed. This creates and sends a properly constructed
* HTTP request.
*/
CURLcode Curl_http(struct connectdata *conn, bool *done)
{
struct Curl_easy *data = conn->data;
struct hyptransfer *h = &data->hyp;
hyper_io *io = NULL;
hyper_clientconn_options *options = NULL;
hyper_task *task = NULL; /* for the handshake */
hyper_task *sendtask = NULL; /* for the send */
hyper_clientconn *client = NULL;
hyper_request *req = NULL;
hyper_headers *headers = NULL;
hyper_task *handshake = NULL;
hyper_error *hypererr = NULL;
CURLcode result;
const char *p_accept; /* Accept: string */
const char *method;
Curl_HttpReq httpreq;
bool h2 = FALSE;
const char *te = NULL; /* transfer-encoding */
/* Always consider the DO phase done after this function call, even if there
may be parts of the request that is not yet sent, since we can deal with
the rest of the request in the PERFORM phase. */
*done = TRUE;
infof(data, "Time for the Hyper dance\n");
memset(h, 0, sizeof(struct hyptransfer));
result = Curl_http_host(data, conn);
if(result)
return result;
Curl_http_method(data, conn, &method, &httpreq);
/* setup the authentication headers */
{
char *pq = NULL;
if(data->state.up.query) {
pq = aprintf("%s?%s", data->state.up.path, data->state.up.query);
if(!pq)
return CURLE_OUT_OF_MEMORY;
}
result = Curl_http_output_auth(conn, method,
(pq ? pq : data->state.up.path), FALSE);
free(pq);
if(result)
return result;
}
result = Curl_http_resume(data, conn, httpreq);
if(result)
return result;
result = Curl_http_range(data, conn, httpreq);
if(result)
return result;
result = Curl_http_useragent(data, conn);
if(result)
return result;
io = hyper_io_new();
if(!io) {
failf(data, "Couldn't create hyper IO");
goto error;
}
/* tell Hyper how to read/write network data */
hyper_io_set_userdata(io, conn);
hyper_io_set_read(io, read_cb);
hyper_io_set_write(io, write_cb);
/* create an executor to poll futures */
if(!h->exec) {
h->exec = hyper_executor_new();
if(!h->exec) {
failf(data, "Couldn't create hyper executor");
goto error;
}
}
options = hyper_clientconn_options_new();
if(!options) {
failf(data, "Couldn't create hyper client options");
goto error;
}
if(conn->negnpn == CURL_HTTP_VERSION_2) {
hyper_clientconn_options_http2(options, 1);
h2 = TRUE;
}
hyper_clientconn_options_exec(options, h->exec);
/* "Both the `io` and the `options` are consumed in this function call" */
handshake = hyper_clientconn_handshake(io, options);
if(!handshake) {
failf(data, "Couldn't create hyper client handshake");
goto error;
}
io = NULL;
options = NULL;
if(HYPERE_OK != hyper_executor_push(h->exec, handshake)) {
failf(data, "Couldn't hyper_executor_push the handshake");
goto error;
}
handshake = NULL; /* ownership passed on */
task = hyper_executor_poll(h->exec);
if(!task) {
failf(data, "Couldn't hyper_executor_poll the handshake");
goto error;
}
client = hyper_task_value(task);
hyper_task_free(task);
req = hyper_request_new();
if(!req) {
failf(data, "Couldn't hyper_request_new");
goto error;
}
if(data->set.httpversion == CURL_HTTP_VERSION_1_0) {
if(HYPERE_OK != hyper_request_set_version(req,
HYPER_HTTP_VERSION_1_0)) {
failf(data, "error settting HTTP version");
goto error;
}
}
if(hyper_request_set_method(req, (uint8_t *)method, strlen(method))) {
failf(data, "error setting method");
goto error;
}
result = request_target(data, conn, method, h2, req);
if(result)
goto error;
headers = hyper_request_headers(req);
if(!headers) {
failf(data, "hyper_request_headers\n");
goto error;
}
result = Curl_http_body(data, conn, httpreq, &te);
if(result)
return result;
if(data->state.aptr.host &&
Curl_hyper_header(data, headers, data->state.aptr.host))
goto error;
if(data->state.aptr.proxyuserpwd &&
Curl_hyper_header(data, headers, data->state.aptr.proxyuserpwd))
goto error;
if(data->state.aptr.userpwd &&
Curl_hyper_header(data, headers, data->state.aptr.userpwd))
goto error;
if((data->state.use_range && data->state.aptr.rangeline) &&
Curl_hyper_header(data, headers, data->state.aptr.rangeline))
goto error;
if(data->set.str[STRING_USERAGENT] &&
*data->set.str[STRING_USERAGENT] &&
data->state.aptr.uagent &&
Curl_hyper_header(data, headers, data->state.aptr.uagent))
goto error;
p_accept = Curl_checkheaders(conn, "Accept")?NULL:"Accept: */*\r\n";
if(p_accept && Curl_hyper_header(data, headers, p_accept))
goto error;
if(te && Curl_hyper_header(data, headers, te))
goto error;
#ifndef CURL_DISABLE_PROXY
if(conn->bits.httpproxy && !conn->bits.tunnel_proxy &&
!Curl_checkProxyheaders(conn, "Proxy-Connection")) {
if(Curl_hyper_header(data, headers, "Proxy-Connection: Keep-Alive"))
goto error;
}
#endif
Curl_safefree(data->state.aptr.ref);
if(data->change.referer && !Curl_checkheaders(conn, "Referer")) {
data->state.aptr.ref = aprintf("Referer: %s\r\n", data->change.referer);
if(!data->state.aptr.ref)
return CURLE_OUT_OF_MEMORY;
if(Curl_hyper_header(data, headers, data->state.aptr.ref))
goto error;
}
result = cookies(data, conn, headers);
if(result)
return result;
result = Curl_add_custom_headers(conn, FALSE, headers);
if(result)
return result;
if((httpreq != HTTPREQ_GET) && (httpreq != HTTPREQ_HEAD)) {
result = bodysend(data, conn, headers, req, httpreq);
if(result)
return result;
}
Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"\r\n", 2);
data->req.upload_chunky = FALSE;
sendtask = hyper_clientconn_send(client, req);
if(!sendtask) {
failf(data, "hyper_clientconn_send\n");
goto error;
}
if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) {
failf(data, "Couldn't hyper_executor_push the send");
goto error;
}
hyper_clientconn_free(client);
do {
task = hyper_executor_poll(h->exec);
if(task) {
bool error = hyper_task_type(task) == HYPER_TASK_ERROR;
if(error)
hypererr = hyper_task_value(task);
hyper_task_free(task);
if(error)
goto error;
}
} while(task);
if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) {
/* HTTP GET/HEAD download */
Curl_pgrsSetUploadSize(data, 0); /* nothing */
Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, -1);
}
conn->datastream = hyperstream;
return CURLE_OK;
error:
if(io)
hyper_io_free(io);
if(options)
hyper_clientconn_options_free(options);
if(handshake)
hyper_task_free(handshake);
if(hypererr) {
uint8_t errbuf[256];
size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf));
failf(data, "Hyper: %.*s", (int)errlen, errbuf);
hyper_error_free(hypererr);
}
return CURLE_OUT_OF_MEMORY;
}
void Curl_hyper_done(struct Curl_easy *data)
{
struct hyptransfer *h = &data->hyp;
if(h->exec) {
hyper_executor_free(h->exec);
h->exec = NULL;
}
if(h->read_waker) {
hyper_waker_free(h->read_waker);
h->read_waker = NULL;
}
if(h->write_waker) {
hyper_waker_free(h->write_waker);
h->write_waker = NULL;
}
}
#endif /* !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER) */

46
lib/c-hyper.h Normal file
View File

@ -0,0 +1,46 @@
#ifndef HEADER_CURL_HYPER_H
#define HEADER_CURL_HYPER_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2020, 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.haxx.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"
#if !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER)
#include <hyper.h>
/* per-transfer data for the Hyper backend */
struct hyptransfer {
hyper_waker *write_waker;
hyper_waker *read_waker;
const hyper_executor *exec;
bool init;
};
CURLcode Curl_hyper_header(struct Curl_easy *data, hyper_headers *headers,
const char *line);
void Curl_hyper_done(struct Curl_easy *);
#else
#define Curl_hyper_done(x)
#endif /* !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER) */
#endif /* HEADER_CURL_HYPER_H */

2460
lib/http.c

File diff suppressed because it is too large Load Diff

View File

@ -35,6 +35,17 @@ extern const struct Curl_handler Curl_handler_http;
extern const struct Curl_handler Curl_handler_https;
#endif
typedef enum {
HTTPREQ_NONE, /* first in list */
HTTPREQ_GET,
HTTPREQ_POST,
HTTPREQ_POST_FORM, /* we make a difference internally */
HTTPREQ_POST_MIME, /* we make a difference internally */
HTTPREQ_PUT,
HTTPREQ_HEAD,
HTTPREQ_LAST /* last in list */
} Curl_HttpReq;
/* Header specific functions */
bool Curl_compareheader(const char *headerline, /* line to check */
const char *header, /* header keyword _with_ colon */
@ -44,21 +55,62 @@ char *Curl_copy_header_value(const char *header);
char *Curl_checkProxyheaders(const struct connectdata *conn,
const char *thisheader);
#ifndef USE_HYPER
CURLcode Curl_buffer_send(struct dynbuf *in,
struct connectdata *conn,
curl_off_t *bytes_written,
size_t included_body_bytes,
int socketindex);
#else
#define Curl_buffer_send(a,b,c,d,e) CURLE_OK
#endif
CURLcode Curl_add_timecondition(const struct connectdata *conn,
struct dynbuf *buf);
CURLcode Curl_add_custom_headers(struct connectdata *conn,
bool is_connect,
struct dynbuf *req_buffer);
#ifndef USE_HYPER
struct dynbuf *req
#else
void *headers
#endif
);
CURLcode Curl_http_compile_trailers(struct curl_slist *trailers,
struct dynbuf *buf,
struct Curl_easy *handle);
void Curl_http_method(struct Curl_easy *data, struct connectdata *conn,
const char **method, Curl_HttpReq *);
CURLcode Curl_http_useragent(struct Curl_easy *data, struct connectdata *conn);
CURLcode Curl_http_host(struct Curl_easy *data, struct connectdata *conn);
CURLcode Curl_http_target(struct Curl_easy *data, struct connectdata *conn,
struct dynbuf *req);
CURLcode Curl_http_statusline(struct Curl_easy *data,
struct connectdata *conn);
CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn,
char *headp);
CURLcode Curl_http_body(struct Curl_easy *data, struct connectdata *conn,
Curl_HttpReq httpreq,
const char **teep);
CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
struct dynbuf *r, Curl_HttpReq httpreq);
#ifndef CURL_DISABLE_COOKIES
CURLcode Curl_http_cookies(struct Curl_easy *data,
struct connectdata *conn,
struct dynbuf *r);
#else
#define Curl_http_cookies(a,b,c) CURLE_OK
#endif
CURLcode Curl_http_resume(struct Curl_easy *data,
struct connectdata *conn,
Curl_HttpReq httpreq);
CURLcode Curl_http_range(struct Curl_easy *data,
struct connectdata *conn,
Curl_HttpReq httpreq);
CURLcode Curl_http_firstwrite(struct Curl_easy *data,
struct connectdata *conn,
bool *done);
/* protocol-specific functions set up to be called by the main engine */
CURLcode Curl_http(struct connectdata *conn, bool *done);
CURLcode Curl_http_done(struct connectdata *, CURLcode, bool premature);
@ -115,7 +167,6 @@ struct HTTP {
const char *postdata;
const char *p_pragma; /* Pragma: string */
const char *p_accept; /* Accept: string */
/* For FORM posting */
curl_mimepart form;

View File

@ -708,64 +708,10 @@ static CURLcode readwrite_data(struct Curl_easy *data,
write a piece of the body */
if(conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_RTSP)) {
/* HTTP-only checks */
if(data->req.newurl) {
if(conn->bits.close) {
/* Abort after the headers if "follow Location" is set
and we're set to close anyway. */
k->keepon &= ~KEEP_RECV;
*done = TRUE;
return CURLE_OK;
result = Curl_http_firstwrite(data, conn, done);
if(result || *done)
return result;
}
/* We have a new url to load, but since we want to be able
to re-use this connection properly, we read the full
response in "ignore more" */
k->ignorebody = TRUE;
infof(data, "Ignoring the response-body\n");
}
if(data->state.resume_from && !k->content_range &&
(data->state.httpreq == HTTPREQ_GET) &&
!k->ignorebody) {
if(k->size == data->state.resume_from) {
/* The resume point is at the end of file, consider this fine
even if it doesn't allow resume from here. */
infof(data, "The entire document is already downloaded");
connclose(conn, "already downloaded");
/* Abort download */
k->keepon &= ~KEEP_RECV;
*done = TRUE;
return CURLE_OK;
}
/* we wanted to resume a download, although the server doesn't
* seem to support this and we did this with a GET (if it
* wasn't a GET we did a POST or PUT resume) */
failf(data, "HTTP server doesn't seem to support "
"byte ranges. Cannot resume.");
return CURLE_RANGE_ERROR;
}
if(data->set.timecondition && !data->state.range) {
/* A time condition has been set AND no ranges have been
requested. This seems to be what chapter 13.3.4 of
RFC 2616 defines to be the correct action for a
HTTP/1.1 client */
if(!Curl_meets_timecondition(data, k->timeofdoc)) {
*done = TRUE;
/* We're simulating a http 304 from server so we return
what should have been returned from the server */
data->info.httpcode = 304;
infof(data, "Simulate a HTTP 304 response!\n");
/* we abort the transfer before it is completed == we ruin the
re-use ability. Close the connection */
connclose(conn, "Simulated 304 handling");
return CURLE_OK;
}
} /* we have a time condition */
} /* this is HTTP or RTSP */
} /* this is the first time we write a body part */
#endif /* CURL_DISABLE_HTTP */
@ -1263,6 +1209,10 @@ CURLcode Curl_readwrite(struct connectdata *conn,
return CURLE_SEND_ERROR;
}
#ifdef USE_HYPER
if(conn->datastream)
return conn->datastream(data, conn, &didwhat, done, select_res);
#endif
/* We go ahead and do a read if we have a readable socket or if
the stream was rewound (in which case we have data in a
buffer) */

View File

@ -118,6 +118,14 @@ typedef ssize_t (Curl_recv)(struct connectdata *conn, /* connection data */
size_t len, /* max amount to read */
CURLcode *err); /* error to return */
#ifdef USE_HYPER
typedef CURLcode (*Curl_datastream)(struct Curl_easy *data,
struct connectdata *conn,
int *didwhat,
bool *done,
int select_res);
#endif
#include "mime.h"
#include "imap.h"
#include "pop3.h"
@ -132,6 +140,7 @@ typedef ssize_t (Curl_recv)(struct connectdata *conn, /* connection data */
#include "wildcard.h"
#include "multihandle.h"
#include "quic.h"
#include "c-hyper.h"
#ifdef HAVE_GSSAPI
# ifdef HAVE_GSSGNU
@ -1114,6 +1123,10 @@ struct connectdata {
#ifdef USE_UNIX_SOCKETS
char *unix_domain_socket;
#endif
#ifdef USE_HYPER
/* if set, an alternative data transfer function */
Curl_datastream datastream;
#endif
};
/* The end of connectdata. */
@ -1208,17 +1221,6 @@ struct Progress {
BIT(is_t_startransfer_set);
};
typedef enum {
HTTPREQ_NONE, /* first in list */
HTTPREQ_GET,
HTTPREQ_POST,
HTTPREQ_POST_FORM, /* we make a difference internally */
HTTPREQ_POST_MIME, /* we make a difference internally */
HTTPREQ_PUT,
HTTPREQ_HEAD,
HTTPREQ_LAST /* last in list */
} Curl_HttpReq;
typedef enum {
RTSPREQ_NONE, /* first in list */
RTSPREQ_OPTIONS,
@ -1400,14 +1402,17 @@ struct UrlState {
int stream_weight;
CURLU *uh; /* URL handle for the current parsed URL */
struct urlpieces up;
Curl_HttpReq httpreq; /* what kind of HTTP request (if any) is this */
#ifndef CURL_DISABLE_HTTP
Curl_HttpReq httpreq; /* what kind of HTTP request (if any) is this */
size_t trailers_bytes_sent;
struct dynbuf trailers_buf; /* a buffer containing the compiled trailing
headers */
#endif
trailers_state trailers_state; /* whether we are sending trailers
and what stage are we at */
#ifdef USE_HYPER
CURLcode hresult; /* used to pass return codes back from hyper callbacks */
#endif
/* Dynamically allocated strings, MUST be freed before this struct is
killed. */
@ -1571,7 +1576,6 @@ enum dupstring {
STRING_ALTSVC, /* CURLOPT_ALTSVC */
STRING_HSTS, /* CURLOPT_HSTS */
STRING_SASL_AUTHZID, /* CURLOPT_SASL_AUTHZID */
STRING_TEMP_URL, /* temp URL storage for proxy use */
STRING_DNS_SERVERS,
STRING_DNS_INTERFACE,
STRING_DNS_LOCAL_IP4,
@ -1702,7 +1706,9 @@ struct UserDefined {
the hostname and port to connect to */
curl_TimeCond timecondition; /* kind of time/date comparison */
time_t timevalue; /* what time to compare with */
#ifndef CURL_DISABLE_HTTP
Curl_HttpReq method; /* what kind of HTTP request (if any) is this */
#endif
long httpversion; /* when non-zero, a specific HTTP version requested to
be used in the library's request(s) */
struct ssl_config_data ssl; /* user defined SSL stuff */
@ -1936,6 +1942,9 @@ struct Curl_easy {
iconv_t inbound_cd; /* for translating from the network encoding */
iconv_t utf8_cd; /* for translating to UTF8 */
#endif /* CURL_DOES_CONVERSIONS && HAVE_ICONV */
#ifdef USE_HYPER
struct hyptransfer hyp;
#endif
unsigned int magic; /* set to a CURLEASY_MAGIC_NUMBER */
};