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:
parent
cd7bc174ce
commit
58974d25d8
273
lib/Makefile.inc
273
lib/Makefile.inc
@ -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
901
lib/c-hyper.c
Normal 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
46
lib/c-hyper.h
Normal 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
2460
lib/http.c
File diff suppressed because it is too large
Load Diff
55
lib/http.h
55
lib/http.h
@ -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;
|
||||
|
@ -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) */
|
||||
|
@ -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 */
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user