curl 2024-03-27 (de7b3e89)
Code extracted from: https://github.com/curl/curl.git at commit de7b3e89218467159a7af72d58cea8425946e97d (curl-8_7_1).stage/master/nightly/2024/04/06
parent
851cc904a0
commit
e17d8d0c3b
|
@ -68,35 +68,6 @@ macro(curl_internal_test CURL_TEST)
|
|||
endif()
|
||||
endmacro()
|
||||
|
||||
macro(curl_nroff_check)
|
||||
find_program(NROFF NAMES gnroff nroff)
|
||||
if(NROFF)
|
||||
# Need a way to write to stdin, this will do
|
||||
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/nroff-input.txt" "test")
|
||||
# Tests for a valid nroff option to generate a manpage
|
||||
foreach(_MANOPT "-man" "-mandoc")
|
||||
execute_process(COMMAND "${NROFF}" ${_MANOPT}
|
||||
OUTPUT_VARIABLE NROFF_MANOPT_OUTPUT
|
||||
INPUT_FILE "${CMAKE_CURRENT_BINARY_DIR}/nroff-input.txt"
|
||||
ERROR_QUIET)
|
||||
# Save the option if it was valid
|
||||
if(NROFF_MANOPT_OUTPUT)
|
||||
message("Found *nroff option: -- ${_MANOPT}")
|
||||
set(NROFF_MANOPT ${_MANOPT})
|
||||
set(NROFF_USEFUL ON)
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
# No need for the temporary file
|
||||
file(REMOVE "${CMAKE_CURRENT_BINARY_DIR}/nroff-input.txt")
|
||||
if(NOT NROFF_USEFUL)
|
||||
message(WARNING "Found no *nroff option to get plaintext from man pages")
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "Found no *nroff program")
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
macro(optional_dependency DEPENDENCY)
|
||||
set(CURL_${DEPENDENCY} AUTO CACHE STRING "Build curl with ${DEPENDENCY} support (AUTO, ON or OFF)")
|
||||
set_property(CACHE CURL_${DEPENDENCY} PROPERTY STRINGS AUTO ON OFF)
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
###########################################################################
|
||||
# File containing various utilities
|
||||
|
||||
# Returns a list of arguments that evaluate to true
|
||||
# Returns number of arguments that evaluate to true
|
||||
function(count_true output_count_var)
|
||||
set(lst_len 0)
|
||||
foreach(option_var IN LISTS ARGN)
|
||||
|
|
|
@ -307,18 +307,14 @@ endif()
|
|||
find_package(Perl)
|
||||
|
||||
option(BUILD_LIBCURL_DOCS "to build libcurl man pages" ON)
|
||||
# curl source release tarballs come with the curl man page pre-built.
|
||||
option(ENABLE_CURL_MANUAL "to build the man page for curl and enable its -M/--manual option" OFF)
|
||||
option(ENABLE_CURL_MANUAL "to build the man page for curl and enable its -M/--manual option" ON)
|
||||
|
||||
if(ENABLE_CURL_MANUAL OR BUILD_LIBCURL_DOCS)
|
||||
if(PERL_FOUND)
|
||||
curl_nroff_check()
|
||||
if(NROFF_USEFUL)
|
||||
set(HAVE_MANUAL_TOOLS ON)
|
||||
endif()
|
||||
set(HAVE_MANUAL_TOOLS ON)
|
||||
endif()
|
||||
if(NOT HAVE_MANUAL_TOOLS)
|
||||
message(WARNING "Perl not found, or nroff not useful. Will not build manuals.")
|
||||
message(WARNING "Perl not found. Will not build manuals.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
@ -396,8 +392,7 @@ if(APPLE)
|
|||
endif()
|
||||
if(WIN32)
|
||||
cmake_dependent_option(CURL_USE_SCHANNEL "Enable Windows native SSL/TLS" OFF CURL_ENABLE_SSL OFF)
|
||||
cmake_dependent_option(CURL_WINDOWS_SSPI "Use windows libraries to allow NTLM authentication without OpenSSL" ON
|
||||
CURL_USE_SCHANNEL OFF)
|
||||
option(CURL_WINDOWS_SSPI "Enable SSPI on Windows" ${CURL_USE_SCHANNEL})
|
||||
endif()
|
||||
cmake_dependent_option(CURL_USE_MBEDTLS "Enable mbedTLS for SSL/TLS" OFF CURL_ENABLE_SSL OFF)
|
||||
cmake_dependent_option(CURL_USE_BEARSSL "Enable BearSSL for SSL/TLS" OFF CURL_ENABLE_SSL OFF)
|
||||
|
@ -720,7 +715,26 @@ if(USE_MSH3)
|
|||
list(APPEND CURL_LIBS ${MSH3_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(CURL_WITH_MULTI_SSL AND (USE_NGTCP2 OR USE_QUICHE OR USE_MSH3))
|
||||
option(USE_OPENSSL_QUIC "Use openssl and nghttp3 libraries for HTTP/3 support" OFF)
|
||||
if(USE_OPENSSL_QUIC)
|
||||
if(USE_NGTCP2 OR USE_QUICHE OR USE_MSH3)
|
||||
message(FATAL_ERROR "Only one HTTP/3 backend can be selected!")
|
||||
endif()
|
||||
find_package(OpenSSL 3.2.0 REQUIRED)
|
||||
|
||||
find_package(NGHTTP3 REQUIRED)
|
||||
set(USE_NGHTTP3 ON)
|
||||
include_directories(${NGHTTP3_INCLUDE_DIRS})
|
||||
list(APPEND CURL_LIBS ${NGHTTP3_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(USE_MBEDTLS OR
|
||||
USE_BEARSSL OR
|
||||
USE_SECTRANSP)
|
||||
message(WARNING "A selected TLS library does not support TLS 1.3.")
|
||||
endif()
|
||||
|
||||
if(CURL_WITH_MULTI_SSL AND (USE_NGTCP2 OR USE_QUICHE OR USE_MSH3 OR USE_OPENSSL_QUIC))
|
||||
message(FATAL_ERROR "MultiSSL cannot be enabled with HTTP/3 and vice versa.")
|
||||
endif()
|
||||
|
||||
|
@ -1542,7 +1556,7 @@ if(NOT CURL_DISABLE_INSTALL)
|
|||
NOT CURL_DISABLE_HTTP AND NTLM_WB_ENABLED)
|
||||
_add_if("TLS-SRP" USE_TLS_SRP)
|
||||
_add_if("HTTP2" USE_NGHTTP2)
|
||||
_add_if("HTTP3" USE_NGTCP2 OR USE_QUICHE)
|
||||
_add_if("HTTP3" USE_NGTCP2 OR USE_QUICHE OR USE_OPENSSL_QUIC)
|
||||
_add_if("MultiSSL" CURL_WITH_MULTI_SSL)
|
||||
# TODO wolfSSL only support this from v5.0.0 onwards
|
||||
_add_if("HTTPS-proxy" SSL_ENABLED AND (USE_OPENSSL OR USE_GNUTLS
|
||||
|
@ -1627,6 +1641,30 @@ if(NOT CURL_DISABLE_INSTALL)
|
|||
set(LDFLAGS "${CMAKE_SHARED_LINKER_FLAGS}")
|
||||
set(LIBCURL_LIBS "")
|
||||
set(libdir "${CMAKE_INSTALL_PREFIX}/lib")
|
||||
|
||||
# For processing full path libraries into -L and -l ld options,
|
||||
# the directories that go with the -L option are cached, so they
|
||||
# only get added once per such directory.
|
||||
set(_libcurl_libs_dirs)
|
||||
# To avoid getting unnecessary -L options for known system directories,
|
||||
# _libcurl_libs_dirs is seeded with them.
|
||||
foreach(_libdir ${CMAKE_SYSTEM_PREFIX_PATH})
|
||||
if(_libdir MATCHES "/$")
|
||||
set(_libdir "${_libdir}lib")
|
||||
else()
|
||||
set(_libdir "${_libdir}/lib")
|
||||
endif()
|
||||
if(IS_DIRECTORY "${_libdir}")
|
||||
list(APPEND _libcurl_libs_dirs "${_libdir}")
|
||||
endif()
|
||||
if(DEFINED CMAKE_LIBRARY_ARCHITECTURE)
|
||||
set(_libdir "${_libdir}/${CMAKE_LIBRARY_ARCHITECTURE}")
|
||||
if(IS_DIRECTORY "${_libdir}")
|
||||
list(APPEND _libcurl_libs_dirs "${_libdir}")
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
foreach(_lib ${CMAKE_C_IMPLICIT_LINK_LIBRARIES} ${CURL_LIBS})
|
||||
if(TARGET "${_lib}")
|
||||
set(_libname "${_lib}")
|
||||
|
@ -1642,8 +1680,24 @@ if(NOT CURL_DISABLE_INSTALL)
|
|||
continue()
|
||||
endif()
|
||||
endif()
|
||||
if(_lib MATCHES ".*/.*" OR _lib MATCHES "^-")
|
||||
if(_lib MATCHES "^-")
|
||||
set(LIBCURL_LIBS "${LIBCURL_LIBS} ${_lib}")
|
||||
elseif(_lib MATCHES ".*/.*")
|
||||
# This gets a bit more complex, because we want to specify the
|
||||
# directory separately, and only once per directory
|
||||
string(REGEX REPLACE "^(.*)/[^/]*$" "\\1" _libdir "${_lib}")
|
||||
string(REGEX REPLACE "^.*/([^/.]*).*$" "\\1" _libname "${_lib}")
|
||||
if(_libname MATCHES "^lib")
|
||||
list(FIND _libcurl_libs_dirs "${_libdir}" _libdir_index)
|
||||
if(_libdir_index LESS 0)
|
||||
list(APPEND _libcurl_libs_dirs "${_libdir}")
|
||||
set(LIBCURL_LIBS "${LIBCURL_LIBS} -L${_libdir}")
|
||||
endif()
|
||||
string(REGEX REPLACE "^lib" "" _libname "${_libname}")
|
||||
set(LIBCURL_LIBS "${LIBCURL_LIBS} -l${_libname}")
|
||||
else()
|
||||
set(LIBCURL_LIBS "${LIBCURL_LIBS} ${_lib}")
|
||||
endif()
|
||||
else()
|
||||
set(LIBCURL_LIBS "${LIBCURL_LIBS} -l${_lib}")
|
||||
endif()
|
||||
|
|
|
@ -2938,7 +2938,8 @@ typedef enum {
|
|||
CURLINFO_XFER_ID = CURLINFO_OFF_T + 63,
|
||||
CURLINFO_CONN_ID = CURLINFO_OFF_T + 64,
|
||||
CURLINFO_QUEUE_TIME_T = CURLINFO_OFF_T + 65,
|
||||
CURLINFO_LASTONE = 65
|
||||
CURLINFO_USED_PROXY = CURLINFO_LONG + 66,
|
||||
CURLINFO_LASTONE = 66
|
||||
} CURLINFO;
|
||||
|
||||
/* CURLINFO_RESPONSE_CODE is the new name for the option previously known as
|
||||
|
|
|
@ -32,12 +32,12 @@
|
|||
|
||||
/* This is the version number of the libcurl package from which this header
|
||||
file origins: */
|
||||
#define LIBCURL_VERSION "8.6.0-DEV"
|
||||
#define LIBCURL_VERSION "8.7.0-DEV"
|
||||
|
||||
/* The numeric version number is also available "in parts" by using these
|
||||
defines: */
|
||||
#define LIBCURL_VERSION_MAJOR 8
|
||||
#define LIBCURL_VERSION_MINOR 6
|
||||
#define LIBCURL_VERSION_MINOR 7
|
||||
#define LIBCURL_VERSION_PATCH 0
|
||||
|
||||
/* This is the numeric version of the libcurl version number, meant for easier
|
||||
|
@ -59,7 +59,7 @@
|
|||
CURL_VERSION_BITS() macro since curl's own configure script greps for it
|
||||
and needs it to contain the full number.
|
||||
*/
|
||||
#define LIBCURL_VERSION_NUM 0x080600
|
||||
#define LIBCURL_VERSION_NUM 0x080700
|
||||
|
||||
/*
|
||||
* This is the date and time when the full source package was created. The
|
||||
|
|
|
@ -134,9 +134,11 @@ LIB_CFILES = \
|
|||
curl_range.c \
|
||||
curl_rtmp.c \
|
||||
curl_sasl.c \
|
||||
curl_sha512_256.c \
|
||||
curl_sspi.c \
|
||||
curl_threads.c \
|
||||
curl_trc.c \
|
||||
cw-out.c \
|
||||
dict.c \
|
||||
doh.c \
|
||||
dynbuf.c \
|
||||
|
@ -199,6 +201,7 @@ LIB_CFILES = \
|
|||
psl.c \
|
||||
rand.c \
|
||||
rename.c \
|
||||
request.c \
|
||||
rtsp.c \
|
||||
select.c \
|
||||
sendf.c \
|
||||
|
@ -277,10 +280,12 @@ LIB_HFILES = \
|
|||
curl_setup.h \
|
||||
curl_setup_once.h \
|
||||
curl_sha256.h \
|
||||
curl_sha512_256.h \
|
||||
curl_sspi.h \
|
||||
curl_threads.h \
|
||||
curl_trc.h \
|
||||
curlx.h \
|
||||
cw-out.h \
|
||||
dict.h \
|
||||
doh.h \
|
||||
dynbuf.h \
|
||||
|
@ -333,6 +338,7 @@ LIB_HFILES = \
|
|||
psl.h \
|
||||
rand.h \
|
||||
rename.h \
|
||||
request.h \
|
||||
rtsp.h \
|
||||
select.h \
|
||||
sendf.h \
|
||||
|
|
18
lib/altsvc.c
18
lib/altsvc.c
|
@ -209,7 +209,6 @@ static CURLcode altsvc_add(struct altsvcinfo *asi, char *line)
|
|||
static CURLcode altsvc_load(struct altsvcinfo *asi, const char *file)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
char *line = NULL;
|
||||
FILE *fp;
|
||||
|
||||
/* we need a private copy of the file name so that the altsvc cache file
|
||||
|
@ -221,11 +220,10 @@ static CURLcode altsvc_load(struct altsvcinfo *asi, const char *file)
|
|||
|
||||
fp = fopen(file, FOPEN_READTEXT);
|
||||
if(fp) {
|
||||
line = malloc(MAX_ALTSVC_LINE);
|
||||
if(!line)
|
||||
goto fail;
|
||||
while(Curl_get_line(line, MAX_ALTSVC_LINE, fp)) {
|
||||
char *lineptr = line;
|
||||
struct dynbuf buf;
|
||||
Curl_dyn_init(&buf, MAX_ALTSVC_LINE);
|
||||
while(Curl_get_line(&buf, fp)) {
|
||||
char *lineptr = Curl_dyn_ptr(&buf);
|
||||
while(*lineptr && ISBLANK(*lineptr))
|
||||
lineptr++;
|
||||
if(*lineptr == '#')
|
||||
|
@ -234,16 +232,10 @@ static CURLcode altsvc_load(struct altsvcinfo *asi, const char *file)
|
|||
|
||||
altsvc_add(asi, lineptr);
|
||||
}
|
||||
free(line); /* free the line buffer */
|
||||
Curl_dyn_free(&buf); /* free the line buffer */
|
||||
fclose(fp);
|
||||
}
|
||||
return result;
|
||||
|
||||
fail:
|
||||
Curl_safefree(asi->filename);
|
||||
free(line);
|
||||
fclose(fp);
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -122,6 +122,8 @@ struct thread_data {
|
|||
|
||||
#define CARES_TIMEOUT_PER_ATTEMPT 2000
|
||||
|
||||
static int ares_ver = 0;
|
||||
|
||||
/*
|
||||
* Curl_resolver_global_init() - the generic low-level asynchronous name
|
||||
* resolve API. Called from curl_global_init() to initialize global resolver
|
||||
|
@ -134,6 +136,7 @@ int Curl_resolver_global_init(void)
|
|||
return CURLE_FAILED_INIT;
|
||||
}
|
||||
#endif
|
||||
ares_version(&ares_ver);
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
|
@ -173,16 +176,8 @@ CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver)
|
|||
int status;
|
||||
struct ares_options options;
|
||||
int optmask = ARES_OPT_SOCK_STATE_CB;
|
||||
static int ares_ver = 0;
|
||||
options.sock_state_cb = sock_state_cb;
|
||||
options.sock_state_cb_data = easy;
|
||||
if(ares_ver == 0)
|
||||
ares_version(&ares_ver);
|
||||
|
||||
if(ares_ver < 0x011400) { /* c-ares included similar change since 1.20.0 */
|
||||
options.timeout = CARES_TIMEOUT_PER_ATTEMPT;
|
||||
optmask |= ARES_OPT_TIMEOUTMS;
|
||||
}
|
||||
|
||||
/*
|
||||
if c ares < 1.20.0: curl set timeout to CARES_TIMEOUT_PER_ATTEMPT (2s)
|
||||
|
@ -193,6 +188,11 @@ CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver)
|
|||
if c-ares >= 1.24.0, user can set the timeout via /etc/resolv.conf to
|
||||
overwrite c-ares' timeout.
|
||||
*/
|
||||
DEBUGASSERT(ares_ver);
|
||||
if(ares_ver < 0x011400) {
|
||||
options.timeout = CARES_TIMEOUT_PER_ATTEMPT;
|
||||
optmask |= ARES_OPT_TIMEOUTMS;
|
||||
}
|
||||
|
||||
status = ares_init_options((ares_channel*)resolver, &options, optmask);
|
||||
if(status != ARES_SUCCESS) {
|
||||
|
@ -850,7 +850,7 @@ CURLcode Curl_set_dns_servers(struct Curl_easy *data,
|
|||
/* If server is NULL or empty, this would purge all DNS servers
|
||||
* from ares library, which will cause any and all queries to fail.
|
||||
* So, just return OK if none are configured and don't actually make
|
||||
* any changes to c-ares. This lets c-ares use it's defaults, which
|
||||
* any changes to c-ares. This lets c-ares use its defaults, which
|
||||
* it gets from the OS (for instance from /etc/resolv.conf on Linux).
|
||||
*/
|
||||
if(!(servers && servers[0]))
|
||||
|
|
|
@ -581,7 +581,7 @@ static void destroy_async_data(struct Curl_async *async)
|
|||
* before the FD is invalidated to avoid EBADF on EPOLL_CTL_DEL
|
||||
*/
|
||||
Curl_multi_closed(data, sock_rd);
|
||||
sclose(sock_rd);
|
||||
wakeup_close(sock_rd);
|
||||
#endif
|
||||
}
|
||||
async->tdata = NULL;
|
||||
|
|
23
lib/bufq.c
23
lib/bufq.c
|
@ -396,7 +396,7 @@ ssize_t Curl_bufq_write(struct bufq *q,
|
|||
while(len) {
|
||||
tail = get_non_full_tail(q);
|
||||
if(!tail) {
|
||||
if(q->chunk_count < q->max_chunks) {
|
||||
if((q->chunk_count < q->max_chunks) || (q->opts & BUFQ_OPT_SOFT_LIMIT)) {
|
||||
*err = CURLE_OUT_OF_MEMORY;
|
||||
return -1;
|
||||
}
|
||||
|
@ -417,6 +417,17 @@ ssize_t Curl_bufq_write(struct bufq *q,
|
|||
return nwritten;
|
||||
}
|
||||
|
||||
CURLcode Curl_bufq_cwrite(struct bufq *q,
|
||||
const char *buf, size_t len,
|
||||
size_t *pnwritten)
|
||||
{
|
||||
ssize_t n;
|
||||
CURLcode result;
|
||||
n = Curl_bufq_write(q, (const unsigned char *)buf, len, &result);
|
||||
*pnwritten = (n < 0)? 0 : (size_t)n;
|
||||
return result;
|
||||
}
|
||||
|
||||
ssize_t Curl_bufq_read(struct bufq *q, unsigned char *buf, size_t len,
|
||||
CURLcode *err)
|
||||
{
|
||||
|
@ -440,6 +451,16 @@ ssize_t Curl_bufq_read(struct bufq *q, unsigned char *buf, size_t len,
|
|||
return nread;
|
||||
}
|
||||
|
||||
CURLcode Curl_bufq_cread(struct bufq *q, char *buf, size_t len,
|
||||
size_t *pnread)
|
||||
{
|
||||
ssize_t n;
|
||||
CURLcode result;
|
||||
n = Curl_bufq_read(q, (unsigned char *)buf, len, &result);
|
||||
*pnread = (n < 0)? 0 : (size_t)n;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Curl_bufq_peek(struct bufq *q,
|
||||
const unsigned char **pbuf, size_t *plen)
|
||||
{
|
||||
|
|
|
@ -178,6 +178,10 @@ ssize_t Curl_bufq_write(struct bufq *q,
|
|||
const unsigned char *buf, size_t len,
|
||||
CURLcode *err);
|
||||
|
||||
CURLcode Curl_bufq_cwrite(struct bufq *q,
|
||||
const char *buf, size_t len,
|
||||
size_t *pnwritten);
|
||||
|
||||
/**
|
||||
* Read buf from the start of the buffer queue. The buf is copied
|
||||
* and the amount of copied bytes is returned.
|
||||
|
@ -187,6 +191,9 @@ ssize_t Curl_bufq_write(struct bufq *q,
|
|||
ssize_t Curl_bufq_read(struct bufq *q, unsigned char *buf, size_t len,
|
||||
CURLcode *err);
|
||||
|
||||
CURLcode Curl_bufq_cread(struct bufq *q, char *buf, size_t len,
|
||||
size_t *pnread);
|
||||
|
||||
/**
|
||||
* Peek at the head chunk in the buffer queue. Returns a pointer to
|
||||
* the chunk buf (at the current offset) and its length. Does not
|
||||
|
|
320
lib/c-hyper.c
320
lib/c-hyper.c
|
@ -53,7 +53,9 @@
|
|||
|
||||
#include <hyper.h>
|
||||
#include "urldata.h"
|
||||
#include "cfilters.h"
|
||||
#include "sendf.h"
|
||||
#include "headers.h"
|
||||
#include "transfer.h"
|
||||
#include "multiif.h"
|
||||
#include "progress.h"
|
||||
|
@ -65,6 +67,9 @@
|
|||
#include "curl_memory.h"
|
||||
#include "memdebug.h"
|
||||
|
||||
|
||||
static CURLcode cr_hyper_add(struct Curl_easy *data);
|
||||
|
||||
typedef enum {
|
||||
USERDATA_NOT_SET = 0, /* for tasks with no userdata set; must be zero */
|
||||
USERDATA_RESP_BODY
|
||||
|
@ -73,7 +78,8 @@ typedef enum {
|
|||
size_t Curl_hyper_recv(void *userp, hyper_context *ctx,
|
||||
uint8_t *buf, size_t buflen)
|
||||
{
|
||||
struct Curl_easy *data = userp;
|
||||
struct hyp_io_ctx *io_ctx = userp;
|
||||
struct Curl_easy *data = io_ctx->data;
|
||||
struct connectdata *conn = data->conn;
|
||||
CURLcode result;
|
||||
ssize_t nread;
|
||||
|
@ -81,7 +87,8 @@ size_t Curl_hyper_recv(void *userp, hyper_context *ctx,
|
|||
(void)ctx;
|
||||
|
||||
DEBUGF(infof(data, "Curl_hyper_recv(%zu)", buflen));
|
||||
result = Curl_read(data, conn->sockfd, (char *)buf, buflen, &nread);
|
||||
result = Curl_conn_recv(data, io_ctx->sockindex,
|
||||
(char *)buf, buflen, &nread);
|
||||
if(result == CURLE_AGAIN) {
|
||||
/* would block, register interest */
|
||||
DEBUGF(infof(data, "Curl_hyper_recv(%zu) -> EAGAIN", buflen));
|
||||
|
@ -105,15 +112,14 @@ size_t Curl_hyper_recv(void *userp, hyper_context *ctx,
|
|||
size_t Curl_hyper_send(void *userp, hyper_context *ctx,
|
||||
const uint8_t *buf, size_t buflen)
|
||||
{
|
||||
struct Curl_easy *data = userp;
|
||||
struct connectdata *conn = data->conn;
|
||||
struct hyp_io_ctx *io_ctx = userp;
|
||||
struct Curl_easy *data = io_ctx->data;
|
||||
CURLcode result;
|
||||
ssize_t nwrote;
|
||||
size_t nwrote;
|
||||
|
||||
DEBUGF(infof(data, "Curl_hyper_send(%zu)", buflen));
|
||||
result = Curl_write(data, conn->sockfd, (void *)buf, buflen, &nwrote);
|
||||
if(!result && !nwrote)
|
||||
result = CURLE_AGAIN;
|
||||
result = Curl_conn_send(data, io_ctx->sockindex,
|
||||
(void *)buf, buflen, &nwrote);
|
||||
if(result == CURLE_AGAIN) {
|
||||
DEBUGF(infof(data, "Curl_hyper_send(%zu) -> EAGAIN", buflen));
|
||||
/* would block, register interest */
|
||||
|
@ -152,9 +158,6 @@ static int hyper_each_header(void *userdata,
|
|||
return HYPER_ITER_BREAK;
|
||||
}
|
||||
|
||||
if(!data->req.bytecount)
|
||||
Curl_pgrsTime(data, TIMER_STARTTRANSFER);
|
||||
|
||||
Curl_dyn_reset(&data->state.headerb);
|
||||
if(name_len) {
|
||||
if(Curl_dyn_addf(&data->state.headerb, "%.*s: %.*s\r\n",
|
||||
|
@ -168,7 +171,7 @@ static int hyper_each_header(void *userdata,
|
|||
len = Curl_dyn_len(&data->state.headerb);
|
||||
headp = Curl_dyn_ptr(&data->state.headerb);
|
||||
|
||||
result = Curl_http_header(data, data->conn, headp);
|
||||
result = Curl_http_header(data, data->conn, headp, len);
|
||||
if(result) {
|
||||
data->state.hresult = result;
|
||||
return HYPER_ITER_BREAK;
|
||||
|
@ -204,7 +207,6 @@ static int hyper_body_chunk(void *userdata, const hyper_buf *chunk)
|
|||
CURLcode result = CURLE_OK;
|
||||
|
||||
if(0 == k->bodywrites) {
|
||||
bool done = FALSE;
|
||||
#if defined(USE_NTLM)
|
||||
struct connectdata *conn = data->conn;
|
||||
if(conn->bits.close &&
|
||||
|
@ -217,27 +219,26 @@ static int hyper_body_chunk(void *userdata, const hyper_buf *chunk)
|
|||
Curl_safefree(data->req.newurl);
|
||||
}
|
||||
#endif
|
||||
if(data->state.expect100header) {
|
||||
Curl_expire_done(data, EXPIRE_100_TIMEOUT);
|
||||
if(Curl_http_exp100_is_selected(data)) {
|
||||
if(data->req.httpcode < 400) {
|
||||
k->exp100 = EXP100_SEND_DATA;
|
||||
if(data->hyp.exp100_waker) {
|
||||
hyper_waker_wake(data->hyp.exp100_waker);
|
||||
data->hyp.exp100_waker = NULL;
|
||||
Curl_http_exp100_got100(data);
|
||||
if(data->hyp.send_body_waker) {
|
||||
hyper_waker_wake(data->hyp.send_body_waker);
|
||||
data->hyp.send_body_waker = NULL;
|
||||
}
|
||||
}
|
||||
else { /* >= 4xx */
|
||||
k->exp100 = EXP100_FAILED;
|
||||
Curl_req_abort_sending(data);
|
||||
}
|
||||
}
|
||||
if(data->state.hconnect && (data->req.httpcode/100 != 2) &&
|
||||
data->state.authproxy.done) {
|
||||
done = TRUE;
|
||||
data->req.done = TRUE;
|
||||
result = CURLE_OK;
|
||||
}
|
||||
else
|
||||
result = Curl_http_firstwrite(data, data->conn, &done);
|
||||
if(result || done) {
|
||||
result = Curl_http_firstwrite(data);
|
||||
if(result || data->req.done) {
|
||||
infof(data, "Return early from hyper_body_chunk");
|
||||
data->state.hresult = result;
|
||||
return HYPER_ITER_BREAK;
|
||||
|
@ -273,14 +274,13 @@ static CURLcode status_line(struct Curl_easy *data,
|
|||
/* We need to set 'httpcodeq' for functions that check the response code in
|
||||
a single place. */
|
||||
data->req.httpcode = http_status;
|
||||
|
||||
data->req.httpversion = http_version == HYPER_HTTP_VERSION_1_1? 11 :
|
||||
(http_version == HYPER_HTTP_VERSION_2 ? 20 : 10);
|
||||
if(data->state.hconnect)
|
||||
/* CONNECT */
|
||||
data->info.httpproxycode = http_status;
|
||||
else {
|
||||
conn->httpversion =
|
||||
http_version == HYPER_HTTP_VERSION_1_1 ? 11 :
|
||||
(http_version == HYPER_HTTP_VERSION_2 ? 20 : 10);
|
||||
conn->httpversion = (unsigned char)data->req.httpversion;
|
||||
if(http_version == HYPER_HTTP_VERSION_1_0)
|
||||
data->state.httpwant = CURL_HTTP_VERSION_1_0;
|
||||
|
||||
|
@ -335,7 +335,6 @@ static CURLcode empty_header(struct Curl_easy *data)
|
|||
CURLcode Curl_hyper_stream(struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
int *didwhat,
|
||||
bool *done,
|
||||
int select_res)
|
||||
{
|
||||
hyper_response *resp = NULL;
|
||||
|
@ -352,20 +351,9 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data,
|
|||
struct SingleRequest *k = &data->req;
|
||||
(void)conn;
|
||||
|
||||
if(k->exp100 > EXP100_SEND_DATA) {
|
||||
struct curltime now = Curl_now();
|
||||
timediff_t ms = Curl_timediff(now, k->start100);
|
||||
if(ms >= data->set.expect_100_timeout) {
|
||||
/* we've waited long enough, continue anyway */
|
||||
k->exp100 = EXP100_SEND_DATA;
|
||||
k->keepon |= KEEP_SEND;
|
||||
Curl_expire_done(data, EXPIRE_100_TIMEOUT);
|
||||
infof(data, "Done waiting for 100-continue");
|
||||
if(data->hyp.exp100_waker) {
|
||||
hyper_waker_wake(data->hyp.exp100_waker);
|
||||
data->hyp.exp100_waker = NULL;
|
||||
}
|
||||
}
|
||||
if(data->hyp.send_body_waker) {
|
||||
hyper_waker_wake(data->hyp.send_body_waker);
|
||||
data->hyp.send_body_waker = NULL;
|
||||
}
|
||||
|
||||
if(select_res & CURL_CSELECT_IN) {
|
||||
|
@ -379,7 +367,6 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data,
|
|||
h->write_waker = NULL;
|
||||
}
|
||||
|
||||
*done = FALSE;
|
||||
do {
|
||||
hyper_task_return_type t;
|
||||
task = hyper_executor_poll(h->exec);
|
||||
|
@ -422,7 +409,7 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data,
|
|||
break;
|
||||
}
|
||||
}
|
||||
*done = TRUE;
|
||||
data->req.done = TRUE;
|
||||
hyper_error_free(hypererr);
|
||||
break;
|
||||
}
|
||||
|
@ -431,12 +418,11 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data,
|
|||
hyper_task_free(task);
|
||||
if((userdata_t)userdata == USERDATA_RESP_BODY) {
|
||||
/* end of transfer */
|
||||
*done = TRUE;
|
||||
data->req.done = TRUE;
|
||||
infof(data, "hyperstream is done");
|
||||
if(!k->bodywrites) {
|
||||
/* hyper doesn't always call the body write callback */
|
||||
bool stilldone;
|
||||
result = Curl_http_firstwrite(data, data->conn, &stilldone);
|
||||
result = Curl_http_firstwrite(data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -462,11 +448,11 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data,
|
|||
reasonp = hyper_response_reason_phrase(resp);
|
||||
reason_len = hyper_response_reason_phrase_len(resp);
|
||||
|
||||
if(http_status == 417 && data->state.expect100header) {
|
||||
if(http_status == 417 && Curl_http_exp100_is_selected(data)) {
|
||||
infof(data, "Got 417 while waiting for a 100");
|
||||
data->state.disableexpect = TRUE;
|
||||
data->req.newurl = strdup(data->state.url);
|
||||
Curl_done_sending(data, k);
|
||||
Curl_req_abort_sending(data);
|
||||
}
|
||||
|
||||
result = status_line(data, conn,
|
||||
|
@ -654,115 +640,66 @@ static CURLcode request_target(struct Curl_easy *data,
|
|||
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.exp100 > EXP100_SEND_DATA) {
|
||||
if(data->req.exp100 == EXP100_FAILED)
|
||||
return HYPER_POLL_ERROR;
|
||||
|
||||
/* still waiting confirmation */
|
||||
if(data->hyp.exp100_waker)
|
||||
hyper_waker_free(data->hyp.exp100_waker);
|
||||
data->hyp.exp100_waker = hyper_context_waker(ctx);
|
||||
return HYPER_POLL_PENDING;
|
||||
}
|
||||
if(data->req.upload_done)
|
||||
*chunk = NULL; /* nothing more to deliver */
|
||||
else {
|
||||
/* send everything off in a single go */
|
||||
hyper_buf *copy = hyper_buf_copy(data->set.postfields,
|
||||
(size_t)data->req.p.http->postsize);
|
||||
if(copy)
|
||||
*chunk = copy;
|
||||
else {
|
||||
data->state.hresult = CURLE_OUT_OF_MEMORY;
|
||||
return HYPER_POLL_ERROR;
|
||||
}
|
||||
/* increasing the writebytecount here is a little premature but we
|
||||
don't know exactly when the body is sent */
|
||||
data->req.writebytecount += (size_t)data->req.p.http->postsize;
|
||||
Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
|
||||
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;
|
||||
struct connectdata *conn = (struct connectdata *)data->conn;
|
||||
CURLcode result;
|
||||
char *xfer_ulbuf;
|
||||
size_t xfer_ulblen;
|
||||
bool eos;
|
||||
int rc = HYPER_POLL_ERROR;
|
||||
(void)ctx;
|
||||
|
||||
if(data->req.exp100 > EXP100_SEND_DATA) {
|
||||
if(data->req.exp100 == EXP100_FAILED)
|
||||
return HYPER_POLL_ERROR;
|
||||
result = Curl_multi_xfer_ulbuf_borrow(data, &xfer_ulbuf, &xfer_ulblen);
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
/* still waiting confirmation */
|
||||
if(data->hyp.exp100_waker)
|
||||
hyper_waker_free(data->hyp.exp100_waker);
|
||||
data->hyp.exp100_waker = hyper_context_waker(ctx);
|
||||
return HYPER_POLL_PENDING;
|
||||
}
|
||||
result = Curl_client_read(data, xfer_ulbuf, xfer_ulblen, &fillcount, &eos);
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
if(data->req.upload_chunky && conn->bits.authneg) {
|
||||
fillcount = 0;
|
||||
data->req.upload_chunky = FALSE;
|
||||
result = CURLE_OK;
|
||||
}
|
||||
else {
|
||||
result = Curl_fillreadbuffer(data, data->set.upload_buffer_size,
|
||||
&fillcount);
|
||||
}
|
||||
if(result) {
|
||||
data->state.hresult = result;
|
||||
return HYPER_POLL_ERROR;
|
||||
}
|
||||
if(!fillcount) {
|
||||
if((data->req.keepon & KEEP_SEND_PAUSE) != KEEP_SEND_PAUSE)
|
||||
/* done! */
|
||||
*chunk = NULL;
|
||||
else {
|
||||
/* paused, save a waker */
|
||||
if(data->hyp.send_body_waker)
|
||||
hyper_waker_free(data->hyp.send_body_waker);
|
||||
data->hyp.send_body_waker = hyper_context_waker(ctx);
|
||||
return HYPER_POLL_PENDING;
|
||||
}
|
||||
}
|
||||
else {
|
||||
hyper_buf *copy = hyper_buf_copy((uint8_t *)data->state.ulbuf, fillcount);
|
||||
if(fillcount) {
|
||||
hyper_buf *copy = hyper_buf_copy((uint8_t *)xfer_ulbuf, fillcount);
|
||||
if(copy)
|
||||
*chunk = copy;
|
||||
else {
|
||||
data->state.hresult = CURLE_OUT_OF_MEMORY;
|
||||
return HYPER_POLL_ERROR;
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
/* increasing the writebytecount here is a little premature but we
|
||||
don't know exactly when the body is sent */
|
||||
data->req.writebytecount += fillcount;
|
||||
Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
|
||||
rc = HYPER_POLL_READY;
|
||||
}
|
||||
return HYPER_POLL_READY;
|
||||
else if(eos) {
|
||||
*chunk = NULL;
|
||||
rc = HYPER_POLL_READY;
|
||||
}
|
||||
else {
|
||||
/* paused, save a waker */
|
||||
if(data->hyp.send_body_waker)
|
||||
hyper_waker_free(data->hyp.send_body_waker);
|
||||
data->hyp.send_body_waker = hyper_context_waker(ctx);
|
||||
rc = HYPER_POLL_PENDING;
|
||||
}
|
||||
|
||||
out:
|
||||
Curl_multi_xfer_ulbuf_release(data, xfer_ulbuf);
|
||||
data->state.hresult = result;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* bodysend() sets up headers in the outgoing request for an HTTP transfer that
|
||||
* sends a body
|
||||
* finalize_request() sets up last headers and optional body settings
|
||||
*/
|
||||
|
||||
static CURLcode bodysend(struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
hyper_headers *headers,
|
||||
hyper_request *hyperreq,
|
||||
Curl_HttpReq httpreq)
|
||||
static CURLcode finalize_request(struct Curl_easy *data,
|
||||
hyper_headers *headers,
|
||||
hyper_request *hyperreq,
|
||||
Curl_HttpReq httpreq)
|
||||
{
|
||||
struct HTTP *http = data->req.p.http;
|
||||
CURLcode result = CURLE_OK;
|
||||
struct dynbuf req;
|
||||
if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD))
|
||||
|
@ -770,34 +707,31 @@ static CURLcode bodysend(struct Curl_easy *data,
|
|||
else {
|
||||
hyper_body *body;
|
||||
Curl_dyn_init(&req, DYN_HTTP_REQUEST);
|
||||
result = Curl_http_bodysend(data, conn, &req, httpreq);
|
||||
result = Curl_http_req_complete(data, &req, httpreq);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
if(!result)
|
||||
/* if the "complete" above did produce more than the closing line,
|
||||
parse the added headers */
|
||||
if(Curl_dyn_len(&req) != 2 || strcmp(Curl_dyn_ptr(&req), "\r\n")) {
|
||||
result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&req));
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
|
||||
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) {
|
||||
hyper_body_free(body);
|
||||
return result;
|
||||
}
|
||||
/* init the "upload from here" pointer */
|
||||
data->req.upload_fromhere = data->state.ulbuf;
|
||||
hyper_body_set_data_func(body, uploadstreamed);
|
||||
}
|
||||
hyper_body_set_data_func(body, uploadstreamed);
|
||||
|
||||
if(HYPERE_OK != hyper_request_set_body(hyperreq, body)) {
|
||||
/* fail */
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
http->sending = HTTPSEND_BODY;
|
||||
return result;
|
||||
|
||||
return cr_hyper_add(data);
|
||||
}
|
||||
|
||||
static CURLcode cookies(struct Curl_easy *data,
|
||||
|
@ -885,7 +819,16 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
|
|||
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;
|
||||
Curl_client_cleanup(data);
|
||||
result = Curl_client_start(data);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
/* Add collecting of headers written to client. For a new connection,
|
||||
* we might have done that already, but reuse
|
||||
* or multiplex needs it here as well. */
|
||||
result = Curl_headers_init(data);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
infof(data, "Time for the Hyper dance");
|
||||
memset(h, 0, sizeof(struct hyptransfer));
|
||||
|
@ -913,9 +856,9 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
|
|||
return result;
|
||||
}
|
||||
|
||||
result = Curl_http_resume(data, conn, httpreq);
|
||||
result = Curl_http_req_set_reader(data, httpreq, &te);
|
||||
if(result)
|
||||
return result;
|
||||
goto error;
|
||||
|
||||
result = Curl_http_range(data, httpreq);
|
||||
if(result)
|
||||
|
@ -932,7 +875,9 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
|
|||
goto error;
|
||||
}
|
||||
/* tell Hyper how to read/write network data */
|
||||
hyper_io_set_userdata(io, data);
|
||||
h->io_ctx.data = data;
|
||||
h->io_ctx.sockindex = FIRSTSOCKET;
|
||||
hyper_io_set_userdata(io, &h->io_ctx);
|
||||
hyper_io_set_read(io, Curl_hyper_recv);
|
||||
hyper_io_set_write(io, Curl_hyper_send);
|
||||
|
||||
|
@ -1005,11 +950,6 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
|
|||
goto error;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(!data->state.disableexpect) {
|
||||
data->state.expect100header = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if(hyper_request_set_method(req, (uint8_t *)method, strlen(method))) {
|
||||
failf(data, "error setting method");
|
||||
|
@ -1034,10 +974,6 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
|
|||
goto error;
|
||||
}
|
||||
|
||||
result = Curl_http_body(data, conn, httpreq, &te);
|
||||
if(result)
|
||||
goto error;
|
||||
|
||||
if(data->state.aptr.host) {
|
||||
result = Curl_hyper_header(data, headers, data->state.aptr.host);
|
||||
if(result)
|
||||
|
@ -1160,13 +1096,13 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
|
|||
if(result)
|
||||
goto error;
|
||||
|
||||
result = bodysend(data, conn, headers, req, httpreq);
|
||||
result = finalize_request(data, headers, req, httpreq);
|
||||
if(result)
|
||||
goto error;
|
||||
|
||||
Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"\r\n", 2);
|
||||
|
||||
if(data->req.upload_chunky && conn->bits.authneg) {
|
||||
if(data->req.upload_chunky && data->req.authneg) {
|
||||
data->req.upload_chunky = TRUE;
|
||||
}
|
||||
else {
|
||||
|
@ -1193,13 +1129,10 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
|
|||
if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) {
|
||||
/* HTTP GET/HEAD download */
|
||||
Curl_pgrsSetUploadSize(data, 0); /* nothing */
|
||||
Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, -1);
|
||||
}
|
||||
|
||||
Curl_xfer_setup(data, FIRSTSOCKET, -1, TRUE, FIRSTSOCKET);
|
||||
conn->datastream = Curl_hyper_stream;
|
||||
if(data->state.expect100header)
|
||||
/* Timeout count starts now since with Hyper we don't know exactly when
|
||||
the full request has been sent. */
|
||||
data->req.start100 = Curl_now();
|
||||
|
||||
/* clear userpwd and proxyuserpwd to avoid reusing old credentials
|
||||
* from reused connections */
|
||||
|
@ -1241,10 +1174,51 @@ void Curl_hyper_done(struct Curl_easy *data)
|
|||
hyper_waker_free(h->write_waker);
|
||||
h->write_waker = NULL;
|
||||
}
|
||||
if(h->exp100_waker) {
|
||||
hyper_waker_free(h->exp100_waker);
|
||||
h->exp100_waker = NULL;
|
||||
if(h->send_body_waker) {
|
||||
hyper_waker_free(h->send_body_waker);
|
||||
h->send_body_waker = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static CURLcode cr_hyper_unpause(struct Curl_easy *data,
|
||||
struct Curl_creader *reader)
|
||||
{
|
||||
(void)reader;
|
||||
if(data->hyp.send_body_waker) {
|
||||
hyper_waker_wake(data->hyp.send_body_waker);
|
||||
data->hyp.send_body_waker = NULL;
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/* Hyper client reader, handling unpausing */
|
||||
static const struct Curl_crtype cr_hyper_protocol = {
|
||||
"cr-hyper",
|
||||
Curl_creader_def_init,
|
||||
Curl_creader_def_read,
|
||||
Curl_creader_def_close,
|
||||
Curl_creader_def_needs_rewind,
|
||||
Curl_creader_def_total_length,
|
||||
Curl_creader_def_resume_from,
|
||||
Curl_creader_def_rewind,
|
||||
cr_hyper_unpause,
|
||||
Curl_creader_def_done,
|
||||
sizeof(struct Curl_creader)
|
||||
};
|
||||
|
||||
static CURLcode cr_hyper_add(struct Curl_easy *data)
|
||||
{
|
||||
struct Curl_creader *reader = NULL;
|
||||
CURLcode result;
|
||||
|
||||
result = Curl_creader_create(&reader, data, &cr_hyper_protocol,
|
||||
CURL_CR_PROTOCOL);
|
||||
if(!result)
|
||||
result = Curl_creader_add(data, reader);
|
||||
|
||||
if(result && reader)
|
||||
Curl_creader_free(data, reader);
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif /* !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER) */
|
||||
|
|
|
@ -29,13 +29,18 @@
|
|||
|
||||
#include <hyper.h>
|
||||
|
||||
struct hyp_io_ctx {
|
||||
struct Curl_easy *data;
|
||||
int sockindex;
|
||||
};
|
||||
|
||||
/* per-transfer data for the Hyper backend */
|
||||
struct hyptransfer {
|
||||
hyper_waker *write_waker;
|
||||
hyper_waker *read_waker;
|
||||
const hyper_executor *exec;
|
||||
hyper_waker *exp100_waker;
|
||||
hyper_waker *send_body_waker;
|
||||
struct hyp_io_ctx io_ctx;
|
||||
};
|
||||
|
||||
size_t Curl_hyper_recv(void *userp, hyper_context *ctx,
|
||||
|
@ -45,7 +50,6 @@ size_t Curl_hyper_send(void *userp, hyper_context *ctx,
|
|||
CURLcode Curl_hyper_stream(struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
int *didwhat,
|
||||
bool *done,
|
||||
int select_res);
|
||||
|
||||
CURLcode Curl_hyper_header(struct Curl_easy *data, hyper_headers *headers,
|
||||
|
|
|
@ -114,18 +114,12 @@ static CURLcode tunnel_init(struct Curl_cfilter *cf,
|
|||
struct h1_tunnel_state **pts)
|
||||
{
|
||||
struct h1_tunnel_state *ts;
|
||||
CURLcode result;
|
||||
|
||||
if(cf->conn->handler->flags & PROTOPT_NOTCPPROXY) {
|
||||
failf(data, "%s cannot be done over CONNECT", cf->conn->handler->scheme);
|
||||
return CURLE_UNSUPPORTED_PROTOCOL;
|
||||
}
|
||||
|
||||
/* we might need the upload buffer for streaming a partial request */
|
||||
result = Curl_get_upload_buffer(data);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
ts = calloc(1, sizeof(*ts));
|
||||
if(!ts)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
@ -212,6 +206,11 @@ static void tunnel_free(struct Curl_cfilter *cf,
|
|||
}
|
||||
}
|
||||
|
||||
static bool tunnel_want_send(struct h1_tunnel_state *ts)
|
||||
{
|
||||
return (ts->tunnel_state == H1_TUNNEL_CONNECT);
|
||||
}
|
||||
|
||||
#ifndef USE_HYPER
|
||||
static CURLcode start_CONNECT(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
|
@ -238,6 +237,8 @@ static CURLcode start_CONNECT(struct Curl_cfilter *cf,
|
|||
http_minor = (cf->conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? 0 : 1;
|
||||
|
||||
result = Curl_h1_req_write_head(req, http_minor, &ts->request_data);
|
||||
if(!result)
|
||||
result = Curl_creader_set_null(data);
|
||||
|
||||
out:
|
||||
if(result)
|
||||
|
@ -366,7 +367,6 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
|
|||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
struct SingleRequest *k = &data->req;
|
||||
curl_socket_t tunnelsocket = Curl_conn_cf_get_socket(cf, data);
|
||||
char *linep;
|
||||
size_t line_len;
|
||||
int error, writetype;
|
||||
|
@ -386,7 +386,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
|
|||
|
||||
/* Read one byte at a time to avoid a race condition. Wait at most one
|
||||
second before looping to ensure continuous pgrsUpdates. */
|
||||
result = Curl_read(data, tunnelsocket, &byte, 1, &nread);
|
||||
result = Curl_conn_recv(data, cf->sockindex, &byte, 1, &nread);
|
||||
if(result == CURLE_AGAIN)
|
||||
/* socket buffer drained, return */
|
||||
return CURLE_OK;
|
||||
|
@ -593,7 +593,9 @@ static CURLcode start_CONNECT(struct Curl_cfilter *cf,
|
|||
goto error;
|
||||
}
|
||||
/* tell Hyper how to read/write network data */
|
||||
hyper_io_set_userdata(io, data);
|
||||
h->io_ctx.data = data;
|
||||
h->io_ctx.sockindex = cf->sockindex;
|
||||
hyper_io_set_userdata(io, &h->io_ctx);
|
||||
hyper_io_set_read(io, Curl_hyper_recv);
|
||||
hyper_io_set_write(io, Curl_hyper_send);
|
||||
conn->sockfd = tunnelsocket;
|
||||
|
@ -749,6 +751,10 @@ static CURLcode start_CONNECT(struct Curl_cfilter *cf,
|
|||
if(result)
|
||||
goto error;
|
||||
|
||||
result = Curl_creader_set_null(data);
|
||||
if(result)
|
||||
goto error;
|
||||
|
||||
sendtask = hyper_clientconn_send(client, req);
|
||||
if(!sendtask) {
|
||||
failf(data, "hyper_clientconn_send");
|
||||
|
@ -832,9 +838,9 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
|
|||
int didwhat;
|
||||
|
||||
(void)ts;
|
||||
*done = FALSE;
|
||||
result = Curl_hyper_stream(data, cf->conn, &didwhat, done,
|
||||
result = Curl_hyper_stream(data, cf->conn, &didwhat,
|
||||
CURL_CSELECT_IN | CURL_CSELECT_OUT);
|
||||
*done = data->req.done;
|
||||
if(result || !*done)
|
||||
return result;
|
||||
if(h->exec) {
|
||||
|
@ -918,6 +924,7 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf,
|
|||
* If the other side indicated a connection close, or if someone
|
||||
* else told us to close this connection, do so now.
|
||||
*/
|
||||
Curl_req_soft_reset(&data->req, data);
|
||||
if(ts->close_connection || conn->bits.close) {
|
||||
/* Close this filter and the sub-chain, re-connect the
|
||||
* sub-chain and continue. Closing this filter will
|
||||
|
@ -1003,11 +1010,9 @@ out:
|
|||
*done = (result == CURLE_OK) && tunnel_is_established(cf->ctx);
|
||||
if(*done) {
|
||||
cf->connected = TRUE;
|
||||
/* Restore `data->req` fields that may habe been touched */
|
||||
data->req.header = TRUE; /* assume header */
|
||||
data->req.bytecount = 0;
|
||||
data->req.ignorebody = FALSE;
|
||||
Curl_client_cleanup(data);
|
||||
/* The real request will follow the CONNECT, reset request partially */
|
||||
Curl_req_soft_reset(&data->req, data);
|
||||
Curl_client_reset(data);
|
||||
Curl_pgrsSetUploadCounter(data, 0);
|
||||
Curl_pgrsSetDownloadCounter(data, 0);
|
||||
|
||||
|
@ -1031,7 +1036,7 @@ static void cf_h1_proxy_adjust_pollset(struct Curl_cfilter *cf,
|
|||
wait for the socket to become readable to be able to get the
|
||||
response headers or if we're still sending the request, wait
|
||||
for write. */
|
||||
if(ts->CONNECT.sending == HTTPSEND_REQUEST)
|
||||
if(tunnel_want_send(ts))
|
||||
Curl_pollset_set_out_only(data, ps, sock);
|
||||
else
|
||||
Curl_pollset_set_in_only(data, ps, sock);
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include "http2.h"
|
||||
#include "http_proxy.h"
|
||||
#include "multiif.h"
|
||||
#include "sendf.h"
|
||||
#include "cf-h2-proxy.h"
|
||||
|
||||
/* The last 3 #include files should be in this order */
|
||||
|
@ -954,6 +955,9 @@ static CURLcode submit_CONNECT(struct Curl_cfilter *cf,
|
|||
struct httpreq *req = NULL;
|
||||
|
||||
result = Curl_http_proxy_create_CONNECT(&req, cf, data, 2);
|
||||
if(result)
|
||||
goto out;
|
||||
result = Curl_creader_set_null(data);
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
|
@ -1125,7 +1129,12 @@ static CURLcode cf_h2_proxy_connect(struct Curl_cfilter *cf,
|
|||
|
||||
out:
|
||||
*done = (result == CURLE_OK) && (ts->state == H2_TUNNEL_ESTABLISHED);
|
||||
cf->connected = *done;
|
||||
if(*done) {
|
||||
cf->connected = TRUE;
|
||||
/* The real request will follow the CONNECT, reset request partially */
|
||||
Curl_req_soft_reset(&data->req, data);
|
||||
Curl_client_reset(data);
|
||||
}
|
||||
CF_DATA_RESTORE(cf, save);
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -86,14 +86,14 @@ static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter*cf,
|
|||
if(data->set.str[STRING_HAPROXY_CLIENT_IP])
|
||||
client_ip = data->set.str[STRING_HAPROXY_CLIENT_IP];
|
||||
else
|
||||
client_ip = data->info.conn_local_ip;
|
||||
client_ip = data->info.primary.local_ip;
|
||||
|
||||
result = Curl_dyn_addf(&ctx->data_out, "PROXY %s %s %s %i %i\r\n",
|
||||
tcp_version,
|
||||
client_ip,
|
||||
data->info.conn_primary_ip,
|
||||
data->info.conn_local_port,
|
||||
data->info.conn_primary_port);
|
||||
data->info.primary.remote_ip,
|
||||
data->info.primary.local_port,
|
||||
data->info.primary.remote_port);
|
||||
|
||||
#ifdef USE_UNIX_SOCKETS
|
||||
}
|
||||
|
@ -129,12 +129,17 @@ static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf,
|
|||
case HAPROXY_SEND:
|
||||
len = Curl_dyn_len(&ctx->data_out);
|
||||
if(len > 0) {
|
||||
ssize_t written = Curl_conn_send(data, cf->sockindex,
|
||||
Curl_dyn_ptr(&ctx->data_out),
|
||||
len, &result);
|
||||
if(written < 0)
|
||||
size_t written;
|
||||
result = Curl_conn_send(data, cf->sockindex,
|
||||
Curl_dyn_ptr(&ctx->data_out),
|
||||
len, &written);
|
||||
if(result == CURLE_AGAIN) {
|
||||
result = CURLE_OK;
|
||||
written = 0;
|
||||
}
|
||||
else if(result)
|
||||
goto out;
|
||||
Curl_dyn_tail(&ctx->data_out, len - (size_t)written);
|
||||
Curl_dyn_tail(&ctx->data_out, len - written);
|
||||
if(Curl_dyn_len(&ctx->data_out) > 0) {
|
||||
result = CURLE_OK;
|
||||
goto out;
|
||||
|
|
|
@ -776,10 +776,7 @@ struct cf_socket_ctx {
|
|||
struct Curl_sockaddr_ex addr; /* address to connect to */
|
||||
curl_socket_t sock; /* current attempt socket */
|
||||
struct bufq recvbuf; /* used when `buffer_recv` is set */
|
||||
char r_ip[MAX_IPADR_LEN]; /* remote IP as string */
|
||||
int r_port; /* remote port number */
|
||||
char l_ip[MAX_IPADR_LEN]; /* local IP as string */
|
||||
int l_port; /* local port number */
|
||||
struct ip_quadruple ip; /* The IP quadruple 2x(addr+port) */
|
||||
struct curltime started_at; /* when socket was created */
|
||||
struct curltime connected_at; /* when socket connected/got first byte */
|
||||
struct curltime first_byte_at; /* when first byte was recvd */
|
||||
|
@ -880,8 +877,9 @@ static ssize_t nw_in_read(void *reader_ctx,
|
|||
nread = -1;
|
||||
}
|
||||
}
|
||||
CURL_TRC_CF(rctx->data, rctx->cf, "nw_in_read(len=%zu) -> %d, err=%d",
|
||||
len, (int)nread, *err);
|
||||
CURL_TRC_CF(rctx->data, rctx->cf, "nw_in_read(len=%zu, fd=%"
|
||||
CURL_FORMAT_SOCKET_T ") -> %d, err=%d",
|
||||
len, ctx->sock, (int)nread, *err);
|
||||
return nread;
|
||||
}
|
||||
|
||||
|
@ -940,7 +938,7 @@ static CURLcode set_local_ip(struct Curl_cfilter *cf,
|
|||
return CURLE_FAILED_INIT;
|
||||
}
|
||||
if(!Curl_addr2string((struct sockaddr*)&ssloc, slen,
|
||||
ctx->l_ip, &ctx->l_port)) {
|
||||
ctx->ip.local_ip, &ctx->ip.local_port)) {
|
||||
failf(data, "ssloc inet_ntop() failed with errno %d: %s",
|
||||
errno, Curl_strerror(errno, buffer, sizeof(buffer)));
|
||||
return CURLE_FAILED_INIT;
|
||||
|
@ -961,7 +959,7 @@ static CURLcode set_remote_ip(struct Curl_cfilter *cf,
|
|||
|
||||
/* store remote address and port used in this connection attempt */
|
||||
if(!Curl_addr2string(&ctx->addr.sa_addr, ctx->addr.addrlen,
|
||||
ctx->r_ip, &ctx->r_port)) {
|
||||
ctx->ip.remote_ip, &ctx->ip.remote_port)) {
|
||||
char buffer[STRERROR_LEN];
|
||||
|
||||
ctx->error = errno;
|
||||
|
@ -996,11 +994,11 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf,
|
|||
#ifdef ENABLE_IPV6
|
||||
if(ctx->addr.family == AF_INET6) {
|
||||
set_ipv6_v6only(ctx->sock, 0);
|
||||
infof(data, " Trying [%s]:%d...", ctx->r_ip, ctx->r_port);
|
||||
infof(data, " Trying [%s]:%d...", ctx->ip.remote_ip, ctx->ip.remote_port);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
infof(data, " Trying %s:%d...", ctx->r_ip, ctx->r_port);
|
||||
infof(data, " Trying %s:%d...", ctx->ip.remote_ip, ctx->ip.remote_port);
|
||||
|
||||
#ifdef ENABLE_IPV6
|
||||
is_tcp = (ctx->addr.family == AF_INET
|
||||
|
@ -1166,9 +1164,9 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf,
|
|||
error = SOCKERRNO;
|
||||
set_local_ip(cf, data);
|
||||
CURL_TRC_CF(data, cf, "local address %s port %d...",
|
||||
ctx->l_ip, ctx->l_port);
|
||||
ctx->ip.local_ip, ctx->ip.local_port);
|
||||
if(-1 == rc) {
|
||||
result = socket_connect_result(data, ctx->r_ip, error);
|
||||
result = socket_connect_result(data, ctx->ip.remote_ip, error);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
@ -1213,7 +1211,8 @@ out:
|
|||
{
|
||||
char buffer[STRERROR_LEN];
|
||||
infof(data, "connect to %s port %u from %s port %d failed: %s",
|
||||
ctx->r_ip, ctx->r_port, ctx->l_ip, ctx->l_port,
|
||||
ctx->ip.remote_ip, ctx->ip.remote_port,
|
||||
ctx->ip.local_ip, ctx->ip.local_port,
|
||||
Curl_strerror(ctx->error, buffer, sizeof(buffer)));
|
||||
}
|
||||
#endif
|
||||
|
@ -1233,10 +1232,11 @@ static void cf_socket_get_host(struct Curl_cfilter *cf,
|
|||
const char **pdisplay_host,
|
||||
int *pport)
|
||||
{
|
||||
struct cf_socket_ctx *ctx = cf->ctx;
|
||||
(void)data;
|
||||
*phost = cf->conn->host.name;
|
||||
*pdisplay_host = cf->conn->host.dispname;
|
||||
*pport = cf->conn->port;
|
||||
*pport = ctx->ip.remote_port;
|
||||
}
|
||||
|
||||
static void cf_socket_adjust_pollset(struct Curl_cfilter *cf,
|
||||
|
@ -1248,11 +1248,13 @@ static void cf_socket_adjust_pollset(struct Curl_cfilter *cf,
|
|||
if(ctx->sock != CURL_SOCKET_BAD) {
|
||||
if(!cf->connected) {
|
||||
Curl_pollset_set_out_only(data, ps, ctx->sock);
|
||||
CURL_TRC_CF(data, cf, "adjust_pollset(!connected) -> %d socks", ps->num);
|
||||
CURL_TRC_CF(data, cf, "adjust_pollset, !connected, POLLOUT fd=%"
|
||||
CURL_FORMAT_SOCKET_T, ctx->sock);
|
||||
}
|
||||
else if(!ctx->active) {
|
||||
Curl_pollset_add_in(data, ps, ctx->sock);
|
||||
CURL_TRC_CF(data, cf, "adjust_pollset(!active) -> %d socks", ps->num);
|
||||
CURL_TRC_CF(data, cf, "adjust_pollset, !active, POLLIN fd=%"
|
||||
CURL_FORMAT_SOCKET_T, ctx->sock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1433,31 +1435,24 @@ out:
|
|||
return nread;
|
||||
}
|
||||
|
||||
static void conn_set_primary_ip(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
struct cf_socket_ctx *ctx = cf->ctx;
|
||||
|
||||
(void)data;
|
||||
DEBUGASSERT(sizeof(ctx->r_ip) == sizeof(cf->conn->primary_ip));
|
||||
memcpy(cf->conn->primary_ip, ctx->r_ip, sizeof(cf->conn->primary_ip));
|
||||
}
|
||||
|
||||
static void cf_socket_active(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
{
|
||||
struct cf_socket_ctx *ctx = cf->ctx;
|
||||
|
||||
/* use this socket from now on */
|
||||
cf->conn->sock[cf->sockindex] = ctx->sock;
|
||||
/* the first socket info gets set at conn and data */
|
||||
set_local_ip(cf, data);
|
||||
if(cf->sockindex == SECONDARYSOCKET)
|
||||
cf->conn->secondary = ctx->ip;
|
||||
else
|
||||
cf->conn->primary = ctx->ip;
|
||||
/* the first socket info gets some specials */
|
||||
if(cf->sockindex == FIRSTSOCKET) {
|
||||
cf->conn->remote_addr = &ctx->addr;
|
||||
#ifdef ENABLE_IPV6
|
||||
cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6)? TRUE : FALSE;
|
||||
#endif
|
||||
conn_set_primary_ip(cf, data);
|
||||
set_local_ip(cf, data);
|
||||
Curl_persistconninfo(data, cf->conn, ctx->l_ip, ctx->l_port);
|
||||
Curl_persistconninfo(data, cf->conn, &ctx->ip);
|
||||
/* buffering is currently disabled by default because we have stalls
|
||||
* in parallel transfers where not all buffered data is consumed and no
|
||||
* socket events happen.
|
||||
|
@ -1480,7 +1475,7 @@ static CURLcode cf_socket_cntrl(struct Curl_cfilter *cf,
|
|||
cf_socket_active(cf, data);
|
||||
break;
|
||||
case CF_CTRL_DATA_SETUP:
|
||||
Curl_persistconninfo(data, cf->conn, ctx->l_ip, ctx->l_port);
|
||||
Curl_persistconninfo(data, cf->conn, &ctx->ip);
|
||||
break;
|
||||
case CF_CTRL_FORGET_SOCKET:
|
||||
ctx->sock = CURL_SOCKET_BAD;
|
||||
|
@ -1637,7 +1632,7 @@ static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf,
|
|||
#else
|
||||
rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
|
||||
if(-1 == rc) {
|
||||
return socket_connect_result(data, ctx->r_ip, SOCKERRNO);
|
||||
return socket_connect_result(data, ctx->ip.remote_ip, SOCKERRNO);
|
||||
}
|
||||
ctx->sock_connected = TRUE;
|
||||
#endif
|
||||
|
@ -1645,7 +1640,8 @@ static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf,
|
|||
CURL_TRC_CF(data, cf, "%s socket %" CURL_FORMAT_SOCKET_T
|
||||
" connected: [%s:%d] -> [%s:%d]",
|
||||
(ctx->transport == TRNSPRT_QUIC)? "QUIC" : "UDP",
|
||||
ctx->sock, ctx->l_ip, ctx->l_port, ctx->r_ip, ctx->r_port);
|
||||
ctx->sock, ctx->ip.local_ip, ctx->ip.local_port,
|
||||
ctx->ip.remote_ip, ctx->ip.remote_port);
|
||||
|
||||
(void)curlx_nonblock(ctx->sock, TRUE);
|
||||
switch(ctx->addr.family) {
|
||||
|
@ -1695,7 +1691,7 @@ static CURLcode cf_udp_connect(struct Curl_cfilter *cf,
|
|||
goto out;
|
||||
CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%"
|
||||
CURL_FORMAT_SOCKET_T " (%s:%d)",
|
||||
ctx->sock, ctx->l_ip, ctx->l_port);
|
||||
ctx->sock, ctx->ip.local_ip, ctx->ip.local_port);
|
||||
}
|
||||
else {
|
||||
CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%"
|
||||
|
@ -1891,8 +1887,8 @@ static void set_accepted_remote_ip(struct Curl_cfilter *cf,
|
|||
struct Curl_sockaddr_storage ssrem;
|
||||
curl_socklen_t plen;
|
||||
|
||||
ctx->r_ip[0] = 0;
|
||||
ctx->r_port = 0;
|
||||
ctx->ip.remote_ip[0] = 0;
|
||||
ctx->ip.remote_port = 0;
|
||||
plen = sizeof(ssrem);
|
||||
memset(&ssrem, 0, plen);
|
||||
if(getpeername(ctx->sock, (struct sockaddr*) &ssrem, &plen)) {
|
||||
|
@ -1902,14 +1898,14 @@ static void set_accepted_remote_ip(struct Curl_cfilter *cf,
|
|||
return;
|
||||
}
|
||||
if(!Curl_addr2string((struct sockaddr*)&ssrem, plen,
|
||||
ctx->r_ip, &ctx->r_port)) {
|
||||
ctx->ip.remote_ip, &ctx->ip.remote_port)) {
|
||||
failf(data, "ssrem inet_ntop() failed with errno %d: %s",
|
||||
errno, Curl_strerror(errno, buffer, sizeof(buffer)));
|
||||
return;
|
||||
}
|
||||
#else
|
||||
ctx->r_ip[0] = 0;
|
||||
ctx->r_port = 0;
|
||||
ctx->ip.remote_ip[0] = 0;
|
||||
ctx->ip.remote_port = 0;
|
||||
(void)data;
|
||||
#endif
|
||||
}
|
||||
|
@ -1938,7 +1934,7 @@ CURLcode Curl_conn_tcp_accepted_set(struct Curl_easy *data,
|
|||
cf->connected = TRUE;
|
||||
CURL_TRC_CF(data, cf, "accepted_set(sock=%" CURL_FORMAT_SOCKET_T
|
||||
", remote=%s port=%d)",
|
||||
ctx->sock, ctx->r_ip, ctx->r_port);
|
||||
ctx->sock, ctx->ip.remote_ip, ctx->ip.remote_port);
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
@ -1958,9 +1954,9 @@ CURLcode Curl_cf_socket_peek(struct Curl_cfilter *cf,
|
|||
struct Curl_easy *data,
|
||||
curl_socket_t *psock,
|
||||
const struct Curl_sockaddr_ex **paddr,
|
||||
const char **pr_ip_str, int *pr_port,
|
||||
const char **pl_ip_str, int *pl_port)
|
||||
struct ip_quadruple *pip)
|
||||
{
|
||||
(void)data;
|
||||
if(cf_is_socket(cf) && cf->ctx) {
|
||||
struct cf_socket_ctx *ctx = cf->ctx;
|
||||
|
||||
|
@ -1968,17 +1964,8 @@ CURLcode Curl_cf_socket_peek(struct Curl_cfilter *cf,
|
|||
*psock = ctx->sock;
|
||||
if(paddr)
|
||||
*paddr = &ctx->addr;
|
||||
if(pr_ip_str)
|
||||
*pr_ip_str = ctx->r_ip;
|
||||
if(pr_port)
|
||||
*pr_port = ctx->r_port;
|
||||
if(pl_port ||pl_ip_str) {
|
||||
set_local_ip(cf, data);
|
||||
if(pl_ip_str)
|
||||
*pl_ip_str = ctx->l_ip;
|
||||
if(pl_port)
|
||||
*pl_port = ctx->l_port;
|
||||
}
|
||||
if(pip)
|
||||
*pip = ctx->ip;
|
||||
return CURLE_OK;
|
||||
}
|
||||
return CURLE_FAILED_INIT;
|
||||
|
|
|
@ -33,6 +33,7 @@ struct Curl_cfilter;
|
|||
struct Curl_easy;
|
||||
struct connectdata;
|
||||
struct Curl_sockaddr_ex;
|
||||
struct ip_quadruple;
|
||||
|
||||
/*
|
||||
* The Curl_sockaddr_ex structure is basically libcurl's external API
|
||||
|
@ -153,18 +154,14 @@ CURLcode Curl_conn_tcp_accepted_set(struct Curl_easy *data,
|
|||
* The filter owns all returned values.
|
||||
* @param psock pointer to hold socket descriptor or NULL
|
||||
* @param paddr pointer to hold addr reference or NULL
|
||||
* @param pr_ip_str pointer to hold remote addr as string or NULL
|
||||
* @param pr_port pointer to hold remote port number or NULL
|
||||
* @param pl_ip_str pointer to hold local addr as string or NULL
|
||||
* @param pl_port pointer to hold local port number or NULL
|
||||
* @param pip pointer to get IP quadruple or NULL
|
||||
* Returns error if the filter is of invalid type.
|
||||
*/
|
||||
CURLcode Curl_cf_socket_peek(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
curl_socket_t *psock,
|
||||
const struct Curl_sockaddr_ex **paddr,
|
||||
const char **pr_ip_str, int *pr_port,
|
||||
const char **pl_ip_str, int *pl_port);
|
||||
struct ip_quadruple *pip);
|
||||
|
||||
extern struct Curl_cftype Curl_cft_tcp;
|
||||
extern struct Curl_cftype Curl_cft_udp;
|
||||
|
|
|
@ -67,7 +67,7 @@ void Curl_cf_def_get_host(struct Curl_cfilter *cf, struct Curl_easy *data,
|
|||
else {
|
||||
*phost = cf->conn->host.name;
|
||||
*pdisplay_host = cf->conn->host.dispname;
|
||||
*pport = cf->conn->port;
|
||||
*pport = cf->conn->primary.remote_port;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -168,38 +168,46 @@ void Curl_conn_close(struct Curl_easy *data, int index)
|
|||
}
|
||||
}
|
||||
|
||||
ssize_t Curl_conn_recv(struct Curl_easy *data, int num, char *buf,
|
||||
size_t len, CURLcode *code)
|
||||
ssize_t Curl_cf_recv(struct Curl_easy *data, int num, char *buf,
|
||||
size_t len, CURLcode *code)
|
||||
{
|
||||
struct Curl_cfilter *cf;
|
||||
|
||||
DEBUGASSERT(data);
|
||||
DEBUGASSERT(data->conn);
|
||||
*code = CURLE_OK;
|
||||
cf = data->conn->cfilter[num];
|
||||
while(cf && !cf->connected) {
|
||||
cf = cf->next;
|
||||
}
|
||||
if(cf) {
|
||||
return cf->cft->do_recv(cf, data, buf, len, code);
|
||||
ssize_t nread = cf->cft->do_recv(cf, data, buf, len, code);
|
||||
DEBUGASSERT(nread >= 0 || *code);
|
||||
DEBUGASSERT(nread < 0 || !*code);
|
||||
return nread;
|
||||
}
|
||||
failf(data, "recv: no filter connected");
|
||||
*code = CURLE_FAILED_INIT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t Curl_conn_send(struct Curl_easy *data, int num,
|
||||
const void *mem, size_t len, CURLcode *code)
|
||||
ssize_t Curl_cf_send(struct Curl_easy *data, int num,
|
||||
const void *mem, size_t len, CURLcode *code)
|
||||
{
|
||||
struct Curl_cfilter *cf;
|
||||
|
||||
DEBUGASSERT(data);
|
||||
DEBUGASSERT(data->conn);
|
||||
*code = CURLE_OK;
|
||||
cf = data->conn->cfilter[num];
|
||||
while(cf && !cf->connected) {
|
||||
cf = cf->next;
|
||||
}
|
||||
if(cf) {
|
||||
return cf->cft->do_send(cf, data, mem, len, code);
|
||||
ssize_t nwritten = cf->cft->do_send(cf, data, mem, len, code);
|
||||
DEBUGASSERT(nwritten >= 0 || *code);
|
||||
DEBUGASSERT(nwritten < 0 || !*code || !len);
|
||||
return nwritten;
|
||||
}
|
||||
failf(data, "send: no filter connected");
|
||||
DEBUGASSERT(0);
|
||||
|
@ -662,6 +670,58 @@ size_t Curl_conn_get_max_concurrent(struct Curl_easy *data,
|
|||
return (result || n <= 0)? 1 : (size_t)n;
|
||||
}
|
||||
|
||||
int Curl_conn_sockindex(struct Curl_easy *data, curl_socket_t sockfd)
|
||||
{
|
||||
if(data && data->conn &&
|
||||
sockfd != CURL_SOCKET_BAD && sockfd == data->conn->sock[SECONDARYSOCKET])
|
||||
return SECONDARYSOCKET;
|
||||
return FIRSTSOCKET;
|
||||
}
|
||||
|
||||
CURLcode Curl_conn_recv(struct Curl_easy *data, int sockindex,
|
||||
char *buf, size_t blen, ssize_t *n)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
ssize_t nread;
|
||||
|
||||
DEBUGASSERT(data->conn);
|
||||
nread = data->conn->recv[sockindex](data, sockindex, buf, blen, &result);
|
||||
DEBUGASSERT(nread >= 0 || result);
|
||||
DEBUGASSERT(nread < 0 || !result);
|
||||
*n = (nread >= 0)? (size_t)nread : 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex,
|
||||
const void *buf, size_t blen,
|
||||
size_t *pnwritten)
|
||||
{
|
||||
ssize_t nwritten;
|
||||
CURLcode result = CURLE_OK;
|
||||
struct connectdata *conn;
|
||||
|
||||
DEBUGASSERT(sockindex >= 0 && sockindex < 2);
|
||||
DEBUGASSERT(pnwritten);
|
||||
DEBUGASSERT(data);
|
||||
DEBUGASSERT(data->conn);
|
||||
conn = data->conn;
|
||||
#ifdef CURLDEBUG
|
||||
{
|
||||
/* Allow debug builds to override this logic to force short sends
|
||||
*/
|
||||
char *p = getenv("CURL_SMALLSENDS");
|
||||
if(p) {
|
||||
size_t altsize = (size_t)strtoul(p, NULL, 10);
|
||||
if(altsize)
|
||||
blen = CURLMIN(blen, altsize);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
nwritten = conn->send[sockindex](data, sockindex, buf, blen, &result);
|
||||
DEBUGASSERT((nwritten >= 0) || result);
|
||||
*pnwritten = (nwritten < 0)? 0 : (size_t)nwritten;
|
||||
return result;
|
||||
}
|
||||
|
||||
void Curl_pollset_reset(struct Curl_easy *data,
|
||||
struct easy_pollset *ps)
|
||||
|
|
|
@ -402,11 +402,11 @@ void Curl_conn_adjust_pollset(struct Curl_easy *data,
|
|||
/**
|
||||
* Receive data through the filter chain at `sockindex` for connection
|
||||
* `data->conn`. Copy at most `len` bytes into `buf`. Return the
|
||||
* actuel number of bytes copied or a negative value on error.
|
||||
* actual number of bytes copied or a negative value on error.
|
||||
* The error code is placed into `*code`.
|
||||
*/
|
||||
ssize_t Curl_conn_recv(struct Curl_easy *data, int sockindex, char *buf,
|
||||
size_t len, CURLcode *code);
|
||||
ssize_t Curl_cf_recv(struct Curl_easy *data, int sockindex, char *buf,
|
||||
size_t len, CURLcode *code);
|
||||
|
||||
/**
|
||||
* Send `len` bytes of data from `buf` through the filter chain `sockindex`
|
||||
|
@ -414,8 +414,8 @@ ssize_t Curl_conn_recv(struct Curl_easy *data, int sockindex, char *buf,
|
|||
* or a negative value on error.
|
||||
* The error code is placed into `*code`.
|
||||
*/
|
||||
ssize_t Curl_conn_send(struct Curl_easy *data, int sockindex,
|
||||
const void *buf, size_t len, CURLcode *code);
|
||||
ssize_t Curl_cf_send(struct Curl_easy *data, int sockindex,
|
||||
const void *buf, size_t len, CURLcode *code);
|
||||
|
||||
/**
|
||||
* The easy handle `data` is being attached to `conn`. This does
|
||||
|
@ -497,6 +497,30 @@ size_t Curl_conn_get_max_concurrent(struct Curl_easy *data,
|
|||
int sockindex);
|
||||
|
||||
|
||||
/**
|
||||
* Get the index of the given socket in the connection's sockets.
|
||||
* Useful in calling `Curl_conn_send()/Curl_conn_recv()` with the
|
||||
* correct socket index.
|
||||
*/
|
||||
int Curl_conn_sockindex(struct Curl_easy *data, curl_socket_t sockfd);
|
||||
|
||||
/*
|
||||
* Receive data on the connection, using FIRSTSOCKET/SECONDARYSOCKET.
|
||||
* Will return CURLE_AGAIN iff blocked on receiving.
|
||||
*/
|
||||
CURLcode Curl_conn_recv(struct Curl_easy *data, int sockindex,
|
||||
char *buf, size_t buffersize,
|
||||
ssize_t *pnread);
|
||||
|
||||
/*
|
||||
* Send data on the connection, using FIRSTSOCKET/SECONDARYSOCKET.
|
||||
* Will return CURLE_AGAIN iff blocked on sending.
|
||||
*/
|
||||
CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex,
|
||||
const void *buf, size_t blen,
|
||||
size_t *pnwritten);
|
||||
|
||||
|
||||
void Curl_pollset_reset(struct Curl_easy *data,
|
||||
struct easy_pollset *ps);
|
||||
|
||||
|
|
|
@ -131,7 +131,7 @@ static void hashkey(struct connectdata *conn, char *buf, size_t len)
|
|||
#ifndef CURL_DISABLE_PROXY
|
||||
if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
|
||||
hostname = conn->http_proxy.host.name;
|
||||
port = conn->port;
|
||||
port = conn->primary.remote_port;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
|
@ -395,8 +395,6 @@ bool Curl_conncache_return_conn(struct Curl_easy *data,
|
|||
important that details from this (unrelated) disconnect does not
|
||||
taint meta-data in the data handle. */
|
||||
struct conncache *connc = data->state.conn_cache;
|
||||
connc->closure_handle->state.buffer = data->state.buffer;
|
||||
connc->closure_handle->set.buffer_size = data->set.buffer_size;
|
||||
Curl_disconnect(connc->closure_handle, conn_candidate,
|
||||
/* dead_connection */ FALSE);
|
||||
}
|
||||
|
@ -522,12 +520,9 @@ Curl_conncache_extract_oldest(struct Curl_easy *data)
|
|||
void Curl_conncache_close_all_connections(struct conncache *connc)
|
||||
{
|
||||
struct connectdata *conn;
|
||||
char buffer[READBUFFER_MIN + 1];
|
||||
SIGPIPE_VARIABLE(pipe_st);
|
||||
if(!connc->closure_handle)
|
||||
return;
|
||||
connc->closure_handle->state.buffer = buffer;
|
||||
connc->closure_handle->set.buffer_size = READBUFFER_MIN;
|
||||
|
||||
conn = conncache_find_first_connection(connc);
|
||||
while(conn) {
|
||||
|
@ -541,7 +536,6 @@ void Curl_conncache_close_all_connections(struct conncache *connc)
|
|||
conn = conncache_find_first_connection(connc);
|
||||
}
|
||||
|
||||
connc->closure_handle->state.buffer = NULL;
|
||||
sigpipe_ignore(connc->closure_handle, &pipe_st);
|
||||
|
||||
Curl_hostcache_clean(connc->closure_handle,
|
||||
|
|
|
@ -94,7 +94,7 @@
|
|||
* infinite time left). If the value is negative, the timeout time has already
|
||||
* elapsed.
|
||||
* @param data the transfer to check on
|
||||
* @param nowp timestamp to use for calculdation, NULL to use Curl_now()
|
||||
* @param nowp timestamp to use for calculation, NULL to use Curl_now()
|
||||
* @param duringconnect TRUE iff connect timeout is also taken into account.
|
||||
* @unittest: 1303
|
||||
*/
|
||||
|
@ -145,19 +145,26 @@ timediff_t Curl_timeleft(struct Curl_easy *data,
|
|||
/* Copies connection info into the transfer handle to make it available when
|
||||
the transfer handle is no longer associated with the connection. */
|
||||
void Curl_persistconninfo(struct Curl_easy *data, struct connectdata *conn,
|
||||
char *local_ip, int local_port)
|
||||
struct ip_quadruple *ip)
|
||||
{
|
||||
memcpy(data->info.conn_primary_ip, conn->primary_ip, MAX_IPADR_LEN);
|
||||
if(local_ip && local_ip[0])
|
||||
memcpy(data->info.conn_local_ip, local_ip, MAX_IPADR_LEN);
|
||||
else
|
||||
data->info.conn_local_ip[0] = 0;
|
||||
if(ip)
|
||||
data->info.primary = *ip;
|
||||
else {
|
||||
memset(&data->info.primary, 0, sizeof(data->info.primary));
|
||||
data->info.primary.remote_port = -1;
|
||||
data->info.primary.local_port = -1;
|
||||
}
|
||||
data->info.conn_scheme = conn->handler->scheme;
|
||||
/* conn_protocol can only provide "old" protocols */
|
||||
data->info.conn_protocol = (conn->handler->protocol) & CURLPROTO_MASK;
|
||||
data->info.conn_primary_port = conn->port;
|
||||
data->info.conn_remote_port = conn->remote_port;
|
||||
data->info.conn_local_port = local_port;
|
||||
data->info.used_proxy =
|
||||
#ifdef CURL_DISABLE_PROXY
|
||||
0
|
||||
#else
|
||||
conn->bits.proxy
|
||||
#endif
|
||||
;
|
||||
}
|
||||
|
||||
static const struct Curl_addrinfo *
|
||||
|
@ -721,7 +728,7 @@ evaluate:
|
|||
|
||||
failf(data, "Failed to connect to %s port %u after "
|
||||
"%" CURL_FORMAT_TIMEDIFF_T " ms: %s",
|
||||
hostname, conn->port,
|
||||
hostname, conn->primary.remote_port,
|
||||
Curl_timediff(now, data->progress.t_startsingle),
|
||||
curl_easy_strerror(result));
|
||||
|
||||
|
@ -911,7 +918,7 @@ static CURLcode cf_he_connect(struct Curl_cfilter *cf,
|
|||
|
||||
if(cf->conn->handler->protocol & PROTO_FAMILY_SSH)
|
||||
Curl_pgrsTime(data, TIMER_APPCONNECT); /* we're connected already */
|
||||
Curl_verboseconnect(data, cf->conn);
|
||||
Curl_verboseconnect(data, cf->conn, cf->sockindex);
|
||||
data->info.numconnects++; /* to track the # of connections made */
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "timeval.h"
|
||||
|
||||
struct Curl_dns_entry;
|
||||
struct ip_quadruple;
|
||||
|
||||
/* generic function that returns how much time there's left to run, according
|
||||
to the timeouts set */
|
||||
|
@ -52,7 +53,7 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
|
|||
char *addr, int *port);
|
||||
|
||||
void Curl_persistconninfo(struct Curl_easy *data, struct connectdata *conn,
|
||||
char *local_ip, int local_port);
|
||||
struct ip_quadruple *ip);
|
||||
|
||||
/*
|
||||
* Curl_conncontrol() marks the end of a connection/stream. The 'closeit'
|
||||
|
|
33
lib/cookie.c
33
lib/cookie.c
|
@ -426,6 +426,7 @@ static void remove_expired(struct CookieInfo *cookies)
|
|||
}
|
||||
}
|
||||
|
||||
#ifndef USE_LIBPSL
|
||||
/* Make sure domain contains a dot or is localhost. */
|
||||
static bool bad_domain(const char *domain, size_t len)
|
||||
{
|
||||
|
@ -443,6 +444,7 @@ static bool bad_domain(const char *domain, size_t len)
|
|||
}
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
RFC 6265 section 4.1.1 says a server should accept this range:
|
||||
|
@ -1040,7 +1042,7 @@ Curl_cookie_add(struct Curl_easy *data,
|
|||
Curl_psl_release(data);
|
||||
}
|
||||
else
|
||||
acceptable = !bad_domain(domain, strlen(domain));
|
||||
infof(data, "libpsl problem, rejecting cookie for satety");
|
||||
}
|
||||
|
||||
if(!acceptable) {
|
||||
|
@ -1205,7 +1207,6 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
|
|||
bool newsession)
|
||||
{
|
||||
struct CookieInfo *c;
|
||||
char *line = NULL;
|
||||
FILE *handle = NULL;
|
||||
|
||||
if(!inc) {
|
||||
|
@ -1241,16 +1242,14 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
|
|||
|
||||
c->running = FALSE; /* this is not running, this is init */
|
||||
if(fp) {
|
||||
|
||||
line = malloc(MAX_COOKIE_LINE);
|
||||
if(!line)
|
||||
goto fail;
|
||||
while(Curl_get_line(line, MAX_COOKIE_LINE, fp)) {
|
||||
char *lineptr = line;
|
||||
struct dynbuf buf;
|
||||
Curl_dyn_init(&buf, MAX_COOKIE_LINE);
|
||||
while(Curl_get_line(&buf, fp)) {
|
||||
char *lineptr = Curl_dyn_ptr(&buf);
|
||||
bool headerline = FALSE;
|
||||
if(checkprefix("Set-Cookie:", line)) {
|
||||
if(checkprefix("Set-Cookie:", lineptr)) {
|
||||
/* This is a cookie line, get it! */
|
||||
lineptr = &line[11];
|
||||
lineptr += 11;
|
||||
headerline = TRUE;
|
||||
while(*lineptr && ISBLANK(*lineptr))
|
||||
lineptr++;
|
||||
|
@ -1258,7 +1257,7 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
|
|||
|
||||
Curl_cookie_add(data, c, headerline, TRUE, lineptr, NULL, NULL, TRUE);
|
||||
}
|
||||
free(line); /* free the line buffer */
|
||||
Curl_dyn_free(&buf); /* free the line buffer */
|
||||
|
||||
/*
|
||||
* Remove expired cookies from the hash. We must make sure to run this
|
||||
|
@ -1274,18 +1273,6 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
|
|||
c->running = TRUE; /* now, we're running */
|
||||
|
||||
return c;
|
||||
|
||||
fail:
|
||||
free(line);
|
||||
/*
|
||||
* Only clean up if we allocated it here, as the original could still be in
|
||||
* use by a share handle.
|
||||
*/
|
||||
if(!inc)
|
||||
Curl_cookie_cleanup(c);
|
||||
if(handle)
|
||||
fclose(handle);
|
||||
return NULL; /* out of memory */
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -720,6 +720,9 @@ ${SIZEOF_TIME_T_CODE}
|
|||
/* to enable quiche */
|
||||
#cmakedefine USE_QUICHE 1
|
||||
|
||||
/* to enable openssl + nghttp3 */
|
||||
#cmakedefine USE_OPENSSL_QUIC 1
|
||||
|
||||
/* Define to 1 if you have the quiche_conn_set_qlog_fd function. */
|
||||
#cmakedefine HAVE_QUICHE_CONN_SET_QLOG_FD 1
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
* Curl_des_set_odd_parity()
|
||||
*
|
||||
* This is used to apply odd parity to the given byte array. It is typically
|
||||
* used by when a cryptography engines doesn't have it's own version.
|
||||
* used by when a cryptography engine doesn't have its own version.
|
||||
*
|
||||
* The function is a port of the Java based oddParity() function over at:
|
||||
*
|
||||
|
|
|
@ -33,14 +33,16 @@
|
|||
#include "memdebug.h"
|
||||
|
||||
/*
|
||||
* Curl_get_line() makes sure to only return complete whole lines that fit in
|
||||
* 'len' bytes and end with a newline.
|
||||
* Curl_get_line() makes sure to only return complete whole lines that end
|
||||
* newlines.
|
||||
*/
|
||||
char *Curl_get_line(char *buf, int len, FILE *input)
|
||||
int Curl_get_line(struct dynbuf *buf, FILE *input)
|
||||
{
|
||||
bool partial = FALSE;
|
||||
CURLcode result;
|
||||
char buffer[128];
|
||||
Curl_dyn_reset(buf);
|
||||
while(1) {
|
||||
char *b = fgets(buf, len, input);
|
||||
char *b = fgets(buffer, sizeof(buffer), input);
|
||||
|
||||
if(b) {
|
||||
size_t rlen = strlen(b);
|
||||
|
@ -48,39 +50,28 @@ char *Curl_get_line(char *buf, int len, FILE *input)
|
|||
if(!rlen)
|
||||
break;
|
||||
|
||||
if(b[rlen-1] == '\n') {
|
||||
/* b is \n terminated */
|
||||
if(partial) {
|
||||
partial = FALSE;
|
||||
continue;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
else if(feof(input)) {
|
||||
if(partial)
|
||||
/* Line is already too large to return, ignore rest */
|
||||
break;
|
||||
result = Curl_dyn_addn(buf, b, rlen);
|
||||
if(result)
|
||||
/* too long line or out of memory */
|
||||
return 0; /* error */
|
||||
|
||||
if(rlen + 1 < (size_t) len) {
|
||||
/* b is EOF terminated, insert missing \n */
|
||||
b[rlen] = '\n';
|
||||
b[rlen + 1] = '\0';
|
||||
return b;
|
||||
}
|
||||
else
|
||||
/* Maximum buffersize reached + EOF
|
||||
* This line is impossible to add a \n to so we'll ignore it
|
||||
*/
|
||||
break;
|
||||
else if(b[rlen-1] == '\n')
|
||||
/* end of the line */
|
||||
return 1; /* all good */
|
||||
|
||||
else if(feof(input)) {
|
||||
/* append a newline */
|
||||
result = Curl_dyn_addn(buf, "\n", 1);
|
||||
if(result)
|
||||
/* too long line or out of memory */
|
||||
return 0; /* error */
|
||||
return 1; /* all good */
|
||||
}
|
||||
else
|
||||
/* Maximum buffersize reached */
|
||||
partial = TRUE;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* if not disabled */
|
||||
|
|
|
@ -24,8 +24,9 @@
|
|||
*
|
||||
***************************************************************************/
|
||||
|
||||
/* get_line() makes sure to only return complete whole lines that fit in 'len'
|
||||
* bytes and end with a newline. */
|
||||
char *Curl_get_line(char *buf, int len, FILE *input);
|
||||
#include "dynbuf.h"
|
||||
|
||||
/* Curl_get_line() returns complete lines that end with a newline. */
|
||||
int Curl_get_line(struct dynbuf *buf, FILE *input);
|
||||
|
||||
#endif /* HEADER_CURL_GET_LINE_H */
|
||||
|
|
|
@ -266,7 +266,7 @@ static CURLcode ntlm_wb_response(struct Curl_easy *data, struct ntlmdata *ntlm,
|
|||
size_t len_in = strlen(input), len_out = 0;
|
||||
struct dynbuf b;
|
||||
char *ptr = NULL;
|
||||
usigned char buf[1024]
|
||||
unsigned char buf[1024];
|
||||
Curl_dyn_init(&b, MAX_NTLM_WB_RESPONSE);
|
||||
|
||||
while(len_in > 0) {
|
||||
|
|
|
@ -265,10 +265,10 @@ static CURLcode rtmp_do(struct Curl_easy *data, bool *done)
|
|||
|
||||
if(data->state.upload) {
|
||||
Curl_pgrsSetUploadSize(data, data->state.infilesize);
|
||||
Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
|
||||
Curl_xfer_setup(data, -1, -1, FALSE, FIRSTSOCKET);
|
||||
}
|
||||
else
|
||||
Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
|
||||
Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1);
|
||||
*done = TRUE;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
|
|
@ -266,6 +266,13 @@
|
|||
|
||||
#include <curl/system.h>
|
||||
|
||||
/* Helper macro to expand and concatenate two macros.
|
||||
* Direct macros concatenation does not work because macros
|
||||
* are not expanded before direct concatenation.
|
||||
*/
|
||||
#define CURL_CONC_MACROS_(A,B) A ## B
|
||||
#define CURL_CONC_MACROS(A,B) CURL_CONC_MACROS_(A,B)
|
||||
|
||||
/* curl uses its own printf() function internally. It understands the GNU
|
||||
* format. Use this format, so that is matches the GNU format attribute we
|
||||
* use with the mingw compiler, allowing it to verify them at compile-time.
|
||||
|
@ -495,6 +502,17 @@
|
|||
#endif
|
||||
#define CURL_OFF_T_MIN (-CURL_OFF_T_MAX - CURL_OFF_T_C(1))
|
||||
|
||||
#if (SIZEOF_CURL_OFF_T != 8)
|
||||
# error "curl_off_t must be exactly 64 bits"
|
||||
#else
|
||||
typedef unsigned CURL_TYPEOF_CURL_OFF_T curl_uint64_t;
|
||||
# ifndef CURL_SUFFIX_CURL_OFF_TU
|
||||
# error "CURL_SUFFIX_CURL_OFF_TU must be defined"
|
||||
# endif
|
||||
# define CURL_UINT64_SUFFIX CURL_SUFFIX_CURL_OFF_TU
|
||||
# define CURL_UINT64_C(val) CURL_CONC_MACROS(val,CURL_UINT64_SUFFIX)
|
||||
#endif
|
||||
|
||||
#if (SIZEOF_TIME_T == 4)
|
||||
# ifdef HAVE_TIME_T_UNSIGNED
|
||||
# define TIME_T_MAX UINT_MAX
|
||||
|
|
|
@ -0,0 +1,844 @@
|
|||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) Evgeny Grin (Karlson2k), <k2k@narod.ru>.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at https://curl.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#include "curl_setup.h"
|
||||
|
||||
#if !defined(CURL_DISABLE_DIGEST_AUTH) && !defined(CURL_DISABLE_SHA512_256)
|
||||
|
||||
#include "curl_sha512_256.h"
|
||||
#include "warnless.h"
|
||||
|
||||
/* The recommended order of the TLS backends:
|
||||
* * OpenSSL
|
||||
* * GnuTLS
|
||||
* * wolfSSL
|
||||
* * Schannel SSPI
|
||||
* * SecureTransport (Darwin)
|
||||
* * mbedTLS
|
||||
* * BearSSL
|
||||
* * rustls
|
||||
* Skip the backend if it does not support the required algorithm */
|
||||
|
||||
#if defined(USE_OPENSSL)
|
||||
# include <openssl/opensslv.h>
|
||||
# if (!defined(LIBRESSL_VERSION_NUMBER) && \
|
||||
defined(OPENSSL_VERSION_NUMBER) && \
|
||||
(OPENSSL_VERSION_NUMBER >= 0x10100010L)) || \
|
||||
(defined(LIBRESSL_VERSION_NUMBER) && \
|
||||
(LIBRESSL_VERSION_NUMBER >= 0x3080000fL))
|
||||
# include <openssl/opensslconf.h>
|
||||
# if !defined(OPENSSL_NO_SHA) && !defined(OPENSSL_NO_SHA512)
|
||||
# include <openssl/evp.h>
|
||||
# define USE_OPENSSL_SHA512_256 1
|
||||
# define HAS_SHA512_256_IMPLEMENTATION 1
|
||||
# endif
|
||||
# endif
|
||||
#endif /* USE_OPENSSL */
|
||||
|
||||
|
||||
#if !defined(HAS_SHA512_256_IMPLEMENTATION) && defined(USE_GNUTLS)
|
||||
# include <nettle/sha.h>
|
||||
# if defined(SHA512_256_DIGEST_SIZE)
|
||||
# define USE_GNUTLS_SHA512_256 1
|
||||
# define HAS_SHA512_256_IMPLEMENTATION 1
|
||||
# endif
|
||||
#endif /* ! HAS_SHA512_256_IMPLEMENTATION && USE_GNUTLS */
|
||||
|
||||
#if defined(USE_OPENSSL_SHA512_256)
|
||||
|
||||
/* OpenSSL does not provide macros for SHA-512/256 sizes */
|
||||
|
||||
/**
|
||||
* Size of the SHA-512/256 single processing block in bytes.
|
||||
*/
|
||||
#define SHA512_256_BLOCK_SIZE 128
|
||||
|
||||
/**
|
||||
* Size of the SHA-512/256 resulting digest in bytes.
|
||||
* This is the final digest size, not intermediate hash.
|
||||
*/
|
||||
#define SHA512_256_DIGEST_SIZE SHA512_256_DIGEST_LENGTH
|
||||
|
||||
/**
|
||||
* Context type used for SHA-512/256 calculations
|
||||
*/
|
||||
typedef EVP_MD_CTX *Curl_sha512_256_ctx;
|
||||
|
||||
/**
|
||||
* Initialise structure for SHA-512/256 calculation.
|
||||
*
|
||||
* @param context the calculation context
|
||||
* @return CURLE_OK if succeed,
|
||||
* error code otherwise
|
||||
*/
|
||||
static CURLcode
|
||||
Curl_sha512_256_init(void *context)
|
||||
{
|
||||
Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context;
|
||||
|
||||
*ctx = EVP_MD_CTX_create();
|
||||
if(!*ctx)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
if(EVP_DigestInit_ex(*ctx, EVP_sha512_256(), NULL)) {
|
||||
/* Check whether the header and this file use the same numbers */
|
||||
DEBUGASSERT(EVP_MD_CTX_size(*ctx) == SHA512_256_DIGEST_SIZE);
|
||||
/* Check whether the block size is correct */
|
||||
DEBUGASSERT(EVP_MD_CTX_block_size(*ctx) == SHA512_256_BLOCK_SIZE);
|
||||
|
||||
return CURLE_OK; /* Success */
|
||||
}
|
||||
|
||||
/* Cleanup */
|
||||
EVP_MD_CTX_destroy(*ctx);
|
||||
return CURLE_FAILED_INIT;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process portion of bytes.
|
||||
*
|
||||
* @param context the calculation context
|
||||
* @param data bytes to add to hash
|
||||
* @return CURLE_OK if succeed,
|
||||
* error code otherwise
|
||||
*/
|
||||
static CURLcode
|
||||
Curl_sha512_256_update(void *context,
|
||||
const unsigned char *data,
|
||||
size_t length)
|
||||
{
|
||||
Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context;
|
||||
|
||||
if(!EVP_DigestUpdate(*ctx, data, length))
|
||||
return CURLE_SSL_CIPHER;
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finalise SHA-512/256 calculation, return digest.
|
||||
*
|
||||
* @param context the calculation context
|
||||
* @param[out] digest set to the hash, must be #SHA512_256_DIGEST_SIZE bytes
|
||||
* @return CURLE_OK if succeed,
|
||||
* error code otherwise
|
||||
*/
|
||||
static CURLcode
|
||||
Curl_sha512_256_finish(unsigned char *digest,
|
||||
void *context)
|
||||
{
|
||||
CURLcode ret;
|
||||
Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context;
|
||||
|
||||
#ifdef __NetBSD__
|
||||
/* Use a larger buffer to work around a bug in NetBSD:
|
||||
https://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=58039 */
|
||||
unsigned char tmp_digest[SHA512_256_DIGEST_SIZE * 2];
|
||||
ret = EVP_DigestFinal_ex(*ctx,
|
||||
tmp_digest, NULL) ? CURLE_OK : CURLE_SSL_CIPHER;
|
||||
if(ret == CURLE_OK)
|
||||
memcpy(digest, tmp_digest, SHA512_256_DIGEST_SIZE);
|
||||
#else /* ! __NetBSD__ */
|
||||
ret = EVP_DigestFinal_ex(*ctx, digest, NULL) ? CURLE_OK : CURLE_SSL_CIPHER;
|
||||
#endif /* ! __NetBSD__ */
|
||||
|
||||
EVP_MD_CTX_destroy(*ctx);
|
||||
*ctx = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#elif defined(USE_GNUTLS_SHA512_256)
|
||||
|
||||
/**
|
||||
* Context type used for SHA-512/256 calculations
|
||||
*/
|
||||
typedef struct sha512_256_ctx Curl_sha512_256_ctx;
|
||||
|
||||
/**
|
||||
* Initialise structure for SHA-512/256 calculation.
|
||||
*
|
||||
* @param context the calculation context
|
||||
* @return always CURLE_OK
|
||||
*/
|
||||
static CURLcode
|
||||
Curl_sha512_256_init(void *context)
|
||||
{
|
||||
Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context;
|
||||
|
||||
/* Check whether the header and this file use the same numbers */
|
||||
DEBUGASSERT(SHA512_256_DIGEST_LENGTH == SHA512_256_DIGEST_SIZE);
|
||||
|
||||
sha512_256_init(ctx);
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process portion of bytes.
|
||||
*
|
||||
* @param context the calculation context
|
||||
* @param data bytes to add to hash
|
||||
* @param length number of bytes in @a data
|
||||
* @return always CURLE_OK
|
||||
*/
|
||||
static CURLcode
|
||||
Curl_sha512_256_update(void *context,
|
||||
const unsigned char *data,
|
||||
size_t length)
|
||||
{
|
||||
Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context;
|
||||
|
||||
DEBUGASSERT((data != NULL) || (length == 0));
|
||||
|
||||
sha512_256_update(ctx, length, (const uint8_t *)data);
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finalise SHA-512/256 calculation, return digest.
|
||||
*
|
||||
* @param context the calculation context
|
||||
* @param[out] digest set to the hash, must be #SHA512_256_DIGEST_SIZE bytes
|
||||
* @return always CURLE_OK
|
||||
*/
|
||||
static CURLcode
|
||||
Curl_sha512_256_finish(unsigned char *digest,
|
||||
void *context)
|
||||
{
|
||||
Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context;
|
||||
|
||||
sha512_256_digest(ctx, (size_t)SHA512_256_DIGEST_SIZE, (uint8_t *)digest);
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
#else /* No system or TLS backend SHA-512/256 implementation available */
|
||||
|
||||
/* Use local implementation */
|
||||
#define HAS_SHA512_256_IMPLEMENTATION 1
|
||||
|
||||
/* ** This implementation of SHA-512/256 hash calculation was originally ** *
|
||||
* ** written by Evgeny Grin (Karlson2k) for GNU libmicrohttpd. ** *
|
||||
* ** The author ported the code to libcurl. The ported code is provided ** *
|
||||
* ** under curl license. ** *
|
||||
* ** This is a minimal version with minimal optimisations. Performance ** *
|
||||
* ** can be significantly improved. Big-endian store and load macros ** *
|
||||
* ** are obvious targets for optimisation. ** */
|
||||
|
||||
#ifdef __GNUC__
|
||||
# if defined(__has_attribute) && defined(__STDC_VERSION__)
|
||||
# if __has_attribute(always_inline) && __STDC_VERSION__ >= 199901
|
||||
# define MHDX_INLINE inline __attribute__((always_inline))
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if !defined(MHDX_INLINE) && \
|
||||
defined(_MSC_VER) && !defined(__GNUC__) && !defined(__clang__)
|
||||
# if _MSC_VER >= 1400
|
||||
# define MHDX_INLINE __forceinline
|
||||
# else
|
||||
# define MHDX_INLINE /* empty */
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if !defined(MHDX_INLINE)
|
||||
# if defined(inline)
|
||||
/* Assume that 'inline' macro was already defined correctly by
|
||||
* the build system. */
|
||||
# define MHDX_INLINE inline
|
||||
# elif defined(__cplusplus)
|
||||
/* The code is compiled with C++ compiler.
|
||||
* C++ always supports 'inline'. */
|
||||
# define MHDX_INLINE inline
|
||||
# elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901
|
||||
/* C99 (and later) supports 'inline' keyword */
|
||||
# define MHDX_INLINE inline
|
||||
# elif defined(__GNUC__) && __GNUC__ >= 3
|
||||
/* GCC supports '__inline__' as an extension */
|
||||
# define MHDX_INLINE __inline__
|
||||
# else
|
||||
# define MHDX_INLINE /* empty */
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Bits manipulation macros and functions.
|
||||
Can be moved to other headers to reuse. */
|
||||
|
||||
#define MHDX_GET_64BIT_BE(ptr) \
|
||||
( ((curl_uint64_t)(((const unsigned char*)(ptr))[0]) << 56) | \
|
||||
((curl_uint64_t)(((const unsigned char*)(ptr))[1]) << 48) | \
|
||||
((curl_uint64_t)(((const unsigned char*)(ptr))[2]) << 40) | \
|
||||
((curl_uint64_t)(((const unsigned char*)(ptr))[3]) << 32) | \
|
||||
((curl_uint64_t)(((const unsigned char*)(ptr))[4]) << 24) | \
|
||||
((curl_uint64_t)(((const unsigned char*)(ptr))[5]) << 16) | \
|
||||
((curl_uint64_t)(((const unsigned char*)(ptr))[6]) << 8) | \
|
||||
(curl_uint64_t)(((const unsigned char*)(ptr))[7]) )
|
||||
|
||||
#define MHDX_PUT_64BIT_BE(ptr,val) do { \
|
||||
((unsigned char*)(ptr))[7]=(unsigned char)((curl_uint64_t)(val)); \
|
||||
((unsigned char*)(ptr))[6]=(unsigned char)(((curl_uint64_t)(val)) >> 8); \
|
||||
((unsigned char*)(ptr))[5]=(unsigned char)(((curl_uint64_t)(val)) >> 16); \
|
||||
((unsigned char*)(ptr))[4]=(unsigned char)(((curl_uint64_t)(val)) >> 24); \
|
||||
((unsigned char*)(ptr))[3]=(unsigned char)(((curl_uint64_t)(val)) >> 32); \
|
||||
((unsigned char*)(ptr))[2]=(unsigned char)(((curl_uint64_t)(val)) >> 40); \
|
||||
((unsigned char*)(ptr))[1]=(unsigned char)(((curl_uint64_t)(val)) >> 48); \
|
||||
((unsigned char*)(ptr))[0]=(unsigned char)(((curl_uint64_t)(val)) >> 56); \
|
||||
} while(0)
|
||||
|
||||
/* Defined as a function. The macro version may duplicate the binary code
|
||||
* size as each argument is used twice, so if any calculation is used
|
||||
* as an argument, the calculation could be done twice. */
|
||||
static MHDX_INLINE curl_uint64_t
|
||||
MHDx_rotr64(curl_uint64_t value, unsigned int bits)
|
||||
{
|
||||
bits %= 64;
|
||||
if(0 == bits)
|
||||
return value;
|
||||
/* Defined in a form which modern compiler could optimise. */
|
||||
return (value >> bits) | (value << (64 - bits));
|
||||
}
|
||||
|
||||
/* SHA-512/256 specific data */
|
||||
|
||||
/**
|
||||
* Number of bits in a single SHA-512/256 word.
|
||||
*/
|
||||
#define SHA512_256_WORD_SIZE_BITS 64
|
||||
|
||||
/**
|
||||
* Number of bytes in a single SHA-512/256 word.
|
||||
*/
|
||||
#define SHA512_256_BYTES_IN_WORD (SHA512_256_WORD_SIZE_BITS / 8)
|
||||
|
||||
/**
|
||||
* Hash is kept internally as 8 64-bit words.
|
||||
* This is the intermediate hash size, used during computing the final digest.
|
||||
*/
|
||||
#define SHA512_256_HASH_SIZE_WORDS 8
|
||||
|
||||
/**
|
||||
* Size of the SHA-512/256 resulting digest in words.
|
||||
* This is the final digest size, not intermediate hash.
|
||||
*/
|
||||
#define SHA512_256_DIGEST_SIZE_WORDS (SHA512_256_HASH_SIZE_WORDS / 2)
|
||||
|
||||
/**
|
||||
* Size of the SHA-512/256 resulting digest in bytes
|
||||
* This is the final digest size, not intermediate hash.
|
||||
*/
|
||||
#define SHA512_256_DIGEST_SIZE \
|
||||
(SHA512_256_DIGEST_SIZE_WORDS * SHA512_256_BYTES_IN_WORD)
|
||||
|
||||
/**
|
||||
* Size of the SHA-512/256 single processing block in bits.
|
||||
*/
|
||||
#define SHA512_256_BLOCK_SIZE_BITS 1024
|
||||
|
||||
/**
|
||||
* Size of the SHA-512/256 single processing block in bytes.
|
||||
*/
|
||||
#define SHA512_256_BLOCK_SIZE (SHA512_256_BLOCK_SIZE_BITS / 8)
|
||||
|
||||
/**
|
||||
* Size of the SHA-512/256 single processing block in words.
|
||||
*/
|
||||
#define SHA512_256_BLOCK_SIZE_WORDS \
|
||||
(SHA512_256_BLOCK_SIZE_BITS / SHA512_256_WORD_SIZE_BITS)
|
||||
|
||||
/**
|
||||
* SHA-512/256 calculation context
|
||||
*/
|
||||
struct mhdx_sha512_256ctx
|
||||
{
|
||||
/**
|
||||
* Intermediate hash value. The variable is properly aligned. Smart
|
||||
* compilers may automatically use fast load/store instruction for big
|
||||
* endian data on little endian machine.
|
||||
*/
|
||||
curl_uint64_t H[SHA512_256_HASH_SIZE_WORDS];
|
||||
/**
|
||||
* SHA-512/256 input data buffer. The buffer is properly aligned. Smart
|
||||
* compilers may automatically use fast load/store instruction for big
|
||||
* endian data on little endian machine.
|
||||
*/
|
||||
curl_uint64_t buffer[SHA512_256_BLOCK_SIZE_WORDS];
|
||||
/**
|
||||
* The number of bytes, lower part
|
||||
*/
|
||||
curl_uint64_t count;
|
||||
/**
|
||||
* The number of bits, high part. Unlike lower part, this counts the number
|
||||
* of bits, not bytes.
|
||||
*/
|
||||
curl_uint64_t count_bits_hi;
|
||||
};
|
||||
|
||||
/**
|
||||
* Context type used for SHA-512/256 calculations
|
||||
*/
|
||||
typedef struct mhdx_sha512_256ctx Curl_sha512_256_ctx;
|
||||
|
||||
|
||||
/**
|
||||
* Initialise structure for SHA-512/256 calculation.
|
||||
*
|
||||
* @param context the calculation context
|
||||
* @return always CURLE_OK
|
||||
*/
|
||||
static CURLcode
|
||||
MHDx_sha512_256_init(void *context)
|
||||
{
|
||||
struct mhdx_sha512_256ctx *const ctx = (struct mhdx_sha512_256ctx *) context;
|
||||
|
||||
/* Check whether the header and this file use the same numbers */
|
||||
DEBUGASSERT(SHA512_256_DIGEST_LENGTH == SHA512_256_DIGEST_SIZE);
|
||||
|
||||
DEBUGASSERT(sizeof(curl_uint64_t) == 8);
|
||||
|
||||
/* Initial hash values, see FIPS PUB 180-4 section 5.3.6.2 */
|
||||
/* Values generated by "IV Generation Function" as described in
|
||||
* section 5.3.6 */
|
||||
ctx->H[0] = CURL_UINT64_C(0x22312194FC2BF72C);
|
||||
ctx->H[1] = CURL_UINT64_C(0x9F555FA3C84C64C2);
|
||||
ctx->H[2] = CURL_UINT64_C(0x2393B86B6F53B151);
|
||||
ctx->H[3] = CURL_UINT64_C(0x963877195940EABD);
|
||||
ctx->H[4] = CURL_UINT64_C(0x96283EE2A88EFFE3);
|
||||
ctx->H[5] = CURL_UINT64_C(0xBE5E1E2553863992);
|
||||
ctx->H[6] = CURL_UINT64_C(0x2B0199FC2C85B8AA);
|
||||
ctx->H[7] = CURL_UINT64_C(0x0EB72DDC81C52CA2);
|
||||
|
||||
/* Initialise number of bytes and high part of number of bits. */
|
||||
ctx->count = CURL_UINT64_C(0);
|
||||
ctx->count_bits_hi = CURL_UINT64_C(0);
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Base of the SHA-512/256 transformation.
|
||||
* Gets a full 128 bytes block of data and updates hash values;
|
||||
* @param H hash values
|
||||
* @param data the data buffer with #SHA512_256_BLOCK_SIZE bytes block
|
||||
*/
|
||||
static void
|
||||
MHDx_sha512_256_transform(curl_uint64_t H[SHA512_256_HASH_SIZE_WORDS],
|
||||
const void *data)
|
||||
{
|
||||
/* Working variables,
|
||||
see FIPS PUB 180-4 section 6.7, 6.4. */
|
||||
curl_uint64_t a = H[0];
|
||||
curl_uint64_t b = H[1];
|
||||
curl_uint64_t c = H[2];
|
||||
curl_uint64_t d = H[3];
|
||||
curl_uint64_t e = H[4];
|
||||
curl_uint64_t f = H[5];
|
||||
curl_uint64_t g = H[6];
|
||||
curl_uint64_t h = H[7];
|
||||
|
||||
/* Data buffer, used as a cyclic buffer.
|
||||
See FIPS PUB 180-4 section 5.2.2, 6.7, 6.4. */
|
||||
curl_uint64_t W[16];
|
||||
|
||||
/* 'Ch' and 'Maj' macro functions are defined with widely-used optimisation.
|
||||
See FIPS PUB 180-4 formulae 4.8, 4.9. */
|
||||
#define Ch(x,y,z) ( (z) ^ ((x) & ((y) ^ (z))) )
|
||||
#define Maj(x,y,z) ( ((x) & (y)) ^ ((z) & ((x) ^ (y))) )
|
||||
|
||||
/* Four 'Sigma' macro functions.
|
||||
See FIPS PUB 180-4 formulae 4.10, 4.11, 4.12, 4.13. */
|
||||
#define SIG0(x) \
|
||||
( MHDx_rotr64((x), 28) ^ MHDx_rotr64((x), 34) ^ MHDx_rotr64((x), 39) )
|
||||
#define SIG1(x) \
|
||||
( MHDx_rotr64((x), 14) ^ MHDx_rotr64((x), 18) ^ MHDx_rotr64((x), 41) )
|
||||
#define sig0(x) \
|
||||
( MHDx_rotr64((x), 1) ^ MHDx_rotr64((x), 8) ^ ((x) >> 7) )
|
||||
#define sig1(x) \
|
||||
( MHDx_rotr64((x), 19) ^ MHDx_rotr64((x), 61) ^ ((x) >> 6) )
|
||||
|
||||
if(1) {
|
||||
unsigned int t;
|
||||
/* K constants array.
|
||||
See FIPS PUB 180-4 section 4.2.3 for K values. */
|
||||
static const curl_uint64_t K[80] = {
|
||||
CURL_UINT64_C(0x428a2f98d728ae22), CURL_UINT64_C(0x7137449123ef65cd),
|
||||
CURL_UINT64_C(0xb5c0fbcfec4d3b2f), CURL_UINT64_C(0xe9b5dba58189dbbc),
|
||||
CURL_UINT64_C(0x3956c25bf348b538), CURL_UINT64_C(0x59f111f1b605d019),
|
||||
CURL_UINT64_C(0x923f82a4af194f9b), CURL_UINT64_C(0xab1c5ed5da6d8118),
|
||||
CURL_UINT64_C(0xd807aa98a3030242), CURL_UINT64_C(0x12835b0145706fbe),
|
||||
CURL_UINT64_C(0x243185be4ee4b28c), CURL_UINT64_C(0x550c7dc3d5ffb4e2),
|
||||
CURL_UINT64_C(0x72be5d74f27b896f), CURL_UINT64_C(0x80deb1fe3b1696b1),
|
||||
CURL_UINT64_C(0x9bdc06a725c71235), CURL_UINT64_C(0xc19bf174cf692694),
|
||||
CURL_UINT64_C(0xe49b69c19ef14ad2), CURL_UINT64_C(0xefbe4786384f25e3),
|
||||
CURL_UINT64_C(0x0fc19dc68b8cd5b5), CURL_UINT64_C(0x240ca1cc77ac9c65),
|
||||
CURL_UINT64_C(0x2de92c6f592b0275), CURL_UINT64_C(0x4a7484aa6ea6e483),
|
||||
CURL_UINT64_C(0x5cb0a9dcbd41fbd4), CURL_UINT64_C(0x76f988da831153b5),
|
||||
CURL_UINT64_C(0x983e5152ee66dfab), CURL_UINT64_C(0xa831c66d2db43210),
|
||||
CURL_UINT64_C(0xb00327c898fb213f), CURL_UINT64_C(0xbf597fc7beef0ee4),
|
||||
CURL_UINT64_C(0xc6e00bf33da88fc2), CURL_UINT64_C(0xd5a79147930aa725),
|
||||
CURL_UINT64_C(0x06ca6351e003826f), CURL_UINT64_C(0x142929670a0e6e70),
|
||||
CURL_UINT64_C(0x27b70a8546d22ffc), CURL_UINT64_C(0x2e1b21385c26c926),
|
||||
CURL_UINT64_C(0x4d2c6dfc5ac42aed), CURL_UINT64_C(0x53380d139d95b3df),
|
||||
CURL_UINT64_C(0x650a73548baf63de), CURL_UINT64_C(0x766a0abb3c77b2a8),
|
||||
CURL_UINT64_C(0x81c2c92e47edaee6), CURL_UINT64_C(0x92722c851482353b),
|
||||
CURL_UINT64_C(0xa2bfe8a14cf10364), CURL_UINT64_C(0xa81a664bbc423001),
|
||||
CURL_UINT64_C(0xc24b8b70d0f89791), CURL_UINT64_C(0xc76c51a30654be30),
|
||||
CURL_UINT64_C(0xd192e819d6ef5218), CURL_UINT64_C(0xd69906245565a910),
|
||||
CURL_UINT64_C(0xf40e35855771202a), CURL_UINT64_C(0x106aa07032bbd1b8),
|
||||
CURL_UINT64_C(0x19a4c116b8d2d0c8), CURL_UINT64_C(0x1e376c085141ab53),
|
||||
CURL_UINT64_C(0x2748774cdf8eeb99), CURL_UINT64_C(0x34b0bcb5e19b48a8),
|
||||
CURL_UINT64_C(0x391c0cb3c5c95a63), CURL_UINT64_C(0x4ed8aa4ae3418acb),
|
||||
CURL_UINT64_C(0x5b9cca4f7763e373), CURL_UINT64_C(0x682e6ff3d6b2b8a3),
|
||||
CURL_UINT64_C(0x748f82ee5defb2fc), CURL_UINT64_C(0x78a5636f43172f60),
|
||||
CURL_UINT64_C(0x84c87814a1f0ab72), CURL_UINT64_C(0x8cc702081a6439ec),
|
||||
CURL_UINT64_C(0x90befffa23631e28), CURL_UINT64_C(0xa4506cebde82bde9),
|
||||
CURL_UINT64_C(0xbef9a3f7b2c67915), CURL_UINT64_C(0xc67178f2e372532b),
|
||||
CURL_UINT64_C(0xca273eceea26619c), CURL_UINT64_C(0xd186b8c721c0c207),
|
||||
CURL_UINT64_C(0xeada7dd6cde0eb1e), CURL_UINT64_C(0xf57d4f7fee6ed178),
|
||||
CURL_UINT64_C(0x06f067aa72176fba), CURL_UINT64_C(0x0a637dc5a2c898a6),
|
||||
CURL_UINT64_C(0x113f9804bef90dae), CURL_UINT64_C(0x1b710b35131c471b),
|
||||
CURL_UINT64_C(0x28db77f523047d84), CURL_UINT64_C(0x32caab7b40c72493),
|
||||
CURL_UINT64_C(0x3c9ebe0a15c9bebc), CURL_UINT64_C(0x431d67c49c100d4c),
|
||||
CURL_UINT64_C(0x4cc5d4becb3e42b6), CURL_UINT64_C(0x597f299cfc657e2a),
|
||||
CURL_UINT64_C(0x5fcb6fab3ad6faec), CURL_UINT64_C(0x6c44198c4a475817)
|
||||
};
|
||||
|
||||
/* One step of SHA-512/256 computation,
|
||||
see FIPS PUB 180-4 section 6.4.2 step 3.
|
||||
* Note: this macro updates working variables in-place, without rotation.
|
||||
* Note: the first (vH += SIG1(vE) + Ch(vE,vF,vG) + kt + wt) equals T1 in
|
||||
FIPS PUB 180-4 section 6.4.2 step 3.
|
||||
the second (vH += SIG0(vA) + Maj(vE,vF,vC) equals T1 + T2 in
|
||||
FIPS PUB 180-4 section 6.4.2 step 3.
|
||||
* Note: 'wt' must be used exactly one time in this macro as macro for
|
||||
'wt' calculation may change other data as well every time when
|
||||
used. */
|
||||
#define SHA2STEP64(vA,vB,vC,vD,vE,vF,vG,vH,kt,wt) do { \
|
||||
(vD) += ((vH) += SIG1 ((vE)) + Ch ((vE),(vF),(vG)) + (kt) + (wt)); \
|
||||
(vH) += SIG0 ((vA)) + Maj ((vA),(vB),(vC)); } while (0)
|
||||
|
||||
/* One step of SHA-512/256 computation with working variables rotation,
|
||||
see FIPS PUB 180-4 section 6.4.2 step 3. This macro version reassigns
|
||||
all working variables on each step. */
|
||||
#define SHA2STEP64RV(vA,vB,vC,vD,vE,vF,vG,vH,kt,wt) do { \
|
||||
curl_uint64_t tmp_h_ = (vH); \
|
||||
SHA2STEP64((vA),(vB),(vC),(vD),(vE),(vF),(vG),tmp_h_,(kt),(wt)); \
|
||||
(vH) = (vG); \
|
||||
(vG) = (vF); \
|
||||
(vF) = (vE); \
|
||||
(vE) = (vD); \
|
||||
(vD) = (vC); \
|
||||
(vC) = (vB); \
|
||||
(vB) = (vA); \
|
||||
(vA) = tmp_h_; } while(0)
|
||||
|
||||
/* Get value of W(t) from input data buffer for 0 <= t <= 15,
|
||||
See FIPS PUB 180-4 section 6.2.
|
||||
Input data must be read in big-endian bytes order,
|
||||
see FIPS PUB 180-4 section 3.1.2. */
|
||||
#define SHA512_GET_W_FROM_DATA(buf,t) \
|
||||
MHDX_GET_64BIT_BE( \
|
||||
((const unsigned char*) (buf)) + (t) * SHA512_256_BYTES_IN_WORD)
|
||||
|
||||
/* During first 16 steps, before making any calculation on each step, the
|
||||
W element is read from the input data buffer as a big-endian value and
|
||||
stored in the array of W elements. */
|
||||
for(t = 0; t < 16; ++t) {
|
||||
SHA2STEP64RV(a, b, c, d, e, f, g, h, K[t], \
|
||||
W[t] = SHA512_GET_W_FROM_DATA(data, t));
|
||||
}
|
||||
|
||||
/* 'W' generation and assignment for 16 <= t <= 79.
|
||||
See FIPS PUB 180-4 section 6.4.2.
|
||||
As only the last 16 'W' are used in calculations, it is possible to
|
||||
use 16 elements array of W as a cyclic buffer.
|
||||
Note: ((t-16) & 15) have same value as (t & 15) */
|
||||
#define Wgen(w,t) \
|
||||
(curl_uint64_t)( (w)[(t - 16) & 15] + sig1((w)[((t) - 2) & 15]) \
|
||||
+ (w)[((t) - 7) & 15] + sig0((w)[((t) - 15) & 15]) )
|
||||
|
||||
/* During the last 64 steps, before making any calculation on each step,
|
||||
current W element is generated from other W elements of the cyclic
|
||||
buffer and the generated value is stored back in the cyclic buffer. */
|
||||
for(t = 16; t < 80; ++t) {
|
||||
SHA2STEP64RV(a, b, c, d, e, f, g, h, K[t], \
|
||||
W[t & 15] = Wgen(W, t));
|
||||
}
|
||||
}
|
||||
|
||||
/* Compute and store the intermediate hash.
|
||||
See FIPS PUB 180-4 section 6.4.2 step 4. */
|
||||
H[0] += a;
|
||||
H[1] += b;
|
||||
H[2] += c;
|
||||
H[3] += d;
|
||||
H[4] += e;
|
||||
H[5] += f;
|
||||
H[6] += g;
|
||||
H[7] += h;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process portion of bytes.
|
||||
*
|
||||
* @param context the calculation context
|
||||
* @param data bytes to add to hash
|
||||
* @param length number of bytes in @a data
|
||||
* @return always CURLE_OK
|
||||
*/
|
||||
static CURLcode
|
||||
MHDx_sha512_256_update(void *context,
|
||||
const unsigned char *data,
|
||||
size_t length)
|
||||
{
|
||||
unsigned int bytes_have; /**< Number of bytes in the context buffer */
|
||||
struct mhdx_sha512_256ctx *const ctx = (struct mhdx_sha512_256ctx *)context;
|
||||
/* the void pointer here is required to mute Intel compiler warning */
|
||||
void *const ctx_buf = ctx->buffer;
|
||||
|
||||
DEBUGASSERT((data != NULL) || (length == 0));
|
||||
|
||||
if(0 == length)
|
||||
return CURLE_OK; /* Shortcut, do nothing */
|
||||
|
||||
/* Note: (count & (SHA512_256_BLOCK_SIZE-1))
|
||||
equals (count % SHA512_256_BLOCK_SIZE) for this block size. */
|
||||
bytes_have = (unsigned int) (ctx->count & (SHA512_256_BLOCK_SIZE - 1));
|
||||
ctx->count += length;
|
||||
if(length > ctx->count)
|
||||
ctx->count_bits_hi += 1U << 3; /* Value wrap */
|
||||
ctx->count_bits_hi += ctx->count >> 61;
|
||||
ctx->count &= CURL_UINT64_C(0x1FFFFFFFFFFFFFFF);
|
||||
|
||||
if(0 != bytes_have) {
|
||||
unsigned int bytes_left = SHA512_256_BLOCK_SIZE - bytes_have;
|
||||
if(length >= bytes_left) {
|
||||
/* Combine new data with data in the buffer and process the full
|
||||
block. */
|
||||
memcpy(((unsigned char *) ctx_buf) + bytes_have,
|
||||
data,
|
||||
bytes_left);
|
||||
data += bytes_left;
|
||||
length -= bytes_left;
|
||||
MHDx_sha512_256_transform(ctx->H, ctx->buffer);
|
||||
bytes_have = 0;
|
||||
}
|
||||
}
|
||||
|
||||
while(SHA512_256_BLOCK_SIZE <= length) {
|
||||
/* Process any full blocks of new data directly,
|
||||
without copying to the buffer. */
|
||||
MHDx_sha512_256_transform(ctx->H, data);
|
||||
data += SHA512_256_BLOCK_SIZE;
|
||||
length -= SHA512_256_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
if(0 != length) {
|
||||
/* Copy incomplete block of new data (if any)
|
||||
to the buffer. */
|
||||
memcpy(((unsigned char *) ctx_buf) + bytes_have, data, length);
|
||||
}
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Size of "length" insertion in bits.
|
||||
* See FIPS PUB 180-4 section 5.1.2.
|
||||
*/
|
||||
#define SHA512_256_SIZE_OF_LEN_ADD_BITS 128
|
||||
|
||||
/**
|
||||
* Size of "length" insertion in bytes.
|
||||
*/
|
||||
#define SHA512_256_SIZE_OF_LEN_ADD (SHA512_256_SIZE_OF_LEN_ADD_BITS / 8)
|
||||
|
||||
/**
|
||||
* Finalise SHA-512/256 calculation, return digest.
|
||||
*
|
||||
* @param context the calculation context
|
||||
* @param[out] digest set to the hash, must be #SHA512_256_DIGEST_SIZE bytes
|
||||
* @return always CURLE_OK
|
||||
*/
|
||||
static CURLcode
|
||||
MHDx_sha512_256_finish(unsigned char *digest,
|
||||
void *context)
|
||||
{
|
||||
struct mhdx_sha512_256ctx *const ctx = (struct mhdx_sha512_256ctx *)context;
|
||||
curl_uint64_t num_bits; /**< Number of processed bits */
|
||||
unsigned int bytes_have; /**< Number of bytes in the context buffer */
|
||||
/* the void pointer here is required to mute Intel compiler warning */
|
||||
void *const ctx_buf = ctx->buffer;
|
||||
|
||||
/* Memorise the number of processed bits.
|
||||
The padding and other data added here during the postprocessing must
|
||||
not change the amount of hashed data. */
|
||||
num_bits = ctx->count << 3;
|
||||
|
||||
/* Note: (count & (SHA512_256_BLOCK_SIZE-1))
|
||||
equals (count % SHA512_256_BLOCK_SIZE) for this block size. */
|
||||
bytes_have = (unsigned int) (ctx->count & (SHA512_256_BLOCK_SIZE - 1));
|
||||
|
||||
/* Input data must be padded with a single bit "1", then with zeros and
|
||||
the finally the length of data in bits must be added as the final bytes
|
||||
of the last block.
|
||||
See FIPS PUB 180-4 section 5.1.2. */
|
||||
|
||||
/* Data is always processed in form of bytes (not by individual bits),
|
||||
therefore position of the first padding bit in byte is always
|
||||
predefined (0x80). */
|
||||
/* Buffer always have space at least for one byte (as full buffers are
|
||||
processed when formed). */
|
||||
((unsigned char *) ctx_buf)[bytes_have++] = 0x80U;
|
||||
|
||||
if(SHA512_256_BLOCK_SIZE - bytes_have < SHA512_256_SIZE_OF_LEN_ADD) {
|
||||
/* No space in the current block to put the total length of message.
|
||||
Pad the current block with zeros and process it. */
|
||||
if(bytes_have < SHA512_256_BLOCK_SIZE)
|
||||
memset(((unsigned char *) ctx_buf) + bytes_have, 0,
|
||||
SHA512_256_BLOCK_SIZE - bytes_have);
|
||||
/* Process the full block. */
|
||||
MHDx_sha512_256_transform(ctx->H, ctx->buffer);
|
||||
/* Start the new block. */
|
||||
bytes_have = 0;
|
||||
}
|
||||
|
||||
/* Pad the rest of the buffer with zeros. */
|
||||
memset(((unsigned char *) ctx_buf) + bytes_have, 0,
|
||||
SHA512_256_BLOCK_SIZE - SHA512_256_SIZE_OF_LEN_ADD - bytes_have);
|
||||
/* Put high part of number of bits in processed message and then lower
|
||||
part of number of bits as big-endian values.
|
||||
See FIPS PUB 180-4 section 5.1.2. */
|
||||
/* Note: the target location is predefined and buffer is always aligned */
|
||||
MHDX_PUT_64BIT_BE(((unsigned char *) ctx_buf) \
|
||||
+ SHA512_256_BLOCK_SIZE \
|
||||
- SHA512_256_SIZE_OF_LEN_ADD, \
|
||||
ctx->count_bits_hi);
|
||||
MHDX_PUT_64BIT_BE(((unsigned char *) ctx_buf) \
|
||||
+ SHA512_256_BLOCK_SIZE \
|
||||
- SHA512_256_SIZE_OF_LEN_ADD \
|
||||
+ SHA512_256_BYTES_IN_WORD, \
|
||||
num_bits);
|
||||
/* Process the full final block. */
|
||||
MHDx_sha512_256_transform(ctx->H, ctx->buffer);
|
||||
|
||||
/* Put in BE mode the leftmost part of the hash as the final digest.
|
||||
See FIPS PUB 180-4 section 6.7. */
|
||||
|
||||
MHDX_PUT_64BIT_BE((digest + 0 * SHA512_256_BYTES_IN_WORD), ctx->H[0]);
|
||||
MHDX_PUT_64BIT_BE((digest + 1 * SHA512_256_BYTES_IN_WORD), ctx->H[1]);
|
||||
MHDX_PUT_64BIT_BE((digest + 2 * SHA512_256_BYTES_IN_WORD), ctx->H[2]);
|
||||
MHDX_PUT_64BIT_BE((digest + 3 * SHA512_256_BYTES_IN_WORD), ctx->H[3]);
|
||||
|
||||
/* Erase potentially sensitive data. */
|
||||
memset(ctx, 0, sizeof(struct mhdx_sha512_256ctx));
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/* Map to the local implementation */
|
||||
#define Curl_sha512_256_init MHDx_sha512_256_init
|
||||
#define Curl_sha512_256_update MHDx_sha512_256_update
|
||||
#define Curl_sha512_256_finish MHDx_sha512_256_finish
|
||||
|
||||
#endif /* Local SHA-512/256 code */
|
||||
|
||||
|
||||
/**
|
||||
* Compute SHA-512/256 hash for the given data in one function call
|
||||
* @param[out] output the pointer to put the hash
|
||||
* @param[in] input the pointer to the data to process
|
||||
* @param input_size the size of the data pointed by @a input
|
||||
* @return always #CURLE_OK
|
||||
*/
|
||||
CURLcode
|
||||
Curl_sha512_256it(unsigned char *output, const unsigned char *input,
|
||||
size_t input_size)
|
||||
{
|
||||
Curl_sha512_256_ctx ctx;
|
||||
CURLcode res;
|
||||
|
||||
res = Curl_sha512_256_init(&ctx);
|
||||
if(res != CURLE_OK)
|
||||
return res;
|
||||
|
||||
res = Curl_sha512_256_update(&ctx, (const void *) input, input_size);
|
||||
|
||||
if(res != CURLE_OK) {
|
||||
(void) Curl_sha512_256_finish(output, &ctx);
|
||||
return res;
|
||||
}
|
||||
|
||||
return Curl_sha512_256_finish(output, &ctx);
|
||||
}
|
||||
|
||||
/* Wrapper function, takes 'unsigned int' as length type, returns void */
|
||||
static void
|
||||
Curl_sha512_256_update_i(void *context,
|
||||
const unsigned char *data,
|
||||
unsigned int length)
|
||||
{
|
||||
/* Hypothetically the function may fail, but assume it does not */
|
||||
(void) Curl_sha512_256_update(context, data, length);
|
||||
}
|
||||
|
||||
/* Wrapper function, returns void */
|
||||
static void
|
||||
Curl_sha512_256_finish_v(unsigned char *result,
|
||||
void *context)
|
||||
{
|
||||
/* Hypothetically the function may fail, but assume it does not */
|
||||
(void) Curl_sha512_256_finish(result, context);
|
||||
}
|
||||
|
||||
/* Wrapper function, takes 'unsigned int' as length type, returns void */
|
||||
|
||||
const struct HMAC_params Curl_HMAC_SHA512_256[] = {
|
||||
{
|
||||
/* Initialize context procedure. */
|
||||
Curl_sha512_256_init,
|
||||
/* Update context with data. */
|
||||
Curl_sha512_256_update_i,
|
||||
/* Get final result procedure. */
|
||||
Curl_sha512_256_finish_v,
|
||||
/* Context structure size. */
|
||||
sizeof(Curl_sha512_256_ctx),
|
||||
/* Maximum key length (bytes). */
|
||||
SHA512_256_BLOCK_SIZE,
|
||||
/* Result length (bytes). */
|
||||
SHA512_256_DIGEST_SIZE
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* !CURL_DISABLE_DIGEST_AUTH && !CURL_DISABLE_SHA512_256 */
|
|
@ -0,0 +1,44 @@
|
|||
#ifndef HEADER_CURL_SHA512_256_H
|
||||
#define HEADER_CURL_SHA512_256_H
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) Evgeny Grin (Karlson2k), <k2k@narod.ru>.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at https://curl.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#if !defined(CURL_DISABLE_DIGEST_AUTH) && !defined(CURL_DISABLE_SHA512_256)
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include "curl_hmac.h"
|
||||
|
||||
#define CURL_HAVE_SHA512_256
|
||||
|
||||
extern const struct HMAC_params Curl_HMAC_SHA512_256[1];
|
||||
|
||||
#define SHA512_256_DIGEST_LENGTH 32
|
||||
|
||||
CURLcode
|
||||
Curl_sha512_256it(unsigned char *output, const unsigned char *input,
|
||||
size_t input_size);
|
||||
|
||||
#endif /* !CURL_DISABLE_DIGEST_AUTH && !CURL_DISABLE_SHA512_256 */
|
||||
|
||||
#endif /* HEADER_CURL_SHA256_H */
|
|
@ -36,6 +36,7 @@
|
|||
|
||||
#include "cf-socket.h"
|
||||
#include "connect.h"
|
||||
#include "doh.h"
|
||||
#include "http2.h"
|
||||
#include "http_proxy.h"
|
||||
#include "cf-h1-proxy.h"
|
||||
|
@ -113,12 +114,14 @@ void Curl_failf(struct Curl_easy *data, const char *fmt, ...)
|
|||
void Curl_infof(struct Curl_easy *data, const char *fmt, ...)
|
||||
{
|
||||
DEBUGASSERT(!strchr(fmt, '\n'));
|
||||
if(data && data->set.verbose) {
|
||||
if(Curl_trc_is_verbose(data)) {
|
||||
va_list ap;
|
||||
int len;
|
||||
int len = 0;
|
||||
char buffer[MAXINFO + 2];
|
||||
if(data->state.feat)
|
||||
len = msnprintf(buffer, MAXINFO, "[%s] ", data->state.feat->name);
|
||||
va_start(ap, fmt);
|
||||
len = mvsnprintf(buffer, MAXINFO, fmt, ap);
|
||||
len += mvsnprintf(buffer + len, MAXINFO - len, fmt, ap);
|
||||
va_end(ap);
|
||||
buffer[len++] = '\n';
|
||||
buffer[len] = '\0';
|
||||
|
@ -132,9 +135,16 @@ void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf,
|
|||
DEBUGASSERT(cf);
|
||||
if(Curl_trc_cf_is_verbose(cf, data)) {
|
||||
va_list ap;
|
||||
int len;
|
||||
int len = 0;
|
||||
char buffer[MAXINFO + 2];
|
||||
len = msnprintf(buffer, MAXINFO, "[%s] ", cf->cft->name);
|
||||
if(data->state.feat)
|
||||
len += msnprintf(buffer + len, MAXINFO - len, "[%s] ",
|
||||
data->state.feat->name);
|
||||
if(cf->sockindex)
|
||||
len += msnprintf(buffer + len, MAXINFO - len, "[%s-%d] ",
|
||||
cf->cft->name, cf->sockindex);
|
||||
else
|
||||
len += msnprintf(buffer + len, MAXINFO - len, "[%s] ", cf->cft->name);
|
||||
va_start(ap, fmt);
|
||||
len += mvsnprintf(buffer + len, MAXINFO - len, fmt, ap);
|
||||
va_end(ap);
|
||||
|
@ -144,6 +154,12 @@ void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf,
|
|||
}
|
||||
}
|
||||
|
||||
static struct curl_trc_feat *trc_feats[] = {
|
||||
#ifndef CURL_DISABLE_DOH
|
||||
&Curl_doh_trc,
|
||||
#endif
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct Curl_cftype *cf_types[] = {
|
||||
&Curl_cft_tcp,
|
||||
|
@ -215,6 +231,15 @@ CURLcode Curl_trc_opt(const char *config)
|
|||
break;
|
||||
}
|
||||
}
|
||||
for(i = 0; trc_feats[i]; ++i) {
|
||||
if(strcasecompare(token, "all")) {
|
||||
trc_feats[i]->log_level = lvl;
|
||||
}
|
||||
else if(strcasecompare(token, trc_feats[i]->name)) {
|
||||
trc_feats[i]->log_level = lvl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
token = strtok_r(NULL, ", ", &tok_buf);
|
||||
}
|
||||
free(tmp);
|
||||
|
|
|
@ -86,10 +86,21 @@ void Curl_failf(struct Curl_easy *data,
|
|||
#ifndef CURL_DISABLE_VERBOSE_STRINGS
|
||||
/* informational messages enabled */
|
||||
|
||||
#define Curl_trc_is_verbose(data) ((data) && (data)->set.verbose)
|
||||
struct curl_trc_feat {
|
||||
const char *name;
|
||||
int log_level;
|
||||
};
|
||||
|
||||
#define Curl_trc_is_verbose(data) \
|
||||
((data) && (data)->set.verbose && \
|
||||
(!(data)->state.feat || \
|
||||
((data)->state.feat->log_level >= CURL_LOG_LVL_INFO)))
|
||||
#define Curl_trc_cf_is_verbose(cf, data) \
|
||||
((data) && (data)->set.verbose && \
|
||||
(cf) && (cf)->cft->log_level >= CURL_LOG_LVL_INFO)
|
||||
(Curl_trc_is_verbose(data) && \
|
||||
(cf) && (cf)->cft->log_level >= CURL_LOG_LVL_INFO)
|
||||
#define Curl_trc_ft_is_verbose(data, ft) \
|
||||
(Curl_trc_is_verbose(data) && \
|
||||
(ft)->log_level >= CURL_LOG_LVL_INFO)
|
||||
|
||||
/**
|
||||
* Output an informational message when transfer's verbose logging is enabled.
|
||||
|
@ -109,6 +120,7 @@ void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf,
|
|||
|
||||
#define Curl_trc_is_verbose(d) ((void)(d), FALSE)
|
||||
#define Curl_trc_cf_is_verbose(x,y) ((void)(x), (void)(y), FALSE)
|
||||
#define Curl_trc_ft_is_verbose(x,y) ((void)(x), (void)(y), FALSE)
|
||||
|
||||
static void Curl_infof(struct Curl_easy *data, const char *fmt, ...)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,437 @@
|
|||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at https://curl.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#include "curl_setup.h"
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include "urldata.h"
|
||||
#include "cfilters.h"
|
||||
#include "headers.h"
|
||||
#include "multiif.h"
|
||||
#include "sendf.h"
|
||||
#include "cw-out.h"
|
||||
|
||||
/* The last 3 #include files should be in this order */
|
||||
#include "curl_printf.h"
|
||||
#include "curl_memory.h"
|
||||
#include "memdebug.h"
|
||||
|
||||
|
||||
/**
|
||||
* OVERALL DESIGN of this client writer
|
||||
*
|
||||
* The 'cw-out' writer is supposed to be the last writer in a transfer's
|
||||
* stack. It is always added when that stack is initialized. Its purpose
|
||||
* is to pass BODY and HEADER bytes to the client-installed callback
|
||||
* functions.
|
||||
*
|
||||
* These callback may return `CURL_WRITEFUNC_PAUSE` to indicate that the
|
||||
* data had not been written and the whole transfer should stop receiving
|
||||
* new data. Or at least, stop calling the functions. When the transfer
|
||||
* is "unpaused" by the client, the previous data shall be passed as
|
||||
* if nothing happened.
|
||||
*
|
||||
* The `cw-out` writer therefore manages buffers for bytes that could
|
||||
* not be written. Data that was already in flight from the server also
|
||||
* needs buffering on paused transfer when it arrives.
|
||||
*
|
||||
* In addition, the writer allows buffering of "small" body writes,
|
||||
* so client functions are called less often. That is only enabled on a
|
||||
* number of conditions.
|
||||
*
|
||||
* HEADER and BODY data may arrive in any order. For paused transfers,
|
||||
* a list of `struct cw_out_buf` is kept for `cw_out_type` types. The
|
||||
* list may be: [BODY]->[HEADER]->[BODY]->[HEADER]....
|
||||
* When unpausing, this list is "played back" to the client callbacks.
|
||||
*
|
||||
* The amount of bytes being buffered is limited by `DYN_PAUSE_BUFFER`
|
||||
* and when that is exceeded `CURLE_TOO_LARGE` is returned as error.
|
||||
*/
|
||||
typedef enum {
|
||||
CW_OUT_NONE,
|
||||
CW_OUT_BODY,
|
||||
CW_OUT_HDS
|
||||
} cw_out_type;
|
||||
|
||||
struct cw_out_buf {
|
||||
struct cw_out_buf *next;
|
||||
struct dynbuf b;
|
||||
cw_out_type type;
|
||||
};
|
||||
|
||||
static struct cw_out_buf *cw_out_buf_create(cw_out_type otype)
|
||||
{
|
||||
struct cw_out_buf *cwbuf = calloc(1, sizeof(*cwbuf));
|
||||
if(cwbuf) {
|
||||
cwbuf->type = otype;
|
||||
Curl_dyn_init(&cwbuf->b, DYN_PAUSE_BUFFER);
|
||||
}
|
||||
return cwbuf;
|
||||
}
|
||||
|
||||
static void cw_out_buf_free(struct cw_out_buf *cwbuf)
|
||||
{
|
||||
if(cwbuf) {
|
||||
Curl_dyn_free(&cwbuf->b);
|
||||
free(cwbuf);
|
||||
}
|
||||
}
|
||||
|
||||
struct cw_out_ctx {
|
||||
struct Curl_cwriter super;
|
||||
struct cw_out_buf *buf;
|
||||
};
|
||||
|
||||
static CURLcode cw_out_write(struct Curl_easy *data,
|
||||
struct Curl_cwriter *writer, int type,
|
||||
const char *buf, size_t nbytes);
|
||||
static void cw_out_close(struct Curl_easy *data, struct Curl_cwriter *writer);
|
||||
static CURLcode cw_out_init(struct Curl_easy *data,
|
||||
struct Curl_cwriter *writer);
|
||||
|
||||
struct Curl_cwtype Curl_cwt_out = {
|
||||
"cw-out",
|
||||
NULL,
|
||||
cw_out_init,
|
||||
cw_out_write,
|
||||
cw_out_close,
|
||||
sizeof(struct cw_out_ctx)
|
||||
};
|
||||
|
||||
static CURLcode cw_out_init(struct Curl_easy *data,
|
||||
struct Curl_cwriter *writer)
|
||||
{
|
||||
struct cw_out_ctx *ctx = writer->ctx;
|
||||
(void)data;
|
||||
ctx->buf = NULL;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static void cw_out_bufs_free(struct cw_out_ctx *ctx)
|
||||
{
|
||||
while(ctx->buf) {
|
||||
struct cw_out_buf *next = ctx->buf->next;
|
||||
cw_out_buf_free(ctx->buf);
|
||||
ctx->buf = next;
|
||||
}
|
||||
}
|
||||
|
||||
static size_t cw_out_bufs_len(struct cw_out_ctx *ctx)
|
||||
{
|
||||
struct cw_out_buf *cwbuf = ctx->buf;
|
||||
size_t len = 0;
|
||||
while(cwbuf) {
|
||||
len += Curl_dyn_len(&cwbuf->b);
|
||||
cwbuf = cwbuf->next;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static void cw_out_close(struct Curl_easy *data, struct Curl_cwriter *writer)
|
||||
{
|
||||
struct cw_out_ctx *ctx = writer->ctx;
|
||||
|
||||
(void)data;
|
||||
cw_out_bufs_free(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current curl_write_callback and user_data for the buf type
|
||||
*/
|
||||
static void cw_get_writefunc(struct Curl_easy *data, cw_out_type otype,
|
||||
curl_write_callback *pwcb, void **pwcb_data,
|
||||
size_t *pmax_write, size_t *pmin_write)
|
||||
{
|
||||
switch(otype) {
|
||||
case CW_OUT_BODY:
|
||||
*pwcb = data->set.fwrite_func;
|
||||
*pwcb_data = data->set.out;
|
||||
*pmax_write = CURL_MAX_WRITE_SIZE;
|
||||
/* if we ever want buffering of BODY output, we can set `min_write`
|
||||
* the preferred size. The default should always be to pass data
|
||||
* to the client as it comes without delay */
|
||||
*pmin_write = 0;
|
||||
break;
|
||||
case CW_OUT_HDS:
|
||||
*pwcb = data->set.fwrite_header? data->set.fwrite_header :
|
||||
(data->set.writeheader? data->set.fwrite_func : NULL);
|
||||
*pwcb_data = data->set.writeheader;
|
||||
*pmax_write = 0; /* do not chunk-write headers, write them as they are */
|
||||
*pmin_write = 0;
|
||||
break;
|
||||
default:
|
||||
*pwcb = NULL;
|
||||
*pwcb_data = NULL;
|
||||
*pmax_write = CURL_MAX_WRITE_SIZE;
|
||||
*pmin_write = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static CURLcode cw_out_ptr_flush(struct cw_out_ctx *ctx,
|
||||
struct Curl_easy *data,
|
||||
cw_out_type otype,
|
||||
bool flush_all,
|
||||
const char *buf, size_t blen,
|
||||
size_t *pconsumed)
|
||||
{
|
||||
curl_write_callback wcb;
|
||||
void *wcb_data;
|
||||
size_t max_write, min_write;
|
||||
size_t wlen, nwritten;
|
||||
|
||||
(void)ctx;
|
||||
/* write callbacks may get NULLed by the client between calls. */
|
||||
cw_get_writefunc(data, otype, &wcb, &wcb_data, &max_write, &min_write);
|
||||
if(!wcb) {
|
||||
*pconsumed = blen;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
*pconsumed = 0;
|
||||
while(blen && !(data->req.keepon & KEEP_RECV_PAUSE)) {
|
||||
if(!flush_all && blen < min_write)
|
||||
break;
|
||||
wlen = max_write? CURLMIN(blen, max_write) : blen;
|
||||
Curl_set_in_callback(data, TRUE);
|
||||
nwritten = wcb((char *)buf, 1, wlen, wcb_data);
|
||||
Curl_set_in_callback(data, FALSE);
|
||||
if(CURL_WRITEFUNC_PAUSE == nwritten) {
|
||||
if(data->conn && data->conn->handler->flags & PROTOPT_NONETWORK) {
|
||||
/* Protocols that work without network cannot be paused. This is
|
||||
actually only FILE:// just now, and it can't pause since the
|
||||
transfer isn't done using the "normal" procedure. */
|
||||
failf(data, "Write callback asked for PAUSE when not supported");
|
||||
return CURLE_WRITE_ERROR;
|
||||
}
|
||||
/* mark the connection as RECV paused */
|
||||
data->req.keepon |= KEEP_RECV_PAUSE;
|
||||
break;
|
||||
}
|
||||
if(nwritten != wlen) {
|
||||
failf(data, "Failure writing output to destination, "
|
||||
"passed %zu returned %zd", wlen, nwritten);
|
||||
return CURLE_WRITE_ERROR;
|
||||
}
|
||||
*pconsumed += nwritten;
|
||||
blen -= nwritten;
|
||||
buf += nwritten;
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static CURLcode cw_out_buf_flush(struct cw_out_ctx *ctx,
|
||||
struct Curl_easy *data,
|
||||
struct cw_out_buf *cwbuf,
|
||||
bool flush_all)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
if(Curl_dyn_len(&cwbuf->b)) {
|
||||
size_t consumed;
|
||||
|
||||
result = cw_out_ptr_flush(ctx, data, cwbuf->type, flush_all,
|
||||
Curl_dyn_ptr(&cwbuf->b),
|
||||
Curl_dyn_len(&cwbuf->b),
|
||||
&consumed);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
if(consumed) {
|
||||
if(consumed == Curl_dyn_len(&cwbuf->b)) {
|
||||
Curl_dyn_free(&cwbuf->b);
|
||||
}
|
||||
else {
|
||||
DEBUGASSERT(consumed < Curl_dyn_len(&cwbuf->b));
|
||||
result = Curl_dyn_tail(&cwbuf->b, Curl_dyn_len(&cwbuf->b) - consumed);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode cw_out_flush_chain(struct cw_out_ctx *ctx,
|
||||
struct Curl_easy *data,
|
||||
struct cw_out_buf **pcwbuf,
|
||||
bool flush_all)
|
||||
{
|
||||
struct cw_out_buf *cwbuf = *pcwbuf;
|
||||
CURLcode result;
|
||||
|
||||
if(!cwbuf)
|
||||
return CURLE_OK;
|
||||
if(data->req.keepon & KEEP_RECV_PAUSE)
|
||||
return CURLE_OK;
|
||||
|
||||
/* write the end of the chain until it blocks or gets empty */
|
||||
while(cwbuf->next) {
|
||||
struct cw_out_buf **plast = &cwbuf->next;
|
||||
while((*plast)->next)
|
||||
plast = &(*plast)->next;
|
||||
result = cw_out_flush_chain(ctx, data, plast, flush_all);
|
||||
if(result)
|
||||
return result;
|
||||
if(*plast) {
|
||||
/* could not write last, paused again? */
|
||||
DEBUGASSERT(data->req.keepon & KEEP_RECV_PAUSE);
|
||||
return CURLE_OK;
|
||||
}
|
||||
}
|
||||
|
||||
result = cw_out_buf_flush(ctx, data, cwbuf, flush_all);
|
||||
if(result)
|
||||
return result;
|
||||
if(!Curl_dyn_len(&cwbuf->b)) {
|
||||
cw_out_buf_free(cwbuf);
|
||||
*pcwbuf = NULL;
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static CURLcode cw_out_append(struct cw_out_ctx *ctx,
|
||||
cw_out_type otype,
|
||||
const char *buf, size_t blen)
|
||||
{
|
||||
if(cw_out_bufs_len(ctx) + blen > DYN_PAUSE_BUFFER)
|
||||
return CURLE_TOO_LARGE;
|
||||
|
||||
/* if we do not have a buffer, or it is of another type, make a new one.
|
||||
* And for CW_OUT_HDS always make a new one, so we "replay" headers
|
||||
* exactly as they came in */
|
||||
if(!ctx->buf || (ctx->buf->type != otype) || (otype == CW_OUT_HDS)) {
|
||||
struct cw_out_buf *cwbuf = cw_out_buf_create(otype);
|
||||
if(!cwbuf)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
cwbuf->next = ctx->buf;
|
||||
ctx->buf = cwbuf;
|
||||
}
|
||||
DEBUGASSERT(ctx->buf && (ctx->buf->type == otype));
|
||||
return Curl_dyn_addn(&ctx->buf->b, buf, blen);
|
||||
}
|
||||
|
||||
static CURLcode cw_out_do_write(struct cw_out_ctx *ctx,
|
||||
struct Curl_easy *data,
|
||||
cw_out_type otype,
|
||||
bool flush_all,
|
||||
const char *buf, size_t blen)
|
||||
{
|
||||
CURLcode result;
|
||||
|
||||
/* if we have buffered data and it is a different type than what
|
||||
* we are writing now, try to flush all */
|
||||
if(ctx->buf && ctx->buf->type != otype) {
|
||||
result = cw_out_flush_chain(ctx, data, &ctx->buf, TRUE);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
|
||||
if(ctx->buf) {
|
||||
/* still have buffered data, append and flush */
|
||||
result = cw_out_append(ctx, otype, buf, blen);
|
||||
if(result)
|
||||
return result;
|
||||
result = cw_out_flush_chain(ctx, data, &ctx->buf, flush_all);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
/* nothing buffered, try direct write */
|
||||
size_t consumed;
|
||||
result = cw_out_ptr_flush(ctx, data, otype, flush_all,
|
||||
buf, blen, &consumed);
|
||||
if(result)
|
||||
return result;
|
||||
if(consumed < blen) {
|
||||
/* did not write all, append the rest */
|
||||
result = cw_out_append(ctx, otype, buf + consumed, blen - consumed);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static CURLcode cw_out_write(struct Curl_easy *data,
|
||||
struct Curl_cwriter *writer, int type,
|
||||
const char *buf, size_t blen)
|
||||
{
|
||||
struct cw_out_ctx *ctx = writer->ctx;
|
||||
CURLcode result;
|
||||
bool flush_all;
|
||||
|
||||
flush_all = (type & CLIENTWRITE_EOS)? TRUE:FALSE;
|
||||
if((type & CLIENTWRITE_BODY) ||
|
||||
((type & CLIENTWRITE_HEADER) && data->set.include_header)) {
|
||||
result = cw_out_do_write(ctx, data, CW_OUT_BODY, flush_all, buf, blen);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
|
||||
if(type & (CLIENTWRITE_HEADER|CLIENTWRITE_INFO)) {
|
||||
result = cw_out_do_write(ctx, data, CW_OUT_HDS, flush_all, buf, blen);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
bool Curl_cw_out_is_paused(struct Curl_easy *data)
|
||||
{
|
||||
struct Curl_cwriter *cw_out;
|
||||
struct cw_out_ctx *ctx;
|
||||
|
||||
cw_out = Curl_cwriter_get_by_type(data, &Curl_cwt_out);
|
||||
if(!cw_out)
|
||||
return FALSE;
|
||||
|
||||
ctx = (struct cw_out_ctx *)cw_out;
|
||||
return cw_out_bufs_len(ctx) > 0;
|
||||
}
|
||||
|
||||
static CURLcode cw_out_flush(struct Curl_easy *data, bool flush_all)
|
||||
{
|
||||
struct Curl_cwriter *cw_out;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
cw_out = Curl_cwriter_get_by_type(data, &Curl_cwt_out);
|
||||
if(cw_out) {
|
||||
struct cw_out_ctx *ctx = (struct cw_out_ctx *)cw_out;
|
||||
|
||||
result = cw_out_flush_chain(ctx, data, &ctx->buf, flush_all);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
CURLcode Curl_cw_out_flush(struct Curl_easy *data)
|
||||
{
|
||||
return cw_out_flush(data, FALSE);
|
||||
}
|
||||
|
||||
CURLcode Curl_cw_out_done(struct Curl_easy *data)
|
||||
{
|
||||
return cw_out_flush(data, TRUE);
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
#ifndef HEADER_CURL_CW_OUT_H
|
||||
#define HEADER_CURL_CW_OUT_H
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at https://curl.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#include "curl_setup.h"
|
||||
|
||||
#include "sendf.h"
|
||||
|
||||
/**
|
||||
* The client writer type "cw-out" that does the actual writing to
|
||||
* the client callbacks. Intended to be the last installed in the
|
||||
* client writer stack of a transfer.
|
||||
*/
|
||||
extern struct Curl_cwtype Curl_cwt_out;
|
||||
|
||||
/**
|
||||
* Return TRUE iff 'cw-out' client write has paused data.
|
||||
*/
|
||||
bool Curl_cw_out_is_paused(struct Curl_easy *data);
|
||||
|
||||
/**
|
||||
* Flush any buffered date to the client, chunk collation still applies.
|
||||
*/
|
||||
CURLcode Curl_cw_out_flush(struct Curl_easy *data);
|
||||
|
||||
/**
|
||||
* Mark EndOfStream reached and flush ALL data to the client.
|
||||
*/
|
||||
CURLcode Curl_cw_out_done(struct Curl_easy *data);
|
||||
|
||||
#endif /* HEADER_CURL_CW_OUT_H */
|
25
lib/dict.c
25
lib/dict.c
|
@ -122,13 +122,12 @@ static char *unescape_word(const char *input)
|
|||
}
|
||||
|
||||
/* sendf() sends formatted data to the server */
|
||||
static CURLcode sendf(curl_socket_t sockfd, struct Curl_easy *data,
|
||||
const char *fmt, ...) CURL_PRINTF(3, 4);
|
||||
static CURLcode sendf(struct Curl_easy *data,
|
||||
const char *fmt, ...) CURL_PRINTF(2, 3);
|
||||
|
||||
static CURLcode sendf(curl_socket_t sockfd, struct Curl_easy *data,
|
||||
const char *fmt, ...)
|
||||
static CURLcode sendf(struct Curl_easy *data, const char *fmt, ...)
|
||||
{
|
||||
ssize_t bytes_written;
|
||||
size_t bytes_written;
|
||||
size_t write_len;
|
||||
CURLcode result = CURLE_OK;
|
||||
char *s;
|
||||
|
@ -146,7 +145,7 @@ static CURLcode sendf(curl_socket_t sockfd, struct Curl_easy *data,
|
|||
|
||||
for(;;) {
|
||||
/* Write the buffer to the socket */
|
||||
result = Curl_write(data, sockfd, sptr, write_len, &bytes_written);
|
||||
result = Curl_xfer_send(data, sptr, write_len, &bytes_written);
|
||||
|
||||
if(result)
|
||||
break;
|
||||
|
@ -178,8 +177,6 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
|
|||
char *nthdef = NULL; /* This is not part of the protocol, but required
|
||||
by RFC 2229 */
|
||||
CURLcode result;
|
||||
struct connectdata *conn = data->conn;
|
||||
curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
|
||||
|
||||
char *path;
|
||||
|
||||
|
@ -228,7 +225,7 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
|
|||
goto error;
|
||||
}
|
||||
|
||||
result = sendf(sockfd, data,
|
||||
result = sendf(data,
|
||||
"CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n"
|
||||
"MATCH "
|
||||
"%s " /* database */
|
||||
|
@ -243,7 +240,7 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
|
|||
failf(data, "Failed sending DICT request");
|
||||
goto error;
|
||||
}
|
||||
Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1); /* no upload */
|
||||
Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1); /* no upload */
|
||||
}
|
||||
else if(strncasecompare(path, DICT_DEFINE, sizeof(DICT_DEFINE)-1) ||
|
||||
strncasecompare(path, DICT_DEFINE2, sizeof(DICT_DEFINE2)-1) ||
|
||||
|
@ -276,7 +273,7 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
|
|||
goto error;
|
||||
}
|
||||
|
||||
result = sendf(sockfd, data,
|
||||
result = sendf(data,
|
||||
"CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n"
|
||||
"DEFINE "
|
||||
"%s " /* database */
|
||||
|
@ -289,7 +286,7 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
|
|||
failf(data, "Failed sending DICT request");
|
||||
goto error;
|
||||
}
|
||||
Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
|
||||
Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1);
|
||||
}
|
||||
else {
|
||||
|
||||
|
@ -302,7 +299,7 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
|
|||
if(ppath[i] == ':')
|
||||
ppath[i] = ' ';
|
||||
}
|
||||
result = sendf(sockfd, data,
|
||||
result = sendf(data,
|
||||
"CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n"
|
||||
"%s\r\n"
|
||||
"QUIT\r\n", ppath);
|
||||
|
@ -311,7 +308,7 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
|
|||
goto error;
|
||||
}
|
||||
|
||||
Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
|
||||
Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
33
lib/doh.c
33
lib/doh.c
|
@ -69,7 +69,12 @@ static const char *doh_strerror(DOHcode code)
|
|||
return errors[code];
|
||||
return "bad error code";
|
||||
}
|
||||
#endif
|
||||
|
||||
struct curl_trc_feat Curl_doh_trc = {
|
||||
"DoH",
|
||||
CURL_LOG_LVL_NONE,
|
||||
};
|
||||
#endif /* !CURL_DISABLE_VERBOSE_STRINGS */
|
||||
|
||||
/* @unittest 1655
|
||||
*/
|
||||
|
@ -189,9 +194,9 @@ static int doh_done(struct Curl_easy *doh, CURLcode result)
|
|||
struct dohdata *dohp = data->req.doh;
|
||||
/* so one of the DoH request done for the 'data' transfer is now complete! */
|
||||
dohp->pending--;
|
||||
infof(data, "a DoH request is completed, %u to go", dohp->pending);
|
||||
infof(doh, "a DoH request is completed, %u to go", dohp->pending);
|
||||
if(result)
|
||||
infof(data, "DoH request %s", curl_easy_strerror(result));
|
||||
infof(doh, "DoH request %s", curl_easy_strerror(result));
|
||||
|
||||
if(!dohp->pending) {
|
||||
/* DoH completed */
|
||||
|
@ -242,6 +247,9 @@ static CURLcode dohprobe(struct Curl_easy *data,
|
|||
the gcc typecheck helpers */
|
||||
struct dynbuf *resp = &p->serverdoh;
|
||||
doh->state.internal = true;
|
||||
#ifndef CURL_DISABLE_VERBOSE_STRINGS
|
||||
doh->state.feat = &Curl_doh_trc;
|
||||
#endif
|
||||
ERROR_CHECK_SETOPT(CURLOPT_URL, url);
|
||||
ERROR_CHECK_SETOPT(CURLOPT_DEFAULT_PROTOCOL, "https");
|
||||
ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb);
|
||||
|
@ -264,7 +272,7 @@ static CURLcode dohprobe(struct Curl_easy *data,
|
|||
ERROR_CHECK_SETOPT(CURLOPT_SHARE, data->share);
|
||||
if(data->set.err && data->set.err != stderr)
|
||||
ERROR_CHECK_SETOPT(CURLOPT_STDERR, data->set.err);
|
||||
if(data->set.verbose)
|
||||
if(Curl_trc_ft_is_verbose(data, &Curl_doh_trc))
|
||||
ERROR_CHECK_SETOPT(CURLOPT_VERBOSE, 1L);
|
||||
if(data->set.no_signal)
|
||||
ERROR_CHECK_SETOPT(CURLOPT_NOSIGNAL, 1L);
|
||||
|
@ -741,11 +749,11 @@ static void showdoh(struct Curl_easy *data,
|
|||
const struct dohentry *d)
|
||||
{
|
||||
int i;
|
||||
infof(data, "TTL: %u seconds", d->ttl);
|
||||
infof(data, "[DoH] TTL: %u seconds", d->ttl);
|
||||
for(i = 0; i < d->numaddr; i++) {
|
||||
const struct dohaddr *a = &d->addr[i];
|
||||
if(a->type == DNS_TYPE_A) {
|
||||
infof(data, "DoH A: %u.%u.%u.%u",
|
||||
infof(data, "[DoH] A: %u.%u.%u.%u",
|
||||
a->ip.v4[0], a->ip.v4[1],
|
||||
a->ip.v4[2], a->ip.v4[3]);
|
||||
}
|
||||
|
@ -754,9 +762,9 @@ static void showdoh(struct Curl_easy *data,
|
|||
char buffer[128];
|
||||
char *ptr;
|
||||
size_t len;
|
||||
msnprintf(buffer, 128, "DoH AAAA: ");
|
||||
ptr = &buffer[10];
|
||||
len = 118;
|
||||
len = msnprintf(buffer, 128, "[DoH] AAAA: ");
|
||||
ptr = &buffer[len];
|
||||
len = sizeof(buffer) - len;
|
||||
for(j = 0; j < 16; j += 2) {
|
||||
size_t l;
|
||||
msnprintf(ptr, len, "%s%02x%02x", j?":":"", d->addr[i].ip.v6[j],
|
||||
|
@ -950,8 +958,11 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
|
|||
struct Curl_dns_entry *dns;
|
||||
struct Curl_addrinfo *ai;
|
||||
|
||||
infof(data, "DoH Host name: %s", dohp->host);
|
||||
showdoh(data, &de);
|
||||
|
||||
if(Curl_trc_ft_is_verbose(data, &Curl_doh_trc)) {
|
||||
infof(data, "[DoH] Host name: %s", dohp->host);
|
||||
showdoh(data, &de);
|
||||
}
|
||||
|
||||
result = doh2ai(&de, dohp->host, dohp->port, &ai);
|
||||
if(result) {
|
||||
|
|
|
@ -120,6 +120,8 @@ void de_init(struct dohentry *d);
|
|||
void de_cleanup(struct dohentry *d);
|
||||
#endif
|
||||
|
||||
extern struct curl_trc_feat Curl_doh_trc;
|
||||
|
||||
#else /* if DoH is disabled */
|
||||
#define Curl_doh(a,b,c,d) NULL
|
||||
#define Curl_doh_is_resolved(x,y) CURLE_COULDNT_RESOLVE_HOST
|
||||
|
|
70
lib/easy.c
70
lib/easy.c
|
@ -58,6 +58,7 @@
|
|||
#include "multiif.h"
|
||||
#include "select.h"
|
||||
#include "cfilters.h"
|
||||
#include "cw-out.h"
|
||||
#include "sendf.h" /* for failf function prototype */
|
||||
#include "connect.h" /* for Curl_getconnectinfo */
|
||||
#include "slist.h"
|
||||
|
@ -741,7 +742,6 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events)
|
|||
multi = Curl_multi_handle(1, 3, 7);
|
||||
if(!multi)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
data->multi_easy = multi;
|
||||
}
|
||||
|
||||
if(multi->in_callback)
|
||||
|
@ -750,15 +750,18 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events)
|
|||
/* Copy the MAXCONNECTS option to the multi handle */
|
||||
curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, (long)data->set.maxconnects);
|
||||
|
||||
data->multi_easy = NULL; /* pretend it does not exist */
|
||||
mcode = curl_multi_add_handle(multi, data);
|
||||
if(mcode) {
|
||||
curl_multi_cleanup(multi);
|
||||
data->multi_easy = NULL;
|
||||
if(mcode == CURLM_OUT_OF_MEMORY)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
return CURLE_FAILED_INIT;
|
||||
}
|
||||
|
||||
/* assign this after curl_multi_add_handle() */
|
||||
data->multi_easy = multi;
|
||||
|
||||
sigpipe_ignore(data, &pipe_st);
|
||||
|
||||
/* run the transfer */
|
||||
|
@ -1021,7 +1024,6 @@ fail:
|
|||
#ifndef CURL_DISABLE_COOKIES
|
||||
free(outcurl->cookies);
|
||||
#endif
|
||||
free(outcurl->state.buffer);
|
||||
Curl_dyn_free(&outcurl->state.headerb);
|
||||
Curl_altsvc_cleanup(&outcurl->asi);
|
||||
Curl_hsts_cleanup(&outcurl->hsts);
|
||||
|
@ -1038,7 +1040,7 @@ fail:
|
|||
*/
|
||||
void curl_easy_reset(struct Curl_easy *data)
|
||||
{
|
||||
Curl_free_request_state(data);
|
||||
Curl_req_hard_reset(&data->req, data);
|
||||
|
||||
/* zero out UserDefined data: */
|
||||
Curl_freeset(data);
|
||||
|
@ -1108,9 +1110,10 @@ CURLcode curl_easy_pause(struct Curl_easy *data, int action)
|
|||
/* Unpause parts in active mime tree. */
|
||||
if((k->keepon & ~newstate & KEEP_SEND_PAUSE) &&
|
||||
(data->mstate == MSTATE_PERFORMING ||
|
||||
data->mstate == MSTATE_RATELIMITING) &&
|
||||
data->state.fread_func == (curl_read_callback) Curl_mime_read) {
|
||||
Curl_mime_unpause(data->state.in);
|
||||
data->mstate == MSTATE_RATELIMITING)) {
|
||||
result = Curl_creader_unpause(data);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
|
||||
/* put it back in the keepon */
|
||||
|
@ -1118,21 +1121,11 @@ CURLcode curl_easy_pause(struct Curl_easy *data, int action)
|
|||
|
||||
if(!(newstate & KEEP_RECV_PAUSE)) {
|
||||
Curl_conn_ev_data_pause(data, FALSE);
|
||||
result = Curl_client_unpause(data);
|
||||
result = Curl_cw_out_flush(data);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef USE_HYPER
|
||||
if(!(newstate & KEEP_SEND_PAUSE)) {
|
||||
/* need to wake the send body waker */
|
||||
if(data->hyp.send_body_waker) {
|
||||
hyper_waker_wake(data->hyp.send_body_waker);
|
||||
data->hyp.send_body_waker = NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* if there's no error and we're not pausing both directions, we want
|
||||
to have this handle checked soon */
|
||||
if((newstate & (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) !=
|
||||
|
@ -1142,7 +1135,7 @@ CURLcode curl_easy_pause(struct Curl_easy *data, int action)
|
|||
/* reset the too-slow time keeper */
|
||||
data->state.keeps_speed.tv_sec = 0;
|
||||
|
||||
if(!data->state.tempcount)
|
||||
if(!Curl_cw_out_is_paused(data))
|
||||
/* if not pausing again, force a recv/send check of this connection as
|
||||
the data might've been read off the socket already */
|
||||
data->state.select_bits = CURL_CSELECT_IN | CURL_CSELECT_OUT;
|
||||
|
@ -1166,9 +1159,11 @@ CURLcode curl_easy_pause(struct Curl_easy *data, int action)
|
|||
}
|
||||
|
||||
|
||||
static CURLcode easy_connection(struct Curl_easy *data, curl_socket_t *sfd,
|
||||
static CURLcode easy_connection(struct Curl_easy *data,
|
||||
struct connectdata **connp)
|
||||
{
|
||||
curl_socket_t sfd;
|
||||
|
||||
if(!data)
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
|
||||
|
@ -1178,9 +1173,9 @@ static CURLcode easy_connection(struct Curl_easy *data, curl_socket_t *sfd,
|
|||
return CURLE_UNSUPPORTED_PROTOCOL;
|
||||
}
|
||||
|
||||
*sfd = Curl_getconnectinfo(data, connp);
|
||||
sfd = Curl_getconnectinfo(data, connp);
|
||||
|
||||
if(*sfd == CURL_SOCKET_BAD) {
|
||||
if(sfd == CURL_SOCKET_BAD) {
|
||||
failf(data, "Failed to get recent socket");
|
||||
return CURLE_UNSUPPORTED_PROTOCOL;
|
||||
}
|
||||
|
@ -1196,7 +1191,6 @@ static CURLcode easy_connection(struct Curl_easy *data, curl_socket_t *sfd,
|
|||
CURLcode curl_easy_recv(struct Curl_easy *data, void *buffer, size_t buflen,
|
||||
size_t *n)
|
||||
{
|
||||
curl_socket_t sfd;
|
||||
CURLcode result;
|
||||
ssize_t n1;
|
||||
struct connectdata *c;
|
||||
|
@ -1204,7 +1198,7 @@ CURLcode curl_easy_recv(struct Curl_easy *data, void *buffer, size_t buflen,
|
|||
if(Curl_is_in_callback(data))
|
||||
return CURLE_RECURSIVE_API_CALL;
|
||||
|
||||
result = easy_connection(data, &sfd, &c);
|
||||
result = easy_connection(data, &c);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
|
@ -1214,7 +1208,7 @@ CURLcode curl_easy_recv(struct Curl_easy *data, void *buffer, size_t buflen,
|
|||
Curl_attach_connection(data, c);
|
||||
|
||||
*n = 0;
|
||||
result = Curl_read(data, sfd, buffer, buflen, &n1);
|
||||
result = Curl_conn_recv(data, FIRSTSOCKET, buffer, buflen, &n1);
|
||||
|
||||
if(result)
|
||||
return result;
|
||||
|
@ -1226,11 +1220,10 @@ CURLcode curl_easy_recv(struct Curl_easy *data, void *buffer, size_t buflen,
|
|||
#ifdef USE_WEBSOCKETS
|
||||
CURLcode Curl_connect_only_attach(struct Curl_easy *data)
|
||||
{
|
||||
curl_socket_t sfd;
|
||||
CURLcode result;
|
||||
struct connectdata *c = NULL;
|
||||
|
||||
result = easy_connection(data, &sfd, &c);
|
||||
result = easy_connection(data, &c);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
|
@ -1249,15 +1242,14 @@ CURLcode Curl_connect_only_attach(struct Curl_easy *data)
|
|||
* This is the private internal version of curl_easy_send()
|
||||
*/
|
||||
CURLcode Curl_senddata(struct Curl_easy *data, const void *buffer,
|
||||
size_t buflen, ssize_t *n)
|
||||
size_t buflen, size_t *n)
|
||||
{
|
||||
curl_socket_t sfd;
|
||||
CURLcode result;
|
||||
ssize_t n1;
|
||||
struct connectdata *c = NULL;
|
||||
SIGPIPE_VARIABLE(pipe_st);
|
||||
|
||||
result = easy_connection(data, &sfd, &c);
|
||||
*n = 0;
|
||||
result = easy_connection(data, &c);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
|
@ -1266,20 +1258,12 @@ CURLcode Curl_senddata(struct Curl_easy *data, const void *buffer,
|
|||
needs to be reattached */
|
||||
Curl_attach_connection(data, c);
|
||||
|
||||
*n = 0;
|
||||
sigpipe_ignore(data, &pipe_st);
|
||||
result = Curl_write(data, sfd, buffer, buflen, &n1);
|
||||
result = Curl_conn_send(data, FIRSTSOCKET, buffer, buflen, n);
|
||||
sigpipe_restore(&pipe_st);
|
||||
|
||||
if(n1 == -1)
|
||||
if(result && result != CURLE_AGAIN)
|
||||
return CURLE_SEND_ERROR;
|
||||
|
||||
/* detect EAGAIN */
|
||||
if(!result && !n1)
|
||||
return CURLE_AGAIN;
|
||||
|
||||
*n = n1;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -1290,13 +1274,13 @@ CURLcode Curl_senddata(struct Curl_easy *data, const void *buffer,
|
|||
CURLcode curl_easy_send(struct Curl_easy *data, const void *buffer,
|
||||
size_t buflen, size_t *n)
|
||||
{
|
||||
ssize_t written = 0;
|
||||
size_t written = 0;
|
||||
CURLcode result;
|
||||
if(Curl_is_in_callback(data))
|
||||
return CURLE_RECURSIVE_API_CALL;
|
||||
|
||||
result = Curl_senddata(data, buffer, buflen, &written);
|
||||
*n = (size_t)written;
|
||||
*n = written;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* | (__| |_| | _ <| |___
|
||||
* ___|___/|_| ______|
|
||||
*
|
||||
* Copyright (C) Daniel Stenberg, <daniel.se>, et al.
|
||||
* Copyright (C) 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
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
* Prototypes for library-wide functions provided by easy.c
|
||||
*/
|
||||
CURLcode Curl_senddata(struct Curl_easy *data, const void *buffer,
|
||||
size_t buflen, ssize_t *n);
|
||||
size_t buflen, size_t *n);
|
||||
|
||||
#ifdef USE_WEBSOCKETS
|
||||
CURLcode Curl_connect_only_attach(struct Curl_easy *data);
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) Daniel Stenberg, <daniel.se>, et al.
|
||||
* Copyright (C) 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
|
||||
|
|
52
lib/file.c
52
lib/file.c
|
@ -59,6 +59,7 @@
|
|||
#include "file.h"
|
||||
#include "speedcheck.h"
|
||||
#include "getinfo.h"
|
||||
#include "multiif.h"
|
||||
#include "transfer.h"
|
||||
#include "url.h"
|
||||
#include "parsedate.h" /* for the week day and month names */
|
||||
|
@ -290,10 +291,12 @@ static CURLcode file_upload(struct Curl_easy *data)
|
|||
int fd;
|
||||
int mode;
|
||||
CURLcode result = CURLE_OK;
|
||||
char buffer[8*1024], *uphere_save;
|
||||
char *xfer_ulbuf;
|
||||
size_t xfer_ulblen;
|
||||
curl_off_t bytecount = 0;
|
||||
struct_stat file_stat;
|
||||
const char *sendbuf;
|
||||
bool eos = FALSE;
|
||||
|
||||
/*
|
||||
* Since FILE: doesn't do the full init, we need to provide some extra
|
||||
|
@ -337,15 +340,16 @@ static CURLcode file_upload(struct Curl_easy *data)
|
|||
data->state.resume_from = (curl_off_t)file_stat.st_size;
|
||||
}
|
||||
|
||||
/* Yikes! Curl_fillreadbuffer uses data->req.upload_fromhere to READ
|
||||
* client data to! Please, someone fix... */
|
||||
uphere_save = data->req.upload_fromhere;
|
||||
while(!result) {
|
||||
result = Curl_multi_xfer_ulbuf_borrow(data, &xfer_ulbuf, &xfer_ulblen);
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
while(!result && !eos) {
|
||||
size_t nread;
|
||||
ssize_t nwrite;
|
||||
size_t readcount;
|
||||
data->req.upload_fromhere = buffer;
|
||||
result = Curl_fillreadbuffer(data, sizeof(buffer), &readcount);
|
||||
|
||||
result = Curl_client_read(data, xfer_ulbuf, xfer_ulblen, &readcount, &eos);
|
||||
if(result)
|
||||
break;
|
||||
|
||||
|
@ -359,16 +363,16 @@ static CURLcode file_upload(struct Curl_easy *data)
|
|||
if((curl_off_t)nread <= data->state.resume_from) {
|
||||
data->state.resume_from -= nread;
|
||||
nread = 0;
|
||||
sendbuf = buffer;
|
||||
sendbuf = xfer_ulbuf;
|
||||
}
|
||||
else {
|
||||
sendbuf = buffer + data->state.resume_from;
|
||||
sendbuf = xfer_ulbuf + data->state.resume_from;
|
||||
nread -= (size_t)data->state.resume_from;
|
||||
data->state.resume_from = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
sendbuf = buffer;
|
||||
sendbuf = xfer_ulbuf;
|
||||
|
||||
/* write the data to the target */
|
||||
nwrite = write(fd, sendbuf, nread);
|
||||
|
@ -389,8 +393,9 @@ static CURLcode file_upload(struct Curl_easy *data)
|
|||
if(!result && Curl_pgrsUpdate(data))
|
||||
result = CURLE_ABORTED_BY_CALLBACK;
|
||||
|
||||
out:
|
||||
close(fd);
|
||||
data->req.upload_fromhere = uphere_save;
|
||||
Curl_multi_xfer_ulbuf_release(data, xfer_ulbuf);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -419,6 +424,8 @@ static CURLcode file_do(struct Curl_easy *data, bool *done)
|
|||
bool fstated = FALSE;
|
||||
int fd;
|
||||
struct FILEPROTO *file;
|
||||
char *xfer_buf;
|
||||
size_t xfer_blen;
|
||||
|
||||
*done = TRUE; /* unconditionally */
|
||||
|
||||
|
@ -541,25 +548,26 @@ static CURLcode file_do(struct Curl_easy *data, bool *done)
|
|||
return CURLE_BAD_DOWNLOAD_RESUME;
|
||||
}
|
||||
|
||||
Curl_pgrsTime(data, TIMER_STARTTRANSFER);
|
||||
result = Curl_multi_xfer_buf_borrow(data, &xfer_buf, &xfer_blen);
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
while(!result) {
|
||||
char tmpbuf[8*1024];
|
||||
ssize_t nread;
|
||||
/* Don't fill a whole buffer if we want less than all data */
|
||||
size_t bytestoread;
|
||||
|
||||
if(size_known) {
|
||||
bytestoread = (expected_size < (curl_off_t)(sizeof(tmpbuf)-1)) ?
|
||||
curlx_sotouz(expected_size) : (sizeof(tmpbuf)-1);
|
||||
bytestoread = (expected_size < (curl_off_t)(xfer_blen-1)) ?
|
||||
curlx_sotouz(expected_size) : (xfer_blen-1);
|
||||
}
|
||||
else
|
||||
bytestoread = sizeof(tmpbuf)-1;
|
||||
bytestoread = xfer_blen-1;
|
||||
|
||||
nread = read(fd, tmpbuf, bytestoread);
|
||||
nread = read(fd, xfer_buf, bytestoread);
|
||||
|
||||
if(nread > 0)
|
||||
tmpbuf[nread] = 0;
|
||||
xfer_buf[nread] = 0;
|
||||
|
||||
if(nread <= 0 || (size_known && (expected_size == 0)))
|
||||
break;
|
||||
|
@ -567,18 +575,22 @@ static CURLcode file_do(struct Curl_easy *data, bool *done)
|
|||
if(size_known)
|
||||
expected_size -= nread;
|
||||
|
||||
result = Curl_client_write(data, CLIENTWRITE_BODY, tmpbuf, nread);
|
||||
result = Curl_client_write(data, CLIENTWRITE_BODY, xfer_buf, nread);
|
||||
if(result)
|
||||
return result;
|
||||
goto out;
|
||||
|
||||
if(Curl_pgrsUpdate(data))
|
||||
result = CURLE_ABORTED_BY_CALLBACK;
|
||||
else
|
||||
result = Curl_speedcheck(data, Curl_now());
|
||||
if(result)
|
||||
goto out;
|
||||
}
|
||||
if(Curl_pgrsUpdate(data))
|
||||
result = CURLE_ABORTED_BY_CALLBACK;
|
||||
|
||||
out:
|
||||
Curl_multi_xfer_buf_release(data, xfer_buf);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -129,7 +129,12 @@ CURLcode Curl_fopen(struct Curl_easy *data, const char *filename,
|
|||
}
|
||||
|
||||
result = CURLE_WRITE_ERROR;
|
||||
#if (defined(ANDROID) || defined(__ANDROID__)) && \
|
||||
(defined(__i386__) || defined(__arm__))
|
||||
fd = open(tempstore, O_WRONLY | O_CREAT | O_EXCL, (mode_t)(0600|sb.st_mode));
|
||||
#else
|
||||
fd = open(tempstore, O_WRONLY | O_CREAT | O_EXCL, 0600|sb.st_mode);
|
||||
#endif
|
||||
if(fd == -1)
|
||||
goto fail;
|
||||
|
||||
|
|
218
lib/ftp.c
218
lib/ftp.c
|
@ -85,6 +85,14 @@
|
|||
#define INET_ADDRSTRLEN 16
|
||||
#endif
|
||||
|
||||
/* macro to check for a three-digit ftp status code at the start of the
|
||||
given string */
|
||||
#define STATUSCODE(line) (ISDIGIT(line[0]) && ISDIGIT(line[1]) && \
|
||||
ISDIGIT(line[2]))
|
||||
|
||||
/* macro to check for the last line in an FTP server response */
|
||||
#define LASTLINE(line) (STATUSCODE(line) && (' ' == line[3]))
|
||||
|
||||
#ifdef CURL_DISABLE_VERBOSE_STRINGS
|
||||
#define ftp_pasv_verbose(a,b,c,d) Curl_nop_stmt
|
||||
#endif
|
||||
|
@ -143,7 +151,7 @@ static CURLcode wc_statemach(struct Curl_easy *data);
|
|||
static void wc_data_dtor(void *ptr);
|
||||
static CURLcode ftp_state_retr(struct Curl_easy *data, curl_off_t filesize);
|
||||
static CURLcode ftp_readresp(struct Curl_easy *data,
|
||||
curl_socket_t sockfd,
|
||||
int sockindex,
|
||||
struct pingpong *pp,
|
||||
int *ftpcode,
|
||||
size_t *size);
|
||||
|
@ -247,6 +255,98 @@ static void freedirs(struct ftp_conn *ftpc)
|
|||
Curl_safefree(ftpc->newhost);
|
||||
}
|
||||
|
||||
#ifdef CURL_DO_LINEEND_CONV
|
||||
/***********************************************************************
|
||||
*
|
||||
* Lineend Conversions
|
||||
* On ASCII transfers, e.g. directory listings, we might get lines
|
||||
* ending in '\r\n' and we prefer just '\n'.
|
||||
* We might also get a lonely '\r' which we convert into a '\n'.
|
||||
*/
|
||||
struct ftp_cw_lc_ctx {
|
||||
struct Curl_cwriter super;
|
||||
bool newline_pending;
|
||||
};
|
||||
|
||||
static CURLcode ftp_cw_lc_write(struct Curl_easy *data,
|
||||
struct Curl_cwriter *writer, int type,
|
||||
const char *buf, size_t blen)
|
||||
{
|
||||
static const char nl = '\n';
|
||||
struct ftp_cw_lc_ctx *ctx = writer->ctx;
|
||||
|
||||
if(!(type & CLIENTWRITE_BODY) ||
|
||||
data->conn->proto.ftpc.transfertype != 'A')
|
||||
return Curl_cwriter_write(data, writer->next, type, buf, blen);
|
||||
|
||||
/* ASCII mode BODY data, convert lineends */
|
||||
while(blen) {
|
||||
/* do not pass EOS when writing parts */
|
||||
int chunk_type = (type & ~CLIENTWRITE_EOS);
|
||||
const char *cp;
|
||||
size_t chunk_len;
|
||||
CURLcode result;
|
||||
|
||||
if(ctx->newline_pending) {
|
||||
if(buf[0] != '\n') {
|
||||
/* previous chunk ended in '\r' and we do not see a '\n' in this one,
|
||||
* need to write a newline. */
|
||||
result = Curl_cwriter_write(data, writer->next, chunk_type, &nl, 1);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
/* either we just wrote the newline or it is part of the next
|
||||
* chunk of bytes we write. */
|
||||
data->state.crlf_conversions++;
|
||||
ctx->newline_pending = FALSE;
|
||||
}
|
||||
|
||||
cp = memchr(buf, '\r', blen);
|
||||
if(!cp)
|
||||
break;
|
||||
|
||||
/* write the bytes before the '\r', excluding the '\r' */
|
||||
chunk_len = cp - buf;
|
||||
if(chunk_len) {
|
||||
result = Curl_cwriter_write(data, writer->next, chunk_type,
|
||||
buf, chunk_len);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
/* skip the '\r', we now have a newline pending */
|
||||
buf = cp + 1;
|
||||
blen = blen - chunk_len - 1;
|
||||
ctx->newline_pending = TRUE;
|
||||
}
|
||||
|
||||
/* Any remaining data does not contain a '\r' */
|
||||
if(blen) {
|
||||
DEBUGASSERT(!ctx->newline_pending);
|
||||
return Curl_cwriter_write(data, writer->next, type, buf, blen);
|
||||
}
|
||||
else if(type & CLIENTWRITE_EOS) {
|
||||
/* EndOfStream, if we have a trailing cr, now is the time to write it */
|
||||
if(ctx->newline_pending) {
|
||||
ctx->newline_pending = FALSE;
|
||||
data->state.crlf_conversions++;
|
||||
return Curl_cwriter_write(data, writer->next, type, &nl, 1);
|
||||
}
|
||||
/* Always pass on the EOS type indicator */
|
||||
return Curl_cwriter_write(data, writer->next, type, buf, 0);
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static const struct Curl_cwtype ftp_cw_lc = {
|
||||
"ftp-lineconv",
|
||||
NULL,
|
||||
Curl_cwriter_def_init,
|
||||
ftp_cw_lc_write,
|
||||
Curl_cwriter_def_close,
|
||||
sizeof(struct ftp_cw_lc_ctx)
|
||||
};
|
||||
|
||||
#endif /* CURL_DO_LINEEND_CONV */
|
||||
/***********************************************************************
|
||||
*
|
||||
* AcceptServerConnect()
|
||||
|
@ -412,8 +512,32 @@ static CURLcode ReceivedServerConnect(struct Curl_easy *data, bool *received)
|
|||
}
|
||||
if(response) {
|
||||
infof(data, "Ctrl conn has data while waiting for data conn");
|
||||
if(pp->overflow > 3) {
|
||||
char *r = Curl_dyn_ptr(&pp->recvbuf);
|
||||
|
||||
DEBUGASSERT((pp->overflow + pp->nfinal) <=
|
||||
Curl_dyn_len(&pp->recvbuf));
|
||||
/* move over the most recently handled response line */
|
||||
r += pp->nfinal;
|
||||
|
||||
if(LASTLINE(r)) {
|
||||
int status = curlx_sltosi(strtol(r, NULL, 10));
|
||||
if(status == 226) {
|
||||
/* funny timing situation where we get the final message on the
|
||||
control connection before traffic on the data connection has been
|
||||
noticed. Leave the 226 in there and use this as a trigger to read
|
||||
the data socket. */
|
||||
infof(data, "Got 226 before data activity");
|
||||
*received = TRUE;
|
||||
return CURLE_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(void)Curl_GetFTPResponse(data, &nread, &ftpcode);
|
||||
|
||||
infof(data, "FTP code: %03d", ftpcode);
|
||||
|
||||
if(ftpcode/100 > 3)
|
||||
return CURLE_FTP_ACCEPT_FAILED;
|
||||
|
||||
|
@ -457,12 +581,12 @@ static CURLcode InitiateTransfer(struct Curl_easy *data)
|
|||
/* set the SO_SNDBUF for the secondary socket for those who need it */
|
||||
Curl_sndbufset(conn->sock[SECONDARYSOCKET]);
|
||||
|
||||
Curl_setup_transfer(data, -1, -1, FALSE, SECONDARYSOCKET);
|
||||
Curl_xfer_setup(data, -1, -1, FALSE, SECONDARYSOCKET);
|
||||
}
|
||||
else {
|
||||
/* FTP download: */
|
||||
Curl_setup_transfer(data, SECONDARYSOCKET,
|
||||
conn->proto.ftpc.retr_size_saved, FALSE, -1);
|
||||
Curl_xfer_setup(data, SECONDARYSOCKET,
|
||||
conn->proto.ftpc.retr_size_saved, FALSE, -1);
|
||||
}
|
||||
|
||||
conn->proto.ftpc.pp.pending_resp = TRUE; /* expect server response */
|
||||
|
@ -525,14 +649,6 @@ out:
|
|||
return result;
|
||||
}
|
||||
|
||||
/* macro to check for a three-digit ftp status code at the start of the
|
||||
given string */
|
||||
#define STATUSCODE(line) (ISDIGIT(line[0]) && ISDIGIT(line[1]) && \
|
||||
ISDIGIT(line[2]))
|
||||
|
||||
/* macro to check for the last line in an FTP server response */
|
||||
#define LASTLINE(line) (STATUSCODE(line) && (' ' == line[3]))
|
||||
|
||||
static bool ftp_endofresp(struct Curl_easy *data, struct connectdata *conn,
|
||||
char *line, size_t len, int *code)
|
||||
{
|
||||
|
@ -548,13 +664,13 @@ static bool ftp_endofresp(struct Curl_easy *data, struct connectdata *conn,
|
|||
}
|
||||
|
||||
static CURLcode ftp_readresp(struct Curl_easy *data,
|
||||
curl_socket_t sockfd,
|
||||
int sockindex,
|
||||
struct pingpong *pp,
|
||||
int *ftpcode, /* return the ftp-code if done */
|
||||
size_t *size) /* size of the response */
|
||||
{
|
||||
int code;
|
||||
CURLcode result = Curl_pp_readresp(data, sockfd, pp, &code, size);
|
||||
CURLcode result = Curl_pp_readresp(data, sockindex, pp, &code, size);
|
||||
|
||||
#ifdef HAVE_GSSAPI
|
||||
{
|
||||
|
@ -689,7 +805,7 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data,
|
|||
break;
|
||||
}
|
||||
}
|
||||
result = ftp_readresp(data, sockfd, pp, ftpcode, &nread);
|
||||
result = ftp_readresp(data, FIRSTSOCKET, pp, ftpcode, &nread);
|
||||
if(result)
|
||||
break;
|
||||
|
||||
|
@ -821,24 +937,18 @@ static int ftp_domore_getsock(struct Curl_easy *data,
|
|||
* remote site, or we could wait for that site to connect to us. Or just
|
||||
* handle ordinary commands.
|
||||
*/
|
||||
|
||||
DEBUGF(infof(data, "ftp_domore_getsock()"));
|
||||
if(conn->cfilter[SECONDARYSOCKET]
|
||||
&& !Curl_conn_is_connected(conn, SECONDARYSOCKET))
|
||||
return 0;
|
||||
|
||||
if(FTP_STOP == ftpc->state) {
|
||||
int bits = GETSOCK_READSOCK(0);
|
||||
|
||||
/* if stopped and still in this state, then we're also waiting for a
|
||||
connect on the secondary connection */
|
||||
DEBUGASSERT(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD ||
|
||||
(conn->cfilter[SECONDARYSOCKET] &&
|
||||
!Curl_conn_is_connected(conn, SECONDARYSOCKET)));
|
||||
socks[0] = conn->sock[FIRSTSOCKET];
|
||||
if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) {
|
||||
socks[1] = conn->sock[SECONDARYSOCKET];
|
||||
bits |= GETSOCK_WRITESOCK(1) | GETSOCK_READSOCK(1);
|
||||
}
|
||||
|
||||
return bits;
|
||||
/* An unconnected SECONDARY will add its socket by itself
|
||||
* via its adjust_pollset() */
|
||||
return GETSOCK_READSOCK(0);
|
||||
}
|
||||
return Curl_pp_getsock(data, &conn->proto.ftpc.pp, socks);
|
||||
}
|
||||
|
@ -1179,6 +1289,12 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
|
|||
conn->bits.ftp_use_eprt = TRUE;
|
||||
#endif
|
||||
|
||||
/* Replace any filter on SECONDARY with one listening on this socket */
|
||||
result = Curl_conn_tcp_listen_set(data, conn, SECONDARYSOCKET, &portsock);
|
||||
if(result)
|
||||
goto out;
|
||||
portsock = CURL_SOCKET_BAD; /* now held in filter */
|
||||
|
||||
for(; fcmd != DONE; fcmd++) {
|
||||
|
||||
if(!conn->bits.ftp_use_eprt && (EPRT == fcmd))
|
||||
|
@ -1252,11 +1368,6 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
|
|||
/* store which command was sent */
|
||||
ftpc->count1 = fcmd;
|
||||
|
||||
/* Replace any filter on SECONDARY with one listening on this socket */
|
||||
result = Curl_conn_tcp_listen_set(data, conn, SECONDARYSOCKET, &portsock);
|
||||
if(result)
|
||||
goto out;
|
||||
portsock = CURL_SOCKET_BAD; /* now held in filter */
|
||||
ftp_state(data, FTP_PORT);
|
||||
|
||||
out:
|
||||
|
@ -1572,10 +1683,10 @@ static CURLcode ftp_state_ul_setup(struct Curl_easy *data,
|
|||
append = TRUE;
|
||||
|
||||
/* Let's read off the proper amount of bytes from the input. */
|
||||
if(conn->seek_func) {
|
||||
if(data->set.seek_func) {
|
||||
Curl_set_in_callback(data, true);
|
||||
seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
|
||||
SEEK_SET);
|
||||
seekerr = data->set.seek_func(data->set.seek_client,
|
||||
data->state.resume_from, SEEK_SET);
|
||||
Curl_set_in_callback(data, false);
|
||||
}
|
||||
|
||||
|
@ -1614,7 +1725,7 @@ static CURLcode ftp_state_ul_setup(struct Curl_easy *data,
|
|||
infof(data, "File already completely uploaded");
|
||||
|
||||
/* no data to transfer */
|
||||
Curl_setup_transfer(data, -1, -1, FALSE, -1);
|
||||
Curl_xfer_setup(data, -1, -1, FALSE, -1);
|
||||
|
||||
/* Set ->transfer so that we won't get any error in
|
||||
* ftp_done() because we didn't transfer anything! */
|
||||
|
@ -1792,7 +1903,7 @@ static char *control_address(struct connectdata *conn)
|
|||
if(conn->bits.tunnel_proxy || conn->bits.socksproxy)
|
||||
return conn->host.name;
|
||||
#endif
|
||||
return conn->primary_ip;
|
||||
return conn->primary.remote_ip;
|
||||
}
|
||||
|
||||
static bool match_pasv_6nums(const char *p,
|
||||
|
@ -1929,14 +2040,14 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
|
|||
*/
|
||||
const char * const host_name = conn->bits.socksproxy ?
|
||||
conn->socks_proxy.host.name : conn->http_proxy.host.name;
|
||||
rc = Curl_resolv(data, host_name, conn->port, FALSE, &addr);
|
||||
rc = Curl_resolv(data, host_name, conn->primary.remote_port, FALSE, &addr);
|
||||
if(rc == CURLRESOLV_PENDING)
|
||||
/* BLOCKING, ignores the return code but 'addr' will be NULL in
|
||||
case of failure */
|
||||
(void)Curl_resolver_wait_resolv(data, &addr);
|
||||
|
||||
connectport =
|
||||
(unsigned short)conn->port; /* we connect to the proxy's port */
|
||||
/* we connect to the proxy's port */
|
||||
connectport = (unsigned short)conn->primary.remote_port;
|
||||
|
||||
if(!addr) {
|
||||
failf(data, "Can't resolve proxy host %s:%hu", host_name, connectport);
|
||||
|
@ -2285,7 +2396,7 @@ static CURLcode ftp_state_retr(struct Curl_easy *data,
|
|||
|
||||
if(ftp->downloadsize == 0) {
|
||||
/* no data to transfer */
|
||||
Curl_setup_transfer(data, -1, -1, FALSE, -1);
|
||||
Curl_xfer_setup(data, -1, -1, FALSE, -1);
|
||||
infof(data, "File already completely downloaded");
|
||||
|
||||
/* Set ->transfer so that we won't get any error in ftp_done()
|
||||
|
@ -2692,7 +2803,6 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
|
|||
struct connectdata *conn)
|
||||
{
|
||||
CURLcode result;
|
||||
curl_socket_t sock = conn->sock[FIRSTSOCKET];
|
||||
int ftpcode;
|
||||
struct ftp_conn *ftpc = &conn->proto.ftpc;
|
||||
struct pingpong *pp = &ftpc->pp;
|
||||
|
@ -2702,7 +2812,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
|
|||
if(pp->sendleft)
|
||||
return Curl_pp_flushsend(data, pp);
|
||||
|
||||
result = ftp_readresp(data, sock, pp, &ftpcode, &nread);
|
||||
result = ftp_readresp(data, FIRSTSOCKET, pp, &ftpcode, &nread);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
|
@ -3702,7 +3812,7 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
|
|||
}
|
||||
|
||||
/* no data to transfer */
|
||||
Curl_setup_transfer(data, -1, -1, FALSE, -1);
|
||||
Curl_xfer_setup(data, -1, -1, FALSE, -1);
|
||||
|
||||
if(!ftpc->wait_data_conn) {
|
||||
/* no waiting for the data connection so this is now complete */
|
||||
|
@ -4010,6 +4120,24 @@ static CURLcode ftp_do(struct Curl_easy *data, bool *done)
|
|||
*done = FALSE; /* default to false */
|
||||
ftpc->wait_data_conn = FALSE; /* default to no such wait */
|
||||
|
||||
#ifdef CURL_DO_LINEEND_CONV
|
||||
{
|
||||
/* FTP data may need conversion. */
|
||||
struct Curl_cwriter *ftp_lc_writer;
|
||||
|
||||
result = Curl_cwriter_create(&ftp_lc_writer, data, &ftp_cw_lc,
|
||||
CURL_CW_CONTENT_DECODE);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
result = Curl_cwriter_add(data, ftp_lc_writer);
|
||||
if(result) {
|
||||
Curl_cwriter_free(data, ftp_lc_writer);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
#endif /* CURL_DO_LINEEND_CONV */
|
||||
|
||||
if(data->state.wildcardmatch) {
|
||||
result = wc_statemach(data);
|
||||
if(data->wildcard->state == CURLWC_SKIP ||
|
||||
|
@ -4286,7 +4414,7 @@ static CURLcode ftp_dophase_done(struct Curl_easy *data, bool connected)
|
|||
|
||||
if(ftp->transfer != PPTRANSFER_BODY)
|
||||
/* no data to transfer */
|
||||
Curl_setup_transfer(data, -1, -1, FALSE, -1);
|
||||
Curl_xfer_setup(data, -1, -1, FALSE, -1);
|
||||
else if(!connected)
|
||||
/* since we didn't connect now, we want do_more to get called */
|
||||
conn->bits.do_more = TRUE;
|
||||
|
|
|
@ -76,10 +76,10 @@ CURLcode Curl_initinfo(struct Curl_easy *data)
|
|||
free(info->wouldredirect);
|
||||
info->wouldredirect = NULL;
|
||||
|
||||
info->conn_primary_ip[0] = '\0';
|
||||
info->conn_local_ip[0] = '\0';
|
||||
info->conn_primary_port = 0;
|
||||
info->conn_local_port = 0;
|
||||
info->primary.remote_ip[0] = '\0';
|
||||
info->primary.local_ip[0] = '\0';
|
||||
info->primary.remote_port = 0;
|
||||
info->primary.local_port = 0;
|
||||
info->retry_after = 0;
|
||||
|
||||
info->conn_scheme = 0;
|
||||
|
@ -153,12 +153,12 @@ static CURLcode getinfo_char(struct Curl_easy *data, CURLINFO info,
|
|||
break;
|
||||
case CURLINFO_PRIMARY_IP:
|
||||
/* Return the ip address of the most recent (primary) connection */
|
||||
*param_charp = data->info.conn_primary_ip;
|
||||
*param_charp = data->info.primary.remote_ip;
|
||||
break;
|
||||
case CURLINFO_LOCAL_IP:
|
||||
/* Return the source/local ip address of the most recent (primary)
|
||||
connection */
|
||||
*param_charp = data->info.conn_local_ip;
|
||||
*param_charp = data->info.primary.local_ip;
|
||||
break;
|
||||
case CURLINFO_RTSP_SESSION_ID:
|
||||
*param_charp = data->set.str[STRING_RTSP_SESSION_ID];
|
||||
|
@ -180,7 +180,6 @@ static CURLcode getinfo_char(struct Curl_easy *data, CURLINFO info,
|
|||
*param_charp = NULL;
|
||||
#endif
|
||||
break;
|
||||
|
||||
default:
|
||||
return CURLE_UNKNOWN_OPTION;
|
||||
}
|
||||
|
@ -285,11 +284,11 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info,
|
|||
break;
|
||||
case CURLINFO_PRIMARY_PORT:
|
||||
/* Return the (remote) port of the most recent (primary) connection */
|
||||
*param_longp = data->info.conn_primary_port;
|
||||
*param_longp = data->info.primary.remote_port;
|
||||
break;
|
||||
case CURLINFO_LOCAL_PORT:
|
||||
/* Return the local port of the most recent (primary) connection */
|
||||
*param_longp = data->info.conn_local_port;
|
||||
*param_longp = data->info.primary.local_port;
|
||||
break;
|
||||
case CURLINFO_PROXY_ERROR:
|
||||
*param_longp = (long)data->info.pxcode;
|
||||
|
@ -334,6 +333,15 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info,
|
|||
case CURLINFO_PROTOCOL:
|
||||
*param_longp = data->info.conn_protocol;
|
||||
break;
|
||||
case CURLINFO_USED_PROXY:
|
||||
*param_longp =
|
||||
#ifdef CURL_DISABLE_PROXY
|
||||
0
|
||||
#else
|
||||
data->info.used_proxy
|
||||
#endif
|
||||
;
|
||||
break;
|
||||
default:
|
||||
return CURLE_UNKNOWN_OPTION;
|
||||
}
|
||||
|
|
10
lib/gopher.c
10
lib/gopher.c
|
@ -139,8 +139,8 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done)
|
|||
char *sel = NULL;
|
||||
char *sel_org = NULL;
|
||||
timediff_t timeout_ms;
|
||||
ssize_t amount, k;
|
||||
size_t len;
|
||||
ssize_t k;
|
||||
size_t amount, len;
|
||||
int what;
|
||||
|
||||
*done = TRUE; /* unconditionally */
|
||||
|
@ -185,7 +185,7 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done)
|
|||
if(strlen(sel) < 1)
|
||||
break;
|
||||
|
||||
result = Curl_nwrite(data, FIRSTSOCKET, sel, k, &amount);
|
||||
result = Curl_xfer_send(data, sel, k, &amount);
|
||||
if(!result) { /* Which may not have written it all! */
|
||||
result = Curl_client_write(data, CLIENTWRITE_HEADER, sel, amount);
|
||||
if(result)
|
||||
|
@ -227,7 +227,7 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done)
|
|||
free(sel_org);
|
||||
|
||||
if(!result)
|
||||
result = Curl_nwrite(data, FIRSTSOCKET, "\r\n", 2, &amount);
|
||||
result = Curl_xfer_send(data, "\r\n", 2, &amount);
|
||||
if(result) {
|
||||
failf(data, "Failed sending Gopher request");
|
||||
return result;
|
||||
|
@ -236,7 +236,7 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done)
|
|||
if(result)
|
||||
return result;
|
||||
|
||||
Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
|
||||
Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1);
|
||||
return CURLE_OK;
|
||||
}
|
||||
#endif /* CURL_DISABLE_GOPHER */
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "urldata.h"
|
||||
#include "strdup.h"
|
||||
#include "strcase.h"
|
||||
#include "sendf.h"
|
||||
#include "headers.h"
|
||||
|
||||
/* The last 3 #include files should be in this order */
|
||||
|
@ -337,14 +338,68 @@ CURLcode Curl_headers_push(struct Curl_easy *data, const char *header,
|
|||
}
|
||||
|
||||
/*
|
||||
* Curl_headers_init(). Init the headers subsystem.
|
||||
* Curl_headers_reset(). Reset the headers subsystem.
|
||||
*/
|
||||
static void headers_init(struct Curl_easy *data)
|
||||
static void headers_reset(struct Curl_easy *data)
|
||||
{
|
||||
Curl_llist_init(&data->state.httphdrs, NULL);
|
||||
data->state.prevhead = NULL;
|
||||
}
|
||||
|
||||
struct hds_cw_collect_ctx {
|
||||
struct Curl_cwriter super;
|
||||
};
|
||||
|
||||
static CURLcode hds_cw_collect_write(struct Curl_easy *data,
|
||||
struct Curl_cwriter *writer, int type,
|
||||
const char *buf, size_t blen)
|
||||
{
|
||||
if((type & CLIENTWRITE_HEADER) && !(type & CLIENTWRITE_STATUS)) {
|
||||
unsigned char htype = (unsigned char)
|
||||
(type & CLIENTWRITE_CONNECT ? CURLH_CONNECT :
|
||||
(type & CLIENTWRITE_1XX ? CURLH_1XX :
|
||||
(type & CLIENTWRITE_TRAILER ? CURLH_TRAILER :
|
||||
CURLH_HEADER)));
|
||||
CURLcode result = Curl_headers_push(data, buf, htype);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
return Curl_cwriter_write(data, writer->next, type, buf, blen);
|
||||
}
|
||||
|
||||
static const struct Curl_cwtype hds_cw_collect = {
|
||||
"hds-collect",
|
||||
NULL,
|
||||
Curl_cwriter_def_init,
|
||||
hds_cw_collect_write,
|
||||
Curl_cwriter_def_close,
|
||||
sizeof(struct hds_cw_collect_ctx)
|
||||
};
|
||||
|
||||
CURLcode Curl_headers_init(struct Curl_easy *data)
|
||||
{
|
||||
struct Curl_cwriter *writer;
|
||||
CURLcode result;
|
||||
|
||||
if(data->conn && (data->conn->handler->protocol & PROTO_FAMILY_HTTP)) {
|
||||
/* avoid installing it twice */
|
||||
if(Curl_cwriter_get_by_name(data, hds_cw_collect.name))
|
||||
return CURLE_OK;
|
||||
|
||||
result = Curl_cwriter_create(&writer, data, &hds_cw_collect,
|
||||
CURL_CW_PROTOCOL);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
result = Curl_cwriter_add(data, writer);
|
||||
if(result) {
|
||||
Curl_cwriter_free(data, writer);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Curl_headers_cleanup(). Free all stored headers and associated memory.
|
||||
*/
|
||||
|
@ -358,7 +413,7 @@ CURLcode Curl_headers_cleanup(struct Curl_easy *data)
|
|||
n = e->next;
|
||||
free(hs);
|
||||
}
|
||||
headers_init(data);
|
||||
headers_reset(data);
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,12 @@ struct Curl_header_store {
|
|||
char buffer[1]; /* this is the raw header blob */
|
||||
};
|
||||
|
||||
/*
|
||||
* Initialize header collecting for a transfer.
|
||||
* Will add a client writer that catches CLIENTWRITE_HEADER writes.
|
||||
*/
|
||||
CURLcode Curl_headers_init(struct Curl_easy *data);
|
||||
|
||||
/*
|
||||
* Curl_headers_push() gets passed a full header to store.
|
||||
*/
|
||||
|
@ -48,6 +54,7 @@ CURLcode Curl_headers_push(struct Curl_easy *data, const char *header,
|
|||
CURLcode Curl_headers_cleanup(struct Curl_easy *data);
|
||||
|
||||
#else
|
||||
#define Curl_headers_init(x) CURLE_OK
|
||||
#define Curl_headers_push(x,y,z) CURLE_OK
|
||||
#define Curl_headers_cleanup(x) Curl_nop_stmt
|
||||
#endif
|
||||
|
|
|
@ -288,7 +288,7 @@ static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data,
|
|||
size_t entry_len = create_hostcache_id(hostname, 0, port,
|
||||
entry_id, sizeof(entry_id));
|
||||
|
||||
/* See if its already in our dns cache */
|
||||
/* See if it's already in our dns cache */
|
||||
dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
|
||||
|
||||
/* No entry found in cache, check if we might have a wildcard entry */
|
||||
|
|
17
lib/hsts.c
17
lib/hsts.c
|
@ -511,7 +511,6 @@ static CURLcode hsts_pull(struct Curl_easy *data, struct hsts *h)
|
|||
static CURLcode hsts_load(struct hsts *h, const char *file)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
char *line = NULL;
|
||||
FILE *fp;
|
||||
|
||||
/* we need a private copy of the file name so that the hsts cache file
|
||||
|
@ -523,11 +522,10 @@ static CURLcode hsts_load(struct hsts *h, const char *file)
|
|||
|
||||
fp = fopen(file, FOPEN_READTEXT);
|
||||
if(fp) {
|
||||
line = malloc(MAX_HSTS_LINE);
|
||||
if(!line)
|
||||
goto fail;
|
||||
while(Curl_get_line(line, MAX_HSTS_LINE, fp)) {
|
||||
char *lineptr = line;
|
||||
struct dynbuf buf;
|
||||
Curl_dyn_init(&buf, MAX_HSTS_LINE);
|
||||
while(Curl_get_line(&buf, fp)) {
|
||||
char *lineptr = Curl_dyn_ptr(&buf);
|
||||
while(*lineptr && ISBLANK(*lineptr))
|
||||
lineptr++;
|
||||
if(*lineptr == '#')
|
||||
|
@ -536,15 +534,10 @@ static CURLcode hsts_load(struct hsts *h, const char *file)
|
|||
|
||||
hsts_add(h, lineptr);
|
||||
}
|
||||
free(line); /* free the line buffer */
|
||||
Curl_dyn_free(&buf); /* free the line buffer */
|
||||
fclose(fp);
|
||||
}
|
||||
return result;
|
||||
|
||||
fail:
|
||||
Curl_safefree(h->filename);
|
||||
fclose(fp);
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
2749
lib/http.c
2749
lib/http.c
File diff suppressed because it is too large
Load Diff
59
lib/http.h
59
lib/http.h
|
@ -74,12 +74,6 @@ char *Curl_checkProxyheaders(struct Curl_easy *data,
|
|||
const char *thisheader,
|
||||
const size_t thislen);
|
||||
struct HTTP; /* see below */
|
||||
CURLcode Curl_buffer_send(struct dynbuf *in,
|
||||
struct Curl_easy *data,
|
||||
struct HTTP *http,
|
||||
curl_off_t *bytes_written,
|
||||
curl_off_t included_body_bytes,
|
||||
int socketindex);
|
||||
|
||||
CURLcode Curl_add_timecondition(struct Curl_easy *data,
|
||||
#ifndef USE_HYPER
|
||||
|
@ -100,10 +94,6 @@ CURLcode Curl_dynhds_add_custom(struct Curl_easy *data,
|
|||
bool is_connect,
|
||||
struct dynhds *hds);
|
||||
|
||||
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);
|
||||
|
@ -113,13 +103,13 @@ CURLcode Curl_http_target(struct Curl_easy *data, struct connectdata *conn,
|
|||
CURLcode Curl_http_statusline(struct Curl_easy *data,
|
||||
struct connectdata *conn);
|
||||
CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn,
|
||||
char *headp);
|
||||
char *headp, size_t hdlen);
|
||||
CURLcode Curl_transferencode(struct Curl_easy *data);
|
||||
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);
|
||||
CURLcode Curl_http_req_set_reader(struct Curl_easy *data,
|
||||
Curl_HttpReq httpreq,
|
||||
const char **tep);
|
||||
CURLcode Curl_http_req_complete(struct Curl_easy *data,
|
||||
struct dynbuf *r, Curl_HttpReq httpreq);
|
||||
bool Curl_use_http_1_1plus(const struct Curl_easy *data,
|
||||
const struct connectdata *conn);
|
||||
#ifndef CURL_DISABLE_COOKIES
|
||||
|
@ -129,14 +119,9 @@ CURLcode Curl_http_cookies(struct Curl_easy *data,
|
|||
#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,
|
||||
Curl_HttpReq httpreq);
|
||||
CURLcode Curl_http_firstwrite(struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
bool *done);
|
||||
CURLcode Curl_http_firstwrite(struct Curl_easy *data);
|
||||
|
||||
/* protocol-specific functions set up to be called by the main engine */
|
||||
CURLcode Curl_http_setup_conn(struct Curl_easy *data,
|
||||
|
@ -148,8 +133,7 @@ int Curl_http_getsock_do(struct Curl_easy *data, struct connectdata *conn,
|
|||
curl_socket_t *socks);
|
||||
CURLcode Curl_http_write_resp(struct Curl_easy *data,
|
||||
const char *buf, size_t blen,
|
||||
bool is_eos,
|
||||
bool *done);
|
||||
bool is_eos);
|
||||
|
||||
/* These functions are in http.c */
|
||||
CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
|
||||
|
@ -192,34 +176,20 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data);
|
|||
version. This count includes CONNECT response headers. */
|
||||
#define MAX_HTTP_RESP_HEADER_SIZE (300*1024)
|
||||
|
||||
bool Curl_http_exp100_is_selected(struct Curl_easy *data);
|
||||
void Curl_http_exp100_got100(struct Curl_easy *data);
|
||||
|
||||
#endif /* CURL_DISABLE_HTTP */
|
||||
|
||||
/****************************************************************************
|
||||
* HTTP unique setup
|
||||
***************************************************************************/
|
||||
struct HTTP {
|
||||
curl_off_t postsize; /* off_t to handle large file sizes */
|
||||
const char *postdata;
|
||||
struct back {
|
||||
curl_read_callback fread_func; /* backup storage for fread pointer */
|
||||
void *fread_in; /* backup storage for fread_in pointer */
|
||||
const char *postdata;
|
||||
curl_off_t postsize;
|
||||
struct Curl_easy *data;
|
||||
} backup;
|
||||
|
||||
enum {
|
||||
HTTPSEND_NADA, /* init */
|
||||
HTTPSEND_REQUEST, /* sending a request */
|
||||
HTTPSEND_BODY /* sending body */
|
||||
} sending;
|
||||
|
||||
#ifndef CURL_DISABLE_HTTP
|
||||
void *h2_ctx; /* HTTP/2 implementation context */
|
||||
void *h3_ctx; /* HTTP/3 implementation context */
|
||||
struct dynbuf send_buffer; /* used if the request couldn't be sent in one
|
||||
chunk, points to an allocated send_buffer
|
||||
struct */
|
||||
#else
|
||||
char unused;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -227,8 +197,7 @@ CURLcode Curl_http_size(struct Curl_easy *data);
|
|||
|
||||
CURLcode Curl_http_write_resp_hds(struct Curl_easy *data,
|
||||
const char *buf, size_t blen,
|
||||
size_t *pconsumed,
|
||||
bool *done);
|
||||
size_t *pconsumed);
|
||||
|
||||
/**
|
||||
* Curl_http_output_auth() setups the authentication headers for the
|
||||
|
|
327
lib/http2.c
327
lib/http2.c
|
@ -121,7 +121,6 @@ static ssize_t populate_binsettings(uint8_t *binsettings,
|
|||
|
||||
struct cf_h2_ctx {
|
||||
nghttp2_session *h2;
|
||||
uint32_t max_concurrent_streams;
|
||||
/* The easy handle used in the current filter call, cleared at return */
|
||||
struct cf_call_data call_data;
|
||||
|
||||
|
@ -130,6 +129,7 @@ struct cf_h2_ctx {
|
|||
struct bufc_pool stream_bufcp; /* spares for stream buffers */
|
||||
|
||||
size_t drain_total; /* sum of all stream's UrlState drain */
|
||||
uint32_t max_concurrent_streams;
|
||||
int32_t goaway_error;
|
||||
int32_t last_stream_id;
|
||||
BIT(conn_closed);
|
||||
|
@ -169,11 +169,9 @@ static CURLcode h2_progress_egress(struct Curl_cfilter *cf,
|
|||
struct Curl_easy *data);
|
||||
|
||||
/**
|
||||
* All about the H3 internals of a stream
|
||||
* All about the H2 internals of a stream
|
||||
*/
|
||||
struct stream_ctx {
|
||||
/*********** for HTTP/2 we store stream-local data here *************/
|
||||
int32_t id; /* HTTP/2 protocol identifier for stream */
|
||||
struct h2_stream_ctx {
|
||||
struct bufq recvbuf; /* response buffer */
|
||||
struct bufq sendbuf; /* request buffer */
|
||||
struct h1_req_parser h1; /* parsing the request */
|
||||
|
@ -181,6 +179,7 @@ struct stream_ctx {
|
|||
size_t resp_hds_len; /* amount of response header bytes in recvbuf */
|
||||
size_t upload_blocked_len;
|
||||
curl_off_t upload_left; /* number of request bytes left to upload */
|
||||
curl_off_t nrcvd_data; /* number of DATA bytes received */
|
||||
|
||||
char **push_headers; /* allocated array */
|
||||
size_t push_headers_used; /* number of entries filled in */
|
||||
|
@ -189,16 +188,18 @@ struct stream_ctx {
|
|||
int status_code; /* HTTP response status code */
|
||||
uint32_t error; /* stream error code */
|
||||
uint32_t local_window_size; /* the local recv window size */
|
||||
bool resp_hds_complete; /* we have a complete, final response */
|
||||
bool closed; /* TRUE on stream close */
|
||||
bool reset; /* TRUE on stream reset */
|
||||
bool close_handled; /* TRUE if stream closure is handled by libcurl */
|
||||
bool bodystarted;
|
||||
bool send_closed; /* transfer is done sending, we might have still
|
||||
buffered data in stream->sendbuf to upload. */
|
||||
int32_t id; /* HTTP/2 protocol identifier for stream */
|
||||
BIT(resp_hds_complete); /* we have a complete, final response */
|
||||
BIT(closed); /* TRUE on stream close */
|
||||
BIT(reset); /* TRUE on stream reset */
|
||||
BIT(close_handled); /* TRUE if stream closure is handled by libcurl */
|
||||
BIT(bodystarted);
|
||||
BIT(send_closed); /* transfer is done sending, we might have still
|
||||
buffered data in stream->sendbuf to upload. */
|
||||
};
|
||||
|
||||
#define H2_STREAM_CTX(d) ((struct stream_ctx *)(((d) && (d)->req.p.http)? \
|
||||
#define H2_STREAM_CTX(d) ((struct h2_stream_ctx *)(((d) && \
|
||||
(d)->req.p.http)? \
|
||||
((struct HTTP *)(d)->req.p.http)->h2_ctx \
|
||||
: NULL))
|
||||
#define H2_STREAM_LCTX(d) ((struct HTTP *)(d)->req.p.http)->h2_ctx
|
||||
|
@ -210,7 +211,7 @@ struct stream_ctx {
|
|||
*/
|
||||
static void drain_stream(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct stream_ctx *stream)
|
||||
struct h2_stream_ctx *stream)
|
||||
{
|
||||
unsigned char bits;
|
||||
|
||||
|
@ -229,10 +230,10 @@ static void drain_stream(struct Curl_cfilter *cf,
|
|||
|
||||
static CURLcode http2_data_setup(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct stream_ctx **pstream)
|
||||
struct h2_stream_ctx **pstream)
|
||||
{
|
||||
struct cf_h2_ctx *ctx = cf->ctx;
|
||||
struct stream_ctx *stream;
|
||||
struct h2_stream_ctx *stream;
|
||||
|
||||
(void)cf;
|
||||
DEBUGASSERT(data);
|
||||
|
@ -253,8 +254,6 @@ static CURLcode http2_data_setup(struct Curl_cfilter *cf,
|
|||
stream->id = -1;
|
||||
Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp,
|
||||
H2_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE);
|
||||
Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp,
|
||||
H2_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT);
|
||||
Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
|
||||
Curl_dynhds_init(&stream->resp_trailers, 0, DYN_HTTP_REQUEST);
|
||||
stream->resp_hds_len = 0;
|
||||
|
@ -265,20 +264,28 @@ static CURLcode http2_data_setup(struct Curl_cfilter *cf,
|
|||
stream->error = NGHTTP2_NO_ERROR;
|
||||
stream->local_window_size = H2_STREAM_WINDOW_SIZE;
|
||||
stream->upload_left = 0;
|
||||
stream->nrcvd_data = 0;
|
||||
|
||||
H2_STREAM_LCTX(data) = stream;
|
||||
*pstream = stream;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static void http2_data_done(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data, bool premature)
|
||||
static void free_push_headers(struct h2_stream_ctx *stream)
|
||||
{
|
||||
size_t i;
|
||||
for(i = 0; i<stream->push_headers_used; i++)
|
||||
free(stream->push_headers[i]);
|
||||
Curl_safefree(stream->push_headers);
|
||||
stream->push_headers_used = 0;
|
||||
}
|
||||
|
||||
static void http2_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
{
|
||||
struct cf_h2_ctx *ctx = cf->ctx;
|
||||
struct stream_ctx *stream = H2_STREAM_CTX(data);
|
||||
struct h2_stream_ctx *stream = H2_STREAM_CTX(data);
|
||||
|
||||
DEBUGASSERT(ctx);
|
||||
(void)premature;
|
||||
if(!stream)
|
||||
return;
|
||||
|
||||
|
@ -298,34 +305,15 @@ static void http2_data_done(struct Curl_cfilter *cf,
|
|||
stream->id, NGHTTP2_STREAM_CLOSED);
|
||||
flush_egress = TRUE;
|
||||
}
|
||||
if(!Curl_bufq_is_empty(&stream->recvbuf)) {
|
||||
/* Anything in the recvbuf is still being counted
|
||||
* in stream and connection window flow control. Need
|
||||
* to free that space or the connection window might get
|
||||
* exhausted eventually. */
|
||||
nghttp2_session_consume(ctx->h2, stream->id,
|
||||
Curl_bufq_len(&stream->recvbuf));
|
||||
/* give WINDOW_UPATE a chance to be sent, but ignore any error */
|
||||
flush_egress = TRUE;
|
||||
}
|
||||
|
||||
if(flush_egress)
|
||||
nghttp2_session_send(ctx->h2);
|
||||
}
|
||||
|
||||
Curl_bufq_free(&stream->sendbuf);
|
||||
Curl_bufq_free(&stream->recvbuf);
|
||||
Curl_h1_req_parse_free(&stream->h1);
|
||||
Curl_dynhds_free(&stream->resp_trailers);
|
||||
if(stream->push_headers) {
|
||||
/* if they weren't used and then freed before */
|
||||
for(; stream->push_headers_used > 0; --stream->push_headers_used) {
|
||||
free(stream->push_headers[stream->push_headers_used - 1]);
|
||||
}
|
||||
free(stream->push_headers);
|
||||
stream->push_headers = NULL;
|
||||
}
|
||||
|
||||
free_push_headers(stream);
|
||||
free(stream);
|
||||
H2_STREAM_LCTX(data) = NULL;
|
||||
}
|
||||
|
@ -411,7 +399,7 @@ static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf,
|
|||
bool via_h1_upgrade)
|
||||
{
|
||||
struct cf_h2_ctx *ctx = cf->ctx;
|
||||
struct stream_ctx *stream;
|
||||
struct h2_stream_ctx *stream;
|
||||
CURLcode result = CURLE_OUT_OF_MEMORY;
|
||||
int rc;
|
||||
nghttp2_session_callbacks *cbs = NULL;
|
||||
|
@ -731,7 +719,7 @@ char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
|
|||
if(!h || !GOOD_EASY_HANDLE(h->data))
|
||||
return NULL;
|
||||
else {
|
||||
struct stream_ctx *stream = H2_STREAM_CTX(h->data);
|
||||
struct h2_stream_ctx *stream = H2_STREAM_CTX(h->data);
|
||||
if(stream && num < stream->push_headers_used)
|
||||
return stream->push_headers[num];
|
||||
}
|
||||
|
@ -743,7 +731,7 @@ char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
|
|||
*/
|
||||
char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
|
||||
{
|
||||
struct stream_ctx *stream;
|
||||
struct h2_stream_ctx *stream;
|
||||
size_t len;
|
||||
size_t i;
|
||||
/* Verify that we got a good easy handle in the push header struct,
|
||||
|
@ -783,7 +771,7 @@ static struct Curl_easy *h2_duphandle(struct Curl_cfilter *cf,
|
|||
(void)Curl_close(&second);
|
||||
}
|
||||
else {
|
||||
struct stream_ctx *second_stream;
|
||||
struct h2_stream_ctx *second_stream;
|
||||
|
||||
second->req.p.http = http;
|
||||
http2_data_setup(cf, second, &second_stream);
|
||||
|
@ -850,9 +838,8 @@ fail:
|
|||
static void discard_newhandle(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *newhandle)
|
||||
{
|
||||
if(!newhandle->req.p.http) {
|
||||
http2_data_done(cf, newhandle, TRUE);
|
||||
newhandle->req.p.http = NULL;
|
||||
if(newhandle->req.p.http) {
|
||||
http2_data_done(cf, newhandle);
|
||||
}
|
||||
(void)Curl_close(&newhandle);
|
||||
}
|
||||
|
@ -867,12 +854,11 @@ static int push_promise(struct Curl_cfilter *cf,
|
|||
CURL_TRC_CF(data, cf, "[%d] PUSH_PROMISE received",
|
||||
frame->promised_stream_id);
|
||||
if(data->multi->push_cb) {
|
||||
struct stream_ctx *stream;
|
||||
struct stream_ctx *newstream;
|
||||
struct h2_stream_ctx *stream;
|
||||
struct h2_stream_ctx *newstream;
|
||||
struct curl_pushheaders heads;
|
||||
CURLMcode rc;
|
||||
CURLcode result;
|
||||
size_t i;
|
||||
/* clone the parent */
|
||||
struct Curl_easy *newhandle = h2_duphandle(cf, data);
|
||||
if(!newhandle) {
|
||||
|
@ -917,11 +903,7 @@ static int push_promise(struct Curl_cfilter *cf,
|
|||
Curl_set_in_callback(data, false);
|
||||
|
||||
/* free the headers again */
|
||||
for(i = 0; i<stream->push_headers_used; i++)
|
||||
free(stream->push_headers[i]);
|
||||
free(stream->push_headers);
|
||||
stream->push_headers = NULL;
|
||||
stream->push_headers_used = 0;
|
||||
free_push_headers(stream);
|
||||
|
||||
if(rv) {
|
||||
DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT));
|
||||
|
@ -967,18 +949,8 @@ static CURLcode recvbuf_write_hds(struct Curl_cfilter *cf,
|
|||
struct Curl_easy *data,
|
||||
const char *buf, size_t blen)
|
||||
{
|
||||
struct stream_ctx *stream = H2_STREAM_CTX(data);
|
||||
ssize_t nwritten;
|
||||
CURLcode result;
|
||||
|
||||
(void)cf;
|
||||
nwritten = Curl_bufq_write(&stream->recvbuf,
|
||||
(const unsigned char *)buf, blen, &result);
|
||||
if(nwritten < 0)
|
||||
return result;
|
||||
stream->resp_hds_len += (size_t)nwritten;
|
||||
DEBUGASSERT((size_t)nwritten == blen);
|
||||
return CURLE_OK;
|
||||
return Curl_xfer_write_resp(data, (char *)buf, blen, FALSE);
|
||||
}
|
||||
|
||||
static CURLcode on_stream_frame(struct Curl_cfilter *cf,
|
||||
|
@ -986,10 +958,9 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf,
|
|||
const nghttp2_frame *frame)
|
||||
{
|
||||
struct cf_h2_ctx *ctx = cf->ctx;
|
||||
struct stream_ctx *stream = H2_STREAM_CTX(data);
|
||||
struct h2_stream_ctx *stream = H2_STREAM_CTX(data);
|
||||
int32_t stream_id = frame->hd.stream_id;
|
||||
CURLcode result;
|
||||
size_t rbuflen;
|
||||
int rv;
|
||||
|
||||
if(!stream) {
|
||||
|
@ -999,9 +970,8 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf,
|
|||
|
||||
switch(frame->hd.type) {
|
||||
case NGHTTP2_DATA:
|
||||
rbuflen = Curl_bufq_len(&stream->recvbuf);
|
||||
CURL_TRC_CF(data, cf, "[%d] DATA, buffered=%zu, window=%d/%d",
|
||||
stream_id, rbuflen,
|
||||
CURL_TRC_CF(data, cf, "[%d] DATA, window=%d/%d",
|
||||
stream_id,
|
||||
nghttp2_session_get_stream_effective_recv_data_length(
|
||||
ctx->h2, stream->id),
|
||||
nghttp2_session_get_stream_effective_local_window_size(
|
||||
|
@ -1018,20 +988,6 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf,
|
|||
if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
drain_stream(cf, data, stream);
|
||||
}
|
||||
else if(rbuflen > stream->local_window_size) {
|
||||
int32_t wsize = nghttp2_session_get_stream_local_window_size(
|
||||
ctx->h2, stream->id);
|
||||
if(wsize > 0 && (uint32_t)wsize != stream->local_window_size) {
|
||||
/* H2 flow control is not absolute, as the server might not have the
|
||||
* same view, yet. When we receive more than we want, we enforce
|
||||
* the local window size again to make nghttp2 send WINDOW_UPATEs
|
||||
* accordingly. */
|
||||
nghttp2_session_set_local_window_size(ctx->h2,
|
||||
NGHTTP2_FLAG_NONE,
|
||||
stream->id,
|
||||
stream->local_window_size);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NGHTTP2_HEADERS:
|
||||
if(stream->bodystarted) {
|
||||
|
@ -1233,7 +1189,7 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
|
|||
* servers send an explicit WINDOW_UPDATE, but not all seem to do that.
|
||||
* To be safe, we UNHOLD a stream in order not to stall. */
|
||||
if(CURL_WANT_SEND(data)) {
|
||||
struct stream_ctx *stream = H2_STREAM_CTX(data);
|
||||
struct h2_stream_ctx *stream = H2_STREAM_CTX(data);
|
||||
if(stream)
|
||||
drain_stream(cf, data, stream);
|
||||
}
|
||||
|
@ -1270,9 +1226,9 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
|
|||
const uint8_t *mem, size_t len, void *userp)
|
||||
{
|
||||
struct Curl_cfilter *cf = userp;
|
||||
struct stream_ctx *stream;
|
||||
struct cf_h2_ctx *ctx = cf->ctx;
|
||||
struct h2_stream_ctx *stream;
|
||||
struct Curl_easy *data_s;
|
||||
ssize_t nwritten;
|
||||
CURLcode result;
|
||||
(void)flags;
|
||||
|
||||
|
@ -1296,18 +1252,15 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
|
|||
if(!stream)
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
|
||||
nwritten = Curl_bufq_write(&stream->recvbuf, mem, len, &result);
|
||||
if(nwritten < 0) {
|
||||
if(result != CURLE_AGAIN)
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
result = Curl_xfer_write_resp(data_s, (char *)mem, len, FALSE);
|
||||
if(result && result != CURLE_AGAIN)
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
|
||||
nwritten = 0;
|
||||
}
|
||||
nghttp2_session_consume(ctx->h2, stream_id, len);
|
||||
stream->nrcvd_data += (curl_off_t)len;
|
||||
|
||||
/* if we receive data for another handle, wake that up */
|
||||
drain_stream(cf, data_s, stream);
|
||||
|
||||
DEBUGASSERT((size_t)nwritten == len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1316,7 +1269,7 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id,
|
|||
{
|
||||
struct Curl_cfilter *cf = userp;
|
||||
struct Curl_easy *data_s, *call_data = CF_DATA_CURRENT(cf);
|
||||
struct stream_ctx *stream;
|
||||
struct h2_stream_ctx *stream;
|
||||
int rv;
|
||||
(void)session;
|
||||
|
||||
|
@ -1374,7 +1327,7 @@ static int on_begin_headers(nghttp2_session *session,
|
|||
const nghttp2_frame *frame, void *userp)
|
||||
{
|
||||
struct Curl_cfilter *cf = userp;
|
||||
struct stream_ctx *stream;
|
||||
struct h2_stream_ctx *stream;
|
||||
struct Curl_easy *data_s = NULL;
|
||||
|
||||
(void)cf;
|
||||
|
@ -1403,7 +1356,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
|
|||
void *userp)
|
||||
{
|
||||
struct Curl_cfilter *cf = userp;
|
||||
struct stream_ctx *stream;
|
||||
struct h2_stream_ctx *stream;
|
||||
struct Curl_easy *data_s;
|
||||
int32_t stream_id = frame->hd.stream_id;
|
||||
CURLcode result;
|
||||
|
@ -1459,7 +1412,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
|
|||
stream->push_headers = malloc(stream->push_headers_alloc *
|
||||
sizeof(char *));
|
||||
if(!stream->push_headers)
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
stream->push_headers_used = 0;
|
||||
}
|
||||
else if(stream->push_headers_used ==
|
||||
|
@ -1468,15 +1421,15 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
|
|||
if(stream->push_headers_alloc > 1000) {
|
||||
/* this is beyond crazy many headers, bail out */
|
||||
failf(data_s, "Too many PUSH_PROMISE headers");
|
||||
Curl_safefree(stream->push_headers);
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
free_push_headers(stream);
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
stream->push_headers_alloc *= 2;
|
||||
headp = Curl_saferealloc(stream->push_headers,
|
||||
stream->push_headers_alloc * sizeof(char *));
|
||||
headp = realloc(stream->push_headers,
|
||||
stream->push_headers_alloc * sizeof(char *));
|
||||
if(!headp) {
|
||||
stream->push_headers = NULL;
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
free_push_headers(stream);
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
stream->push_headers = headp;
|
||||
}
|
||||
|
@ -1565,7 +1518,7 @@ static ssize_t req_body_read_callback(nghttp2_session *session,
|
|||
{
|
||||
struct Curl_cfilter *cf = userp;
|
||||
struct Curl_easy *data_s;
|
||||
struct stream_ctx *stream = NULL;
|
||||
struct h2_stream_ctx *stream = NULL;
|
||||
CURLcode result;
|
||||
ssize_t nread;
|
||||
(void)source;
|
||||
|
@ -1667,7 +1620,7 @@ static CURLcode http2_data_done_send(struct Curl_cfilter *cf,
|
|||
{
|
||||
struct cf_h2_ctx *ctx = cf->ctx;
|
||||
CURLcode result = CURLE_OK;
|
||||
struct stream_ctx *stream = H2_STREAM_CTX(data);
|
||||
struct h2_stream_ctx *stream = H2_STREAM_CTX(data);
|
||||
|
||||
if(!ctx || !ctx->h2 || !stream)
|
||||
goto out;
|
||||
|
@ -1691,7 +1644,7 @@ out:
|
|||
|
||||
static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct stream_ctx *stream,
|
||||
struct h2_stream_ctx *stream,
|
||||
CURLcode *err)
|
||||
{
|
||||
ssize_t rv = 0;
|
||||
|
@ -1713,7 +1666,7 @@ static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf,
|
|||
}
|
||||
else if(stream->reset) {
|
||||
failf(data, "HTTP/2 stream %u was reset", stream->id);
|
||||
*err = stream->bodystarted? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR;
|
||||
*err = data->req.bytecount? CURLE_PARTIAL_FILE : CURLE_HTTP2;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -1787,7 +1740,7 @@ static void h2_pri_spec(struct Curl_easy *data,
|
|||
nghttp2_priority_spec *pri_spec)
|
||||
{
|
||||
struct Curl_data_priority *prio = &data->set.priority;
|
||||
struct stream_ctx *depstream = H2_STREAM_CTX(prio->parent);
|
||||
struct h2_stream_ctx *depstream = H2_STREAM_CTX(prio->parent);
|
||||
int32_t depstream_id = depstream? depstream->id:0;
|
||||
nghttp2_priority_spec_init(pri_spec, depstream_id,
|
||||
sweight_wanted(data),
|
||||
|
@ -1805,7 +1758,7 @@ static CURLcode h2_progress_egress(struct Curl_cfilter *cf,
|
|||
struct Curl_easy *data)
|
||||
{
|
||||
struct cf_h2_ctx *ctx = cf->ctx;
|
||||
struct stream_ctx *stream = H2_STREAM_CTX(data);
|
||||
struct h2_stream_ctx *stream = H2_STREAM_CTX(data);
|
||||
int rv = 0;
|
||||
|
||||
if(stream && stream->id > 0 &&
|
||||
|
@ -1838,40 +1791,26 @@ out:
|
|||
}
|
||||
|
||||
static ssize_t stream_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
|
||||
struct stream_ctx *stream,
|
||||
struct h2_stream_ctx *stream,
|
||||
char *buf, size_t len, CURLcode *err)
|
||||
{
|
||||
struct cf_h2_ctx *ctx = cf->ctx;
|
||||
ssize_t nread = -1;
|
||||
|
||||
(void)buf;
|
||||
*err = CURLE_AGAIN;
|
||||
if(!Curl_bufq_is_empty(&stream->recvbuf)) {
|
||||
nread = Curl_bufq_read(&stream->recvbuf,
|
||||
(unsigned char *)buf, len, err);
|
||||
if(nread < 0)
|
||||
goto out;
|
||||
DEBUGASSERT(nread > 0);
|
||||
if(stream->closed) {
|
||||
CURL_TRC_CF(data, cf, "[%d] returning CLOSE", stream->id);
|
||||
nread = http2_handle_stream_close(cf, data, stream, err);
|
||||
}
|
||||
|
||||
if(nread < 0) {
|
||||
if(stream->closed) {
|
||||
CURL_TRC_CF(data, cf, "[%d] returning CLOSE", stream->id);
|
||||
nread = http2_handle_stream_close(cf, data, stream, err);
|
||||
}
|
||||
else if(stream->reset ||
|
||||
(ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) ||
|
||||
(ctx->goaway && ctx->last_stream_id < stream->id)) {
|
||||
CURL_TRC_CF(data, cf, "[%d] returning ERR", stream->id);
|
||||
*err = stream->bodystarted? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR;
|
||||
nread = -1;
|
||||
}
|
||||
}
|
||||
else if(nread == 0) {
|
||||
*err = CURLE_AGAIN;
|
||||
else if(stream->reset ||
|
||||
(ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) ||
|
||||
(ctx->goaway && ctx->last_stream_id < stream->id)) {
|
||||
CURL_TRC_CF(data, cf, "[%d] returning ERR", stream->id);
|
||||
*err = data->req.bytecount? CURLE_PARTIAL_FILE : CURLE_HTTP2;
|
||||
nread = -1;
|
||||
}
|
||||
|
||||
out:
|
||||
if(nread < 0 && *err != CURLE_AGAIN)
|
||||
CURL_TRC_CF(data, cf, "[%d] stream_recv(len=%zu) -> %zd, %d",
|
||||
stream->id, len, nread, *err);
|
||||
|
@ -1879,10 +1818,11 @@ out:
|
|||
}
|
||||
|
||||
static CURLcode h2_progress_ingress(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
struct Curl_easy *data,
|
||||
size_t data_max_bytes)
|
||||
{
|
||||
struct cf_h2_ctx *ctx = cf->ctx;
|
||||
struct stream_ctx *stream;
|
||||
struct h2_stream_ctx *stream;
|
||||
CURLcode result = CURLE_OK;
|
||||
ssize_t nread;
|
||||
|
||||
|
@ -1899,16 +1839,17 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf,
|
|||
* all network input */
|
||||
while(!ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) {
|
||||
stream = H2_STREAM_CTX(data);
|
||||
if(stream && (stream->closed || Curl_bufq_is_full(&stream->recvbuf))) {
|
||||
if(stream && (stream->closed || !data_max_bytes)) {
|
||||
/* We would like to abort here and stop processing, so that
|
||||
* the transfer loop can handle the data/close here. However,
|
||||
* this may leave data in underlying buffers that will not
|
||||
* be consumed. */
|
||||
if(!cf->next || !cf->next->cft->has_data_pending(cf->next, data))
|
||||
break;
|
||||
drain_stream(cf, data, stream);
|
||||
break;
|
||||
}
|
||||
|
||||
nread = Curl_bufq_slurp(&ctx->inbufq, nw_in_reader, cf, &result);
|
||||
nread = Curl_bufq_sipn(&ctx->inbufq, 0, nw_in_reader, cf, &result);
|
||||
if(nread < 0) {
|
||||
if(result != CURLE_AGAIN) {
|
||||
failf(data, "Failed receiving HTTP2 data: %d(%s)", result,
|
||||
|
@ -1923,8 +1864,9 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf,
|
|||
break;
|
||||
}
|
||||
else {
|
||||
CURL_TRC_CF(data, cf, "[0] ingress: read %zd bytes",
|
||||
nread);
|
||||
CURL_TRC_CF(data, cf, "[0] ingress: read %zd bytes", nread);
|
||||
data_max_bytes = (data_max_bytes > (size_t)nread)?
|
||||
(data_max_bytes - (size_t)nread) : 0;
|
||||
}
|
||||
|
||||
if(h2_process_pending_input(cf, data, &result))
|
||||
|
@ -1942,7 +1884,7 @@ static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
|
|||
char *buf, size_t len, CURLcode *err)
|
||||
{
|
||||
struct cf_h2_ctx *ctx = cf->ctx;
|
||||
struct stream_ctx *stream = H2_STREAM_CTX(data);
|
||||
struct h2_stream_ctx *stream = H2_STREAM_CTX(data);
|
||||
ssize_t nread = -1;
|
||||
CURLcode result;
|
||||
struct cf_call_data save;
|
||||
|
@ -1966,7 +1908,7 @@ static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
|
|||
goto out;
|
||||
|
||||
if(nread < 0) {
|
||||
*err = h2_progress_ingress(cf, data);
|
||||
*err = h2_progress_ingress(cf, data, len);
|
||||
if(*err)
|
||||
goto out;
|
||||
|
||||
|
@ -2011,9 +1953,8 @@ out:
|
|||
nread = -1;
|
||||
}
|
||||
CURL_TRC_CF(data, cf, "[%d] cf_recv(len=%zu) -> %zd %d, "
|
||||
"buffered=%zu, window=%d/%d, connection %d/%d",
|
||||
"window=%d/%d, connection %d/%d",
|
||||
stream->id, len, nread, *err,
|
||||
Curl_bufq_len(&stream->recvbuf),
|
||||
nghttp2_session_get_stream_effective_recv_data_length(
|
||||
ctx->h2, stream->id),
|
||||
nghttp2_session_get_stream_effective_local_window_size(
|
||||
|
@ -2025,12 +1966,13 @@ out:
|
|||
return nread;
|
||||
}
|
||||
|
||||
static ssize_t h2_submit(struct stream_ctx **pstream,
|
||||
static ssize_t h2_submit(struct h2_stream_ctx **pstream,
|
||||
struct Curl_cfilter *cf, struct Curl_easy *data,
|
||||
const void *buf, size_t len, CURLcode *err)
|
||||
const void *buf, size_t len,
|
||||
size_t *phdslen, CURLcode *err)
|
||||
{
|
||||
struct cf_h2_ctx *ctx = cf->ctx;
|
||||
struct stream_ctx *stream = NULL;
|
||||
struct h2_stream_ctx *stream = NULL;
|
||||
struct dynhds h2_headers;
|
||||
nghttp2_nv *nva = NULL;
|
||||
const void *body = NULL;
|
||||
|
@ -2040,6 +1982,7 @@ static ssize_t h2_submit(struct stream_ctx **pstream,
|
|||
nghttp2_priority_spec pri_spec;
|
||||
ssize_t nwritten;
|
||||
|
||||
*phdslen = 0;
|
||||
Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST);
|
||||
|
||||
*err = http2_data_setup(cf, data, &stream);
|
||||
|
@ -2051,6 +1994,7 @@ static ssize_t h2_submit(struct stream_ctx **pstream,
|
|||
nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err);
|
||||
if(nwritten < 0)
|
||||
goto out;
|
||||
*phdslen = (size_t)nwritten;
|
||||
if(!stream->h1.done) {
|
||||
/* need more data */
|
||||
goto out;
|
||||
|
@ -2169,10 +2113,11 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
|
|||
const void *buf, size_t len, CURLcode *err)
|
||||
{
|
||||
struct cf_h2_ctx *ctx = cf->ctx;
|
||||
struct stream_ctx *stream = H2_STREAM_CTX(data);
|
||||
struct h2_stream_ctx *stream = H2_STREAM_CTX(data);
|
||||
struct cf_call_data save;
|
||||
int rv;
|
||||
ssize_t nwritten;
|
||||
size_t hdslen = 0;
|
||||
CURLcode result;
|
||||
int blocked = 0, was_blocked = 0;
|
||||
|
||||
|
@ -2236,11 +2181,12 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
|
|||
}
|
||||
}
|
||||
else {
|
||||
nwritten = h2_submit(&stream, cf, data, buf, len, err);
|
||||
nwritten = h2_submit(&stream, cf, data, buf, len, &hdslen, err);
|
||||
if(nwritten < 0) {
|
||||
goto out;
|
||||
}
|
||||
DEBUGASSERT(stream);
|
||||
DEBUGASSERT(hdslen <= (size_t)nwritten);
|
||||
}
|
||||
|
||||
/* Call the nghttp2 send loop and flush to write ALL buffered data,
|
||||
|
@ -2275,18 +2221,26 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
|
|||
* frame buffer or our network out buffer. */
|
||||
size_t rwin = nghttp2_session_get_stream_remote_window_size(ctx->h2,
|
||||
stream->id);
|
||||
/* Whatever the cause, we need to return CURL_EAGAIN for this call.
|
||||
* We have unwritten state that needs us being invoked again and EAGAIN
|
||||
* is the only way to ensure that. */
|
||||
stream->upload_blocked_len = nwritten;
|
||||
/* At the start of a stream, we are called with request headers
|
||||
* and, possibly, parts of the body. Later, only body data.
|
||||
* If we cannot send pure body data, we EAGAIN. If there had been
|
||||
* header, we return that *they* have been written and remember the
|
||||
* block on the data length only. */
|
||||
stream->upload_blocked_len = ((size_t)nwritten) - hdslen;
|
||||
CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) BLOCK: win %u/%zu "
|
||||
"blocked_len=%zu",
|
||||
"hds_len=%zu blocked_len=%zu",
|
||||
stream->id, len,
|
||||
nghttp2_session_get_remote_window_size(ctx->h2), rwin,
|
||||
nwritten);
|
||||
*err = CURLE_AGAIN;
|
||||
nwritten = -1;
|
||||
goto out;
|
||||
hdslen, stream->upload_blocked_len);
|
||||
if(hdslen) {
|
||||
*err = CURLE_OK;
|
||||
nwritten = hdslen;
|
||||
}
|
||||
else {
|
||||
*err = CURLE_AGAIN;
|
||||
nwritten = -1;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else if(should_close_session(ctx)) {
|
||||
/* nghttp2 thinks this session is done. If the stream has not been
|
||||
|
@ -2340,7 +2294,7 @@ static void cf_h2_adjust_pollset(struct Curl_cfilter *cf,
|
|||
sock = Curl_conn_cf_get_socket(cf, data);
|
||||
Curl_pollset_check(data, ps, sock, &want_recv, &want_send);
|
||||
if(want_recv || want_send) {
|
||||
struct stream_ctx *stream = H2_STREAM_CTX(data);
|
||||
struct h2_stream_ctx *stream = H2_STREAM_CTX(data);
|
||||
struct cf_call_data save;
|
||||
bool c_exhaust, s_exhaust;
|
||||
|
||||
|
@ -2387,7 +2341,7 @@ static CURLcode cf_h2_connect(struct Curl_cfilter *cf,
|
|||
goto out;
|
||||
}
|
||||
|
||||
result = h2_progress_ingress(cf, data);
|
||||
result = h2_progress_ingress(cf, data, H2_CHUNK_SIZE);
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
|
@ -2441,7 +2395,7 @@ static CURLcode http2_data_pause(struct Curl_cfilter *cf,
|
|||
{
|
||||
#ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE
|
||||
struct cf_h2_ctx *ctx = cf->ctx;
|
||||
struct stream_ctx *stream = H2_STREAM_CTX(data);
|
||||
struct h2_stream_ctx *stream = H2_STREAM_CTX(data);
|
||||
|
||||
DEBUGASSERT(data);
|
||||
if(ctx && ctx->h2 && stream) {
|
||||
|
@ -2510,10 +2464,10 @@ static CURLcode cf_h2_cntrl(struct Curl_cfilter *cf,
|
|||
result = http2_data_done_send(cf, data);
|
||||
break;
|
||||
case CF_CTRL_DATA_DETACH:
|
||||
http2_data_done(cf, data, TRUE);
|
||||
http2_data_done(cf, data);
|
||||
break;
|
||||
case CF_CTRL_DATA_DONE:
|
||||
http2_data_done(cf, data, arg1 != 0);
|
||||
http2_data_done(cf, data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -2526,11 +2480,10 @@ static bool cf_h2_data_pending(struct Curl_cfilter *cf,
|
|||
const struct Curl_easy *data)
|
||||
{
|
||||
struct cf_h2_ctx *ctx = cf->ctx;
|
||||
struct stream_ctx *stream = H2_STREAM_CTX(data);
|
||||
struct h2_stream_ctx *stream = H2_STREAM_CTX(data);
|
||||
|
||||
if(ctx && (!Curl_bufq_is_empty(&ctx->inbufq)
|
||||
|| (stream && !Curl_bufq_is_empty(&stream->sendbuf))
|
||||
|| (stream && !Curl_bufq_is_empty(&stream->recvbuf))))
|
||||
|| (stream && !Curl_bufq_is_empty(&stream->sendbuf))))
|
||||
return TRUE;
|
||||
return cf->next? cf->next->cft->has_data_pending(cf->next, data) : FALSE;
|
||||
}
|
||||
|
@ -2615,7 +2568,8 @@ struct Curl_cftype Curl_cft_nghttp2 = {
|
|||
static CURLcode http2_cfilter_add(struct Curl_cfilter **pcf,
|
||||
struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
int sockindex)
|
||||
int sockindex,
|
||||
bool via_h1_upgrade)
|
||||
{
|
||||
struct Curl_cfilter *cf = NULL;
|
||||
struct cf_h2_ctx *ctx;
|
||||
|
@ -2630,8 +2584,9 @@ static CURLcode http2_cfilter_add(struct Curl_cfilter **pcf,
|
|||
if(result)
|
||||
goto out;
|
||||
|
||||
ctx = NULL;
|
||||
Curl_conn_cf_add(data, conn, sockindex, cf);
|
||||
result = CURLE_OK;
|
||||
result = cf_h2_ctx_init(cf, data, via_h1_upgrade);
|
||||
|
||||
out:
|
||||
if(result)
|
||||
|
@ -2641,7 +2596,8 @@ out:
|
|||
}
|
||||
|
||||
static CURLcode http2_cfilter_insert_after(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
struct Curl_easy *data,
|
||||
bool via_h1_upgrade)
|
||||
{
|
||||
struct Curl_cfilter *cf_h2 = NULL;
|
||||
struct cf_h2_ctx *ctx;
|
||||
|
@ -2656,8 +2612,9 @@ static CURLcode http2_cfilter_insert_after(struct Curl_cfilter *cf,
|
|||
if(result)
|
||||
goto out;
|
||||
|
||||
ctx = NULL;
|
||||
Curl_conn_cf_insert_after(cf, cf_h2);
|
||||
result = CURLE_OK;
|
||||
result = cf_h2_ctx_init(cf_h2, data, via_h1_upgrade);
|
||||
|
||||
out:
|
||||
if(result)
|
||||
|
@ -2714,11 +2671,7 @@ CURLcode Curl_http2_switch(struct Curl_easy *data,
|
|||
DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex));
|
||||
DEBUGF(infof(data, "switching to HTTP/2"));
|
||||
|
||||
result = http2_cfilter_add(&cf, data, conn, sockindex);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
result = cf_h2_ctx_init(cf, data, FALSE);
|
||||
result = http2_cfilter_add(&cf, data, conn, sockindex, FALSE);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
|
@ -2741,15 +2694,11 @@ CURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data)
|
|||
|
||||
DEBUGASSERT(!Curl_cf_is_http2(cf, data));
|
||||
|
||||
result = http2_cfilter_insert_after(cf, data);
|
||||
result = http2_cfilter_insert_after(cf, data, FALSE);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
cf_h2 = cf->next;
|
||||
result = cf_h2_ctx_init(cf_h2, data, FALSE);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
cf->conn->httpversion = 20; /* we know we're on HTTP/2 now */
|
||||
cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
|
||||
cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
|
||||
|
@ -2774,17 +2723,13 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data,
|
|||
DEBUGF(infof(data, "upgrading to HTTP/2"));
|
||||
DEBUGASSERT(data->req.upgr101 == UPGR101_RECEIVED);
|
||||
|
||||
result = http2_cfilter_add(&cf, data, conn, sockindex);
|
||||
result = http2_cfilter_add(&cf, data, conn, sockindex, TRUE);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
DEBUGASSERT(cf->cft == &Curl_cft_nghttp2);
|
||||
ctx = cf->ctx;
|
||||
|
||||
result = cf_h2_ctx_init(cf, data, TRUE);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
if(nread > 0) {
|
||||
/* Remaining data from the protocol switch reply is already using
|
||||
* the switched protocol, ie. HTTP/2. We add that to the network
|
||||
|
@ -2823,7 +2768,7 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data,
|
|||
CURLE_HTTP2_STREAM error! */
|
||||
bool Curl_h2_http_1_1_error(struct Curl_easy *data)
|
||||
{
|
||||
struct stream_ctx *stream = H2_STREAM_CTX(data);
|
||||
struct h2_stream_ctx *stream = H2_STREAM_CTX(data);
|
||||
return (stream && stream->error == NGHTTP2_HTTP_1_1_REQUIRED);
|
||||
}
|
||||
|
||||
|
|
|
@ -27,10 +27,12 @@
|
|||
#ifndef CURL_DISABLE_HTTP
|
||||
|
||||
#include "urldata.h" /* it includes http_chunks.h */
|
||||
#include "curl_printf.h"
|
||||
#include "sendf.h" /* for the client write stuff */
|
||||
#include "dynbuf.h"
|
||||
#include "content_encoding.h"
|
||||
#include "http.h"
|
||||
#include "multiif.h"
|
||||
#include "strtoofft.h"
|
||||
#include "warnless.h"
|
||||
|
||||
|
@ -152,9 +154,9 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data,
|
|||
ch->hexbuffer[ch->hexindex++] = *buf;
|
||||
buf++;
|
||||
blen--;
|
||||
(*pconsumed)++;
|
||||
}
|
||||
else {
|
||||
char *endptr;
|
||||
if(0 == ch->hexindex) {
|
||||
/* This is illegal data, we received junk where we expected
|
||||
a hexadecimal digit. */
|
||||
|
@ -166,7 +168,7 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data,
|
|||
|
||||
/* blen and buf are unmodified */
|
||||
ch->hexbuffer[ch->hexindex] = 0;
|
||||
if(curlx_strtoofft(ch->hexbuffer, &endptr, 16, &ch->datasize)) {
|
||||
if(curlx_strtoofft(ch->hexbuffer, NULL, 16, &ch->datasize)) {
|
||||
failf(data, "chunk hex-length not valid: '%s'", ch->hexbuffer);
|
||||
ch->state = CHUNK_FAILED;
|
||||
ch->last_code = CHUNKE_ILLEGAL_HEX;
|
||||
|
@ -189,6 +191,7 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data,
|
|||
|
||||
buf++;
|
||||
blen--;
|
||||
(*pconsumed)++;
|
||||
break;
|
||||
|
||||
case CHUNK_DATA:
|
||||
|
@ -236,6 +239,7 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data,
|
|||
}
|
||||
buf++;
|
||||
blen--;
|
||||
(*pconsumed)++;
|
||||
break;
|
||||
|
||||
case CHUNK_TRAILER:
|
||||
|
@ -293,6 +297,7 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data,
|
|||
}
|
||||
buf++;
|
||||
blen--;
|
||||
(*pconsumed)++;
|
||||
break;
|
||||
|
||||
case CHUNK_TRAILER_CR:
|
||||
|
@ -300,6 +305,7 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data,
|
|||
ch->state = CHUNK_TRAILER_POSTCR;
|
||||
buf++;
|
||||
blen--;
|
||||
(*pconsumed)++;
|
||||
}
|
||||
else {
|
||||
ch->state = CHUNK_FAILED;
|
||||
|
@ -320,6 +326,7 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data,
|
|||
/* skip if CR */
|
||||
buf++;
|
||||
blen--;
|
||||
(*pconsumed)++;
|
||||
}
|
||||
/* now wait for the final LF */
|
||||
ch->state = CHUNK_STOP;
|
||||
|
@ -328,6 +335,7 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data,
|
|||
case CHUNK_STOP:
|
||||
if(*buf == 0x0a) {
|
||||
blen--;
|
||||
(*pconsumed)++;
|
||||
/* Record the length of any data left in the end of the buffer
|
||||
even if there's no more chunks to read */
|
||||
ch->datasize = blen;
|
||||
|
@ -386,7 +394,7 @@ struct chunked_writer {
|
|||
static CURLcode cw_chunked_init(struct Curl_easy *data,
|
||||
struct Curl_cwriter *writer)
|
||||
{
|
||||
struct chunked_writer *ctx = (struct chunked_writer *)writer;
|
||||
struct chunked_writer *ctx = writer->ctx;
|
||||
|
||||
data->req.chunk = TRUE; /* chunks coming our way. */
|
||||
Curl_httpchunk_init(data, &ctx->ch, FALSE);
|
||||
|
@ -396,7 +404,7 @@ static CURLcode cw_chunked_init(struct Curl_easy *data,
|
|||
static void cw_chunked_close(struct Curl_easy *data,
|
||||
struct Curl_cwriter *writer)
|
||||
{
|
||||
struct chunked_writer *ctx = (struct chunked_writer *)writer;
|
||||
struct chunked_writer *ctx = writer->ctx;
|
||||
Curl_httpchunk_free(data, &ctx->ch);
|
||||
}
|
||||
|
||||
|
@ -404,7 +412,7 @@ static CURLcode cw_chunked_write(struct Curl_easy *data,
|
|||
struct Curl_cwriter *writer, int type,
|
||||
const char *buf, size_t blen)
|
||||
{
|
||||
struct chunked_writer *ctx = (struct chunked_writer *)writer;
|
||||
struct chunked_writer *ctx = writer->ctx;
|
||||
CURLcode result;
|
||||
size_t consumed;
|
||||
|
||||
|
@ -452,4 +460,207 @@ const struct Curl_cwtype Curl_httpchunk_unencoder = {
|
|||
sizeof(struct chunked_writer)
|
||||
};
|
||||
|
||||
/* max length of a HTTP chunk that we want to generate */
|
||||
#define CURL_CHUNKED_MINLEN (1024)
|
||||
#define CURL_CHUNKED_MAXLEN (64 * 1024)
|
||||
|
||||
struct chunked_reader {
|
||||
struct Curl_creader super;
|
||||
struct bufq chunkbuf;
|
||||
BIT(read_eos); /* we read an EOS from the next reader */
|
||||
BIT(eos); /* we have returned an EOS */
|
||||
};
|
||||
|
||||
static CURLcode cr_chunked_init(struct Curl_easy *data,
|
||||
struct Curl_creader *reader)
|
||||
{
|
||||
struct chunked_reader *ctx = reader->ctx;
|
||||
(void)data;
|
||||
Curl_bufq_init2(&ctx->chunkbuf, CURL_CHUNKED_MAXLEN, 2, BUFQ_OPT_SOFT_LIMIT);
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static void cr_chunked_close(struct Curl_easy *data,
|
||||
struct Curl_creader *reader)
|
||||
{
|
||||
struct chunked_reader *ctx = reader->ctx;
|
||||
(void)data;
|
||||
Curl_bufq_free(&ctx->chunkbuf);
|
||||
}
|
||||
|
||||
static CURLcode add_last_chunk(struct Curl_easy *data,
|
||||
struct Curl_creader *reader)
|
||||
{
|
||||
struct chunked_reader *ctx = reader->ctx;
|
||||
struct curl_slist *trailers = NULL, *tr;
|
||||
CURLcode result;
|
||||
size_t n;
|
||||
int rc;
|
||||
|
||||
if(!data->set.trailer_callback) {
|
||||
return Curl_bufq_cwrite(&ctx->chunkbuf, STRCONST("0\r\n\r\n"), &n);
|
||||
}
|
||||
|
||||
result = Curl_bufq_cwrite(&ctx->chunkbuf, STRCONST("0\r\n"), &n);
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
Curl_set_in_callback(data, true);
|
||||
rc = data->set.trailer_callback(&trailers, data->set.trailer_data);
|
||||
Curl_set_in_callback(data, false);
|
||||
|
||||
if(rc != CURL_TRAILERFUNC_OK) {
|
||||
failf(data, "operation aborted by trailing headers callback");
|
||||
result = CURLE_ABORTED_BY_CALLBACK;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for(tr = trailers; tr; tr = tr->next) {
|
||||
/* only add correctly formatted trailers */
|
||||
char *ptr = strchr(tr->data, ':');
|
||||
if(!ptr || *(ptr + 1) != ' ') {
|
||||
infof(data, "Malformatted trailing header, skipping trailer");
|
||||
continue;
|
||||
}
|
||||
|
||||
result = Curl_bufq_cwrite(&ctx->chunkbuf, tr->data,
|
||||
strlen(tr->data), &n);
|
||||
if(!result)
|
||||
result = Curl_bufq_cwrite(&ctx->chunkbuf, STRCONST("\r\n"), &n);
|
||||
if(result)
|
||||
goto out;
|
||||
}
|
||||
|
||||
result = Curl_bufq_cwrite(&ctx->chunkbuf, STRCONST("\r\n"), &n);
|
||||
|
||||
out:
|
||||
curl_slist_free_all(trailers);
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode add_chunk(struct Curl_easy *data,
|
||||
struct Curl_creader *reader,
|
||||
char *buf, size_t blen)
|
||||
{
|
||||
struct chunked_reader *ctx = reader->ctx;
|
||||
CURLcode result;
|
||||
char tmp[CURL_CHUNKED_MINLEN];
|
||||
size_t nread;
|
||||
bool eos;
|
||||
|
||||
DEBUGASSERT(!ctx->read_eos);
|
||||
blen = CURLMIN(blen, CURL_CHUNKED_MAXLEN); /* respect our buffer pref */
|
||||
if(blen < sizeof(tmp)) {
|
||||
/* small read, make a chunk of decent size */
|
||||
buf = tmp;
|
||||
blen = sizeof(tmp);
|
||||
}
|
||||
else {
|
||||
/* larger read, make a chunk that will fit when read back */
|
||||
blen -= (8 + 2 + 2); /* deduct max overhead, 8 hex + 2*crlf */
|
||||
}
|
||||
|
||||
result = Curl_creader_read(data, reader->next, buf, blen, &nread, &eos);
|
||||
if(result)
|
||||
return result;
|
||||
if(eos)
|
||||
ctx->read_eos = TRUE;
|
||||
|
||||
if(nread) {
|
||||
/* actually got bytes, wrap them into the chunkbuf */
|
||||
char hd[11] = "";
|
||||
int hdlen;
|
||||
size_t n;
|
||||
|
||||
hdlen = msnprintf(hd, sizeof(hd), "%zx\r\n", nread);
|
||||
if(hdlen <= 0)
|
||||
return CURLE_READ_ERROR;
|
||||
/* On a soft-limited bufq, we do not need to check that all was written */
|
||||
result = Curl_bufq_cwrite(&ctx->chunkbuf, hd, hdlen, &n);
|
||||
if(!result)
|
||||
result = Curl_bufq_cwrite(&ctx->chunkbuf, buf, nread, &n);
|
||||
if(!result)
|
||||
result = Curl_bufq_cwrite(&ctx->chunkbuf, "\r\n", 2, &n);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
|
||||
if(ctx->read_eos)
|
||||
return add_last_chunk(data, reader);
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static CURLcode cr_chunked_read(struct Curl_easy *data,
|
||||
struct Curl_creader *reader,
|
||||
char *buf, size_t blen,
|
||||
size_t *pnread, bool *peos)
|
||||
{
|
||||
struct chunked_reader *ctx = reader->ctx;
|
||||
CURLcode result = CURLE_READ_ERROR;
|
||||
|
||||
*pnread = 0;
|
||||
*peos = ctx->eos;
|
||||
|
||||
if(!ctx->eos) {
|
||||
if(!ctx->read_eos && Curl_bufq_is_empty(&ctx->chunkbuf)) {
|
||||
/* Still getting data form the next reader, buffer is empty */
|
||||
result = add_chunk(data, reader, buf, blen);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
|
||||
if(!Curl_bufq_is_empty(&ctx->chunkbuf)) {
|
||||
result = Curl_bufq_cread(&ctx->chunkbuf, buf, blen, pnread);
|
||||
if(!result && ctx->read_eos && Curl_bufq_is_empty(&ctx->chunkbuf)) {
|
||||
/* no more data, read all, done. */
|
||||
ctx->eos = TRUE;
|
||||
*peos = TRUE;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
/* We may get here, because we are done or because callbacks paused */
|
||||
DEBUGASSERT(ctx->eos || !ctx->read_eos);
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static curl_off_t cr_chunked_total_length(struct Curl_easy *data,
|
||||
struct Curl_creader *reader)
|
||||
{
|
||||
/* this reader changes length depending on input */
|
||||
(void)data;
|
||||
(void)reader;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* HTTP chunked Transfer-Encoding encoder */
|
||||
const struct Curl_crtype Curl_httpchunk_encoder = {
|
||||
"chunked",
|
||||
cr_chunked_init,
|
||||
cr_chunked_read,
|
||||
cr_chunked_close,
|
||||
Curl_creader_def_needs_rewind,
|
||||
cr_chunked_total_length,
|
||||
Curl_creader_def_resume_from,
|
||||
Curl_creader_def_rewind,
|
||||
Curl_creader_def_unpause,
|
||||
Curl_creader_def_done,
|
||||
sizeof(struct chunked_reader)
|
||||
};
|
||||
|
||||
CURLcode Curl_httpchunk_add_reader(struct Curl_easy *data)
|
||||
{
|
||||
struct Curl_creader *reader = NULL;
|
||||
CURLcode result;
|
||||
|
||||
result = Curl_creader_create(&reader, data, &Curl_httpchunk_encoder,
|
||||
CURL_CR_TRANSFER_ENCODE);
|
||||
if(!result)
|
||||
result = Curl_creader_add(data, reader);
|
||||
|
||||
if(result && reader)
|
||||
Curl_creader_free(data, reader);
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif /* CURL_DISABLE_HTTP */
|
||||
|
|
|
@ -133,6 +133,13 @@ bool Curl_httpchunk_is_done(struct Curl_easy *data, struct Curl_chunker *ch);
|
|||
|
||||
extern const struct Curl_cwtype Curl_httpchunk_unencoder;
|
||||
|
||||
extern const struct Curl_crtype Curl_httpchunk_encoder;
|
||||
|
||||
/**
|
||||
* Add a transfer-encoding "chunked" reader to the transfers reader stack
|
||||
*/
|
||||
CURLcode Curl_httpchunk_add_reader(struct Curl_easy *data);
|
||||
|
||||
#endif /* !CURL_DISABLE_HTTP */
|
||||
|
||||
#endif /* HEADER_CURL_HTTP_CHUNKS_H */
|
||||
|
|
36
lib/imap.c
36
lib/imap.c
|
@ -770,6 +770,7 @@ static CURLcode imap_perform_append(struct Curl_easy *data)
|
|||
return CURLE_URL_MALFORMAT;
|
||||
}
|
||||
|
||||
#ifndef CURL_DISABLE_MIME
|
||||
/* Prepare the mime data if some. */
|
||||
if(data->set.mimepost.kind != MIMEKIND_NONE) {
|
||||
/* Use the whole structure as data. */
|
||||
|
@ -785,18 +786,18 @@ static CURLcode imap_perform_append(struct Curl_easy *data)
|
|||
result = Curl_mime_add_header(&data->set.mimepost.curlheaders,
|
||||
"Mime-Version: 1.0");
|
||||
|
||||
/* Make sure we will read the entire mime structure. */
|
||||
if(!result)
|
||||
result = Curl_mime_rewind(&data->set.mimepost);
|
||||
|
||||
result = Curl_creader_set_mime(data, &data->set.mimepost);
|
||||
if(result)
|
||||
return result;
|
||||
data->state.infilesize = Curl_creader_client_length(data);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
result = Curl_creader_set_fread(data, data->state.infilesize);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
data->state.infilesize = Curl_mime_size(&data->set.mimepost);
|
||||
|
||||
/* Read from mime structure. */
|
||||
data->state.fread_func = (curl_read_callback) Curl_mime_read;
|
||||
data->state.in = (void *) &data->set.mimepost;
|
||||
}
|
||||
|
||||
/* Check we know the size of the upload */
|
||||
|
@ -1211,14 +1212,14 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data,
|
|||
|
||||
if(data->req.bytecount == size)
|
||||
/* The entire data is already transferred! */
|
||||
Curl_setup_transfer(data, -1, -1, FALSE, -1);
|
||||
Curl_xfer_setup(data, -1, -1, FALSE, -1);
|
||||
else {
|
||||
/* IMAP download */
|
||||
data->req.maxdownload = size;
|
||||
/* force a recv/send check of this connection, as the data might've been
|
||||
read off the socket already */
|
||||
data->state.select_bits = CURL_CSELECT_IN;
|
||||
Curl_setup_transfer(data, FIRSTSOCKET, size, FALSE, -1);
|
||||
Curl_xfer_setup(data, FIRSTSOCKET, size, FALSE, -1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -1266,7 +1267,7 @@ static CURLcode imap_state_append_resp(struct Curl_easy *data, int imapcode,
|
|||
Curl_pgrsSetUploadSize(data, data->state.infilesize);
|
||||
|
||||
/* IMAP upload */
|
||||
Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
|
||||
Curl_xfer_setup(data, -1, -1, FALSE, FIRSTSOCKET);
|
||||
|
||||
/* End of DO phase */
|
||||
imap_state(data, IMAP_STOP);
|
||||
|
@ -1297,7 +1298,6 @@ static CURLcode imap_statemachine(struct Curl_easy *data,
|
|||
struct connectdata *conn)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
curl_socket_t sock = conn->sock[FIRSTSOCKET];
|
||||
int imapcode;
|
||||
struct imap_conn *imapc = &conn->proto.imapc;
|
||||
struct pingpong *pp = &imapc->pp;
|
||||
|
@ -1314,7 +1314,7 @@ static CURLcode imap_statemachine(struct Curl_easy *data,
|
|||
|
||||
do {
|
||||
/* Read the response from the server */
|
||||
result = Curl_pp_readresp(data, sock, pp, &imapcode, &nread);
|
||||
result = Curl_pp_readresp(data, FIRSTSOCKET, pp, &imapcode, &nread);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
|
@ -1513,10 +1513,10 @@ static CURLcode imap_done(struct Curl_easy *data, CURLcode status,
|
|||
}
|
||||
else if(!data->set.connect_only && !imap->custom &&
|
||||
(imap->uid || imap->mindex || data->state.upload ||
|
||||
data->set.mimepost.kind != MIMEKIND_NONE)) {
|
||||
IS_MIME_POST(data))) {
|
||||
/* Handle responses after FETCH or APPEND transfer has finished */
|
||||
|
||||
if(!data->state.upload && data->set.mimepost.kind == MIMEKIND_NONE)
|
||||
if(!data->state.upload && !IS_MIME_POST(data))
|
||||
imap_state(data, IMAP_FETCH_FINAL);
|
||||
else {
|
||||
/* End the APPEND command first by sending an empty line */
|
||||
|
@ -1582,7 +1582,7 @@ static CURLcode imap_perform(struct Curl_easy *data, bool *connected,
|
|||
selected = TRUE;
|
||||
|
||||
/* Start the first command in the DO phase */
|
||||
if(data->state.upload || data->set.mimepost.kind != MIMEKIND_NONE)
|
||||
if(data->state.upload || IS_MIME_POST(data))
|
||||
/* APPEND can be executed directly */
|
||||
result = imap_perform_append(data);
|
||||
else if(imap->custom && (selected || !imap->mailbox))
|
||||
|
@ -1692,7 +1692,7 @@ static CURLcode imap_dophase_done(struct Curl_easy *data, bool connected)
|
|||
|
||||
if(imap->transfer != PPTRANSFER_BODY)
|
||||
/* no data to transfer */
|
||||
Curl_setup_transfer(data, -1, -1, FALSE, -1);
|
||||
Curl_xfer_setup(data, -1, -1, FALSE, -1);
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
|
25
lib/krb5.c
25
lib/krb5.c
|
@ -52,6 +52,7 @@
|
|||
#include "ftp.h"
|
||||
#include "curl_gssapi.h"
|
||||
#include "sendf.h"
|
||||
#include "transfer.h"
|
||||
#include "curl_krb5.h"
|
||||
#include "warnless.h"
|
||||
#include "strcase.h"
|
||||
|
@ -65,7 +66,7 @@
|
|||
static CURLcode ftpsend(struct Curl_easy *data, struct connectdata *conn,
|
||||
const char *cmd)
|
||||
{
|
||||
ssize_t bytes_written;
|
||||
size_t bytes_written;
|
||||
#define SBUF_SIZE 1024
|
||||
char s[SBUF_SIZE];
|
||||
size_t write_len;
|
||||
|
@ -90,8 +91,7 @@ static CURLcode ftpsend(struct Curl_easy *data, struct connectdata *conn,
|
|||
#ifdef HAVE_GSSAPI
|
||||
conn->data_prot = PROT_CMD;
|
||||
#endif
|
||||
result = Curl_nwrite(data, FIRSTSOCKET, sptr, write_len,
|
||||
&bytes_written);
|
||||
result = Curl_xfer_send(data, sptr, write_len, &bytes_written);
|
||||
#ifdef HAVE_GSSAPI
|
||||
DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST);
|
||||
conn->data_prot = data_sec;
|
||||
|
@ -100,9 +100,9 @@ static CURLcode ftpsend(struct Curl_easy *data, struct connectdata *conn,
|
|||
if(result)
|
||||
break;
|
||||
|
||||
Curl_debug(data, CURLINFO_HEADER_OUT, sptr, (size_t)bytes_written);
|
||||
Curl_debug(data, CURLINFO_HEADER_OUT, sptr, bytes_written);
|
||||
|
||||
if(bytes_written != (ssize_t)write_len) {
|
||||
if(bytes_written != write_len) {
|
||||
write_len -= bytes_written;
|
||||
sptr += bytes_written;
|
||||
}
|
||||
|
@ -470,7 +470,7 @@ socket_read(struct Curl_easy *data, int sockindex, void *to, size_t len)
|
|||
ssize_t nread = 0;
|
||||
|
||||
while(len > 0) {
|
||||
nread = Curl_conn_recv(data, sockindex, to_p, len, &result);
|
||||
result = Curl_conn_recv(data, sockindex, to_p, len, &nread);
|
||||
if(nread > 0) {
|
||||
len -= nread;
|
||||
to_p += nread;
|
||||
|
@ -494,11 +494,11 @@ socket_write(struct Curl_easy *data, int sockindex, const void *to,
|
|||
{
|
||||
const char *to_p = to;
|
||||
CURLcode result;
|
||||
ssize_t written;
|
||||
size_t written;
|
||||
|
||||
while(len > 0) {
|
||||
written = Curl_conn_send(data, sockindex, to_p, len, &result);
|
||||
if(written > 0) {
|
||||
result = Curl_conn_send(data, sockindex, to_p, len, &written);
|
||||
if(!result && written > 0) {
|
||||
len -= written;
|
||||
to_p += written;
|
||||
}
|
||||
|
@ -567,8 +567,11 @@ static ssize_t sec_recv(struct Curl_easy *data, int sockindex,
|
|||
*err = CURLE_OK;
|
||||
|
||||
/* Handle clear text response. */
|
||||
if(conn->sec_complete == 0 || conn->data_prot == PROT_CLEAR)
|
||||
return Curl_conn_recv(data, sockindex, buffer, len, err);
|
||||
if(conn->sec_complete == 0 || conn->data_prot == PROT_CLEAR) {
|
||||
ssize_t nread;
|
||||
*err = Curl_conn_recv(data, sockindex, buffer, len, &nread);
|
||||
return nread;
|
||||
}
|
||||
|
||||
if(conn->in_buffer.eof_flag) {
|
||||
conn->in_buffer.eof_flag = 0;
|
||||
|
|
16
lib/ldap.c
16
lib/ldap.c
|
@ -371,7 +371,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
|
|||
#ifdef HAVE_LDAP_SSL
|
||||
#ifdef USE_WIN32_LDAP
|
||||
/* Win32 LDAP SDK doesn't support insecure mode without CA! */
|
||||
server = ldap_sslinit(host, conn->port, 1);
|
||||
server = ldap_sslinit(host, conn->primary.remote_port, 1);
|
||||
ldap_set_option(server, LDAP_OPT_SSL, LDAP_OPT_ON);
|
||||
#else
|
||||
int ldap_option;
|
||||
|
@ -417,10 +417,10 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
|
|||
result = CURLE_SSL_CERTPROBLEM;
|
||||
goto quit;
|
||||
}
|
||||
server = ldapssl_init(host, conn->port, 1);
|
||||
server = ldapssl_init(host, conn->primary.remote_port, 1);
|
||||
if(!server) {
|
||||
failf(data, "LDAP local: Cannot connect to %s:%u",
|
||||
conn->host.dispname, conn->port);
|
||||
conn->host.dispname, conn->primary.remote_port);
|
||||
result = CURLE_COULDNT_CONNECT;
|
||||
goto quit;
|
||||
}
|
||||
|
@ -458,10 +458,10 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
|
|||
result = CURLE_SSL_CERTPROBLEM;
|
||||
goto quit;
|
||||
}
|
||||
server = ldap_init(host, conn->port);
|
||||
server = ldap_init(host, conn->primary.remote_port);
|
||||
if(!server) {
|
||||
failf(data, "LDAP local: Cannot connect to %s:%u",
|
||||
conn->host.dispname, conn->port);
|
||||
conn->host.dispname, conn->primary.remote_port);
|
||||
result = CURLE_COULDNT_CONNECT;
|
||||
goto quit;
|
||||
}
|
||||
|
@ -499,10 +499,10 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
|
|||
goto quit;
|
||||
}
|
||||
else {
|
||||
server = ldap_init(host, conn->port);
|
||||
server = ldap_init(host, conn->primary.remote_port);
|
||||
if(!server) {
|
||||
failf(data, "LDAP local: Cannot connect to %s:%u",
|
||||
conn->host.dispname, conn->port);
|
||||
conn->host.dispname, conn->primary.remote_port);
|
||||
result = CURLE_COULDNT_CONNECT;
|
||||
goto quit;
|
||||
}
|
||||
|
@ -749,7 +749,7 @@ quit:
|
|||
FREE_ON_WINLDAP(host);
|
||||
|
||||
/* no data to transfer */
|
||||
Curl_setup_transfer(data, -1, -1, FALSE, -1);
|
||||
Curl_xfer_setup(data, -1, -1, FALSE, -1);
|
||||
connclose(conn, "LDAP connection always disable reuse");
|
||||
|
||||
return result;
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
#include <string.h>
|
||||
|
||||
#include "strdup.h"
|
||||
#include "curl_md4.h"
|
||||
#include "warnless.h"
|
||||
|
||||
|
|
227
lib/mime.c
227
lib/mime.c
|
@ -74,6 +74,7 @@ static curl_off_t encoder_base64_size(curl_mimepart *part);
|
|||
static size_t encoder_qp_read(char *buffer, size_t size, bool ateof,
|
||||
curl_mimepart *part);
|
||||
static curl_off_t encoder_qp_size(curl_mimepart *part);
|
||||
static curl_off_t mime_size(curl_mimepart *part);
|
||||
|
||||
static const struct mime_encoder encoders[] = {
|
||||
{"binary", encoder_nop_read, encoder_nop_size},
|
||||
|
@ -1602,7 +1603,7 @@ size_t Curl_mime_read(char *buffer, size_t size, size_t nitems, void *instream)
|
|||
}
|
||||
|
||||
/* Rewind mime stream. */
|
||||
CURLcode Curl_mime_rewind(curl_mimepart *part)
|
||||
static CURLcode mime_rewind(curl_mimepart *part)
|
||||
{
|
||||
return mime_part_rewind(part) == CURL_SEEKFUNC_OK?
|
||||
CURLE_OK: CURLE_SEND_FAIL_REWIND;
|
||||
|
@ -1634,7 +1635,7 @@ static curl_off_t multipart_size(curl_mime *mime)
|
|||
size = boundarysize; /* Final boundary - CRLF after headers. */
|
||||
|
||||
for(part = mime->firstpart; part; part = part->nextpart) {
|
||||
curl_off_t sz = Curl_mime_size(part);
|
||||
curl_off_t sz = mime_size(part);
|
||||
|
||||
if(sz < 0)
|
||||
size = sz;
|
||||
|
@ -1647,7 +1648,7 @@ static curl_off_t multipart_size(curl_mime *mime)
|
|||
}
|
||||
|
||||
/* Get/compute mime size. */
|
||||
curl_off_t Curl_mime_size(curl_mimepart *part)
|
||||
static curl_off_t mime_size(curl_mimepart *part)
|
||||
{
|
||||
curl_off_t size;
|
||||
|
||||
|
@ -1896,7 +1897,7 @@ CURLcode Curl_mime_prepare_headers(struct Curl_easy *data,
|
|||
}
|
||||
|
||||
/* Recursively reset paused status in the given part. */
|
||||
void Curl_mime_unpause(curl_mimepart *part)
|
||||
static void mime_unpause(curl_mimepart *part)
|
||||
{
|
||||
if(part) {
|
||||
if(part->lastreadstatus == CURL_READFUNC_PAUSE)
|
||||
|
@ -1908,12 +1909,228 @@ void Curl_mime_unpause(curl_mimepart *part)
|
|||
curl_mimepart *subpart;
|
||||
|
||||
for(subpart = mime->firstpart; subpart; subpart = subpart->nextpart)
|
||||
Curl_mime_unpause(subpart);
|
||||
mime_unpause(subpart);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct cr_mime_ctx {
|
||||
struct Curl_creader super;
|
||||
curl_mimepart *part;
|
||||
curl_off_t total_len;
|
||||
curl_off_t read_len;
|
||||
CURLcode error_result;
|
||||
BIT(seen_eos);
|
||||
BIT(errored);
|
||||
};
|
||||
|
||||
static CURLcode cr_mime_init(struct Curl_easy *data,
|
||||
struct Curl_creader *reader)
|
||||
{
|
||||
struct cr_mime_ctx *ctx = reader->ctx;
|
||||
(void)data;
|
||||
ctx->total_len = -1;
|
||||
ctx->read_len = 0;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/* Real client reader to installed client callbacks. */
|
||||
static CURLcode cr_mime_read(struct Curl_easy *data,
|
||||
struct Curl_creader *reader,
|
||||
char *buf, size_t blen,
|
||||
size_t *pnread, bool *peos)
|
||||
{
|
||||
struct cr_mime_ctx *ctx = reader->ctx;
|
||||
size_t nread;
|
||||
|
||||
/* Once we have errored, we will return the same error forever */
|
||||
if(ctx->errored) {
|
||||
*pnread = 0;
|
||||
*peos = FALSE;
|
||||
return ctx->error_result;
|
||||
}
|
||||
if(ctx->seen_eos) {
|
||||
*pnread = 0;
|
||||
*peos = TRUE;
|
||||
return CURLE_OK;
|
||||
}
|
||||
/* respect length limitations */
|
||||
if(ctx->total_len >= 0) {
|
||||
curl_off_t remain = ctx->total_len - ctx->read_len;
|
||||
if(remain <= 0)
|
||||
blen = 0;
|
||||
else if(remain < (curl_off_t)blen)
|
||||
blen = (size_t)remain;
|
||||
}
|
||||
nread = 0;
|
||||
if(blen) {
|
||||
nread = Curl_mime_read(buf, 1, blen, ctx->part);
|
||||
}
|
||||
|
||||
switch(nread) {
|
||||
case 0:
|
||||
if((ctx->total_len >= 0) && (ctx->read_len < ctx->total_len)) {
|
||||
failf(data, "client mime read EOF fail, only "
|
||||
"only %"CURL_FORMAT_CURL_OFF_T"/%"CURL_FORMAT_CURL_OFF_T
|
||||
" of needed bytes read", ctx->read_len, ctx->total_len);
|
||||
return CURLE_READ_ERROR;
|
||||
}
|
||||
*pnread = 0;
|
||||
*peos = TRUE;
|
||||
ctx->seen_eos = TRUE;
|
||||
break;
|
||||
|
||||
case CURL_READFUNC_ABORT:
|
||||
failf(data, "operation aborted by callback");
|
||||
*pnread = 0;
|
||||
*peos = FALSE;
|
||||
ctx->errored = TRUE;
|
||||
ctx->error_result = CURLE_ABORTED_BY_CALLBACK;
|
||||
return CURLE_ABORTED_BY_CALLBACK;
|
||||
|
||||
case CURL_READFUNC_PAUSE:
|
||||
/* CURL_READFUNC_PAUSE pauses read callbacks that feed socket writes */
|
||||
data->req.keepon |= KEEP_SEND_PAUSE; /* mark socket send as paused */
|
||||
*pnread = 0;
|
||||
*peos = FALSE;
|
||||
break; /* nothing was read */
|
||||
|
||||
default:
|
||||
if(nread > blen) {
|
||||
/* the read function returned a too large value */
|
||||
failf(data, "read function returned funny value");
|
||||
*pnread = 0;
|
||||
*peos = FALSE;
|
||||
ctx->errored = TRUE;
|
||||
ctx->error_result = CURLE_READ_ERROR;
|
||||
return CURLE_READ_ERROR;
|
||||
}
|
||||
ctx->read_len += nread;
|
||||
if(ctx->total_len >= 0)
|
||||
ctx->seen_eos = (ctx->read_len >= ctx->total_len);
|
||||
*pnread = nread;
|
||||
*peos = ctx->seen_eos;
|
||||
break;
|
||||
}
|
||||
DEBUGF(infof(data, "cr_mime_read(len=%zu, total=%"CURL_FORMAT_CURL_OFF_T
|
||||
", read=%"CURL_FORMAT_CURL_OFF_T") -> %d, %zu, %d",
|
||||
blen, ctx->total_len, ctx->read_len, CURLE_OK, *pnread, *peos));
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static bool cr_mime_needs_rewind(struct Curl_easy *data,
|
||||
struct Curl_creader *reader)
|
||||
{
|
||||
struct cr_mime_ctx *ctx = reader->ctx;
|
||||
(void)data;
|
||||
return ctx->read_len > 0;
|
||||
}
|
||||
|
||||
static curl_off_t cr_mime_total_length(struct Curl_easy *data,
|
||||
struct Curl_creader *reader)
|
||||
{
|
||||
struct cr_mime_ctx *ctx = reader->ctx;
|
||||
(void)data;
|
||||
return ctx->total_len;
|
||||
}
|
||||
|
||||
static CURLcode cr_mime_resume_from(struct Curl_easy *data,
|
||||
struct Curl_creader *reader,
|
||||
curl_off_t offset)
|
||||
{
|
||||
struct cr_mime_ctx *ctx = reader->ctx;
|
||||
|
||||
if(offset > 0) {
|
||||
curl_off_t passed = 0;
|
||||
|
||||
do {
|
||||
char scratch[4*1024];
|
||||
size_t readthisamountnow =
|
||||
(offset - passed > (curl_off_t)sizeof(scratch)) ?
|
||||
sizeof(scratch) :
|
||||
curlx_sotouz(offset - passed);
|
||||
size_t nread;
|
||||
|
||||
nread = Curl_mime_read(scratch, 1, readthisamountnow, ctx->part);
|
||||
passed += (curl_off_t)nread;
|
||||
if((nread == 0) || (nread > readthisamountnow)) {
|
||||
/* this checks for greater-than only to make sure that the
|
||||
CURL_READFUNC_ABORT return code still aborts */
|
||||
failf(data, "Could only read %" CURL_FORMAT_CURL_OFF_T
|
||||
" bytes from the mime post", passed);
|
||||
return CURLE_READ_ERROR;
|
||||
}
|
||||
} while(passed < offset);
|
||||
|
||||
/* now, decrease the size of the read */
|
||||
if(ctx->total_len > 0) {
|
||||
ctx->total_len -= offset;
|
||||
|
||||
if(ctx->total_len <= 0) {
|
||||
failf(data, "Mime post already completely uploaded");
|
||||
return CURLE_PARTIAL_FILE;
|
||||
}
|
||||
}
|
||||
/* we've passed, proceed as normal */
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static CURLcode cr_mime_rewind(struct Curl_easy *data,
|
||||
struct Curl_creader *reader)
|
||||
{
|
||||
struct cr_mime_ctx *ctx = reader->ctx;
|
||||
CURLcode result = mime_rewind(ctx->part);
|
||||
if(result)
|
||||
failf(data, "Cannot rewind mime/post data");
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode cr_mime_unpause(struct Curl_easy *data,
|
||||
struct Curl_creader *reader)
|
||||
{
|
||||
struct cr_mime_ctx *ctx = reader->ctx;
|
||||
(void)data;
|
||||
mime_unpause(ctx->part);
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static const struct Curl_crtype cr_mime = {
|
||||
"cr-mime",
|
||||
cr_mime_init,
|
||||
cr_mime_read,
|
||||
Curl_creader_def_close,
|
||||
cr_mime_needs_rewind,
|
||||
cr_mime_total_length,
|
||||
cr_mime_resume_from,
|
||||
cr_mime_rewind,
|
||||
cr_mime_unpause,
|
||||
Curl_creader_def_done,
|
||||
sizeof(struct cr_mime_ctx)
|
||||
};
|
||||
|
||||
CURLcode Curl_creader_set_mime(struct Curl_easy *data, curl_mimepart *part)
|
||||
{
|
||||
struct Curl_creader *r;
|
||||
struct cr_mime_ctx *ctx;
|
||||
CURLcode result;
|
||||
|
||||
result = Curl_creader_create(&r, data, &cr_mime, CURL_CR_CLIENT);
|
||||
if(result)
|
||||
return result;
|
||||
ctx = r->ctx;
|
||||
ctx->part = part;
|
||||
/* Make sure we will read the entire mime structure. */
|
||||
result = mime_rewind(ctx->part);
|
||||
if(result) {
|
||||
Curl_creader_free(data, r);
|
||||
return result;
|
||||
}
|
||||
ctx->total_len = mime_size(ctx->part);
|
||||
|
||||
return Curl_creader_set(data, r);
|
||||
}
|
||||
|
||||
#else /* !CURL_DISABLE_MIME && (!CURL_DISABLE_HTTP ||
|
||||
!CURL_DISABLE_SMTP || !CURL_DISABLE_IMAP) */
|
||||
|
|
13
lib/mime.h
13
lib/mime.h
|
@ -151,12 +151,15 @@ CURLcode Curl_mime_prepare_headers(struct Curl_easy *data,
|
|||
const char *contenttype,
|
||||
const char *disposition,
|
||||
enum mimestrategy strategy);
|
||||
curl_off_t Curl_mime_size(struct curl_mimepart *part);
|
||||
size_t Curl_mime_read(char *buffer, size_t size, size_t nitems,
|
||||
void *instream);
|
||||
CURLcode Curl_mime_rewind(struct curl_mimepart *part);
|
||||
const char *Curl_mime_contenttype(const char *filename);
|
||||
void Curl_mime_unpause(struct curl_mimepart *part);
|
||||
|
||||
/**
|
||||
* Install a client reader as upload source that reads the given
|
||||
* mime part.
|
||||
*/
|
||||
CURLcode Curl_creader_set_mime(struct Curl_easy *data, curl_mimepart *part);
|
||||
|
||||
#else
|
||||
/* if disabled */
|
||||
|
@ -165,10 +168,8 @@ void Curl_mime_unpause(struct curl_mimepart *part);
|
|||
#define Curl_mime_duppart(x,y,z) CURLE_OK /* Nothing to duplicate. Succeed */
|
||||
#define Curl_mime_set_subparts(a,b,c) CURLE_NOT_BUILT_IN
|
||||
#define Curl_mime_prepare_headers(a,b,c,d,e) CURLE_NOT_BUILT_IN
|
||||
#define Curl_mime_size(x) (curl_off_t) -1
|
||||
#define Curl_mime_read NULL
|
||||
#define Curl_mime_rewind(x) ((void)x, CURLE_NOT_BUILT_IN)
|
||||
#define Curl_mime_unpause(x)
|
||||
#define Curl_creader_set_mime(x,y) ((void)x, CURLE_NOT_BUILT_IN)
|
||||
#endif
|
||||
|
||||
|
||||
|
|
|
@ -48,16 +48,6 @@
|
|||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Non-ANSI integer extensions
|
||||
*/
|
||||
|
||||
#if (defined(_WIN32_WCE)) || \
|
||||
(defined(__MINGW32__)) || \
|
||||
(defined(_MSC_VER) && (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64))
|
||||
# define MP_HAVE_INT_EXTENSIONS
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Max integer data types that mprintf.c is capable
|
||||
*/
|
||||
|
@ -349,8 +339,9 @@ static int parsefmt(const char *format,
|
|||
case 'h':
|
||||
flags |= FLAGS_SHORT;
|
||||
break;
|
||||
#if defined(MP_HAVE_INT_EXTENSIONS)
|
||||
#if defined(_WIN32) || defined(_WIN32_WCE)
|
||||
case 'I':
|
||||
/* Non-ANSI integer extensions I32 I64 */
|
||||
if((fmt[0] == '3') && (fmt[1] == '2')) {
|
||||
flags |= FLAGS_LONG;
|
||||
fmt += 2;
|
||||
|
@ -367,7 +358,7 @@ static int parsefmt(const char *format,
|
|||
#endif
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#endif /* _WIN32 || _WIN32_WCE */
|
||||
case 'l':
|
||||
if(flags & FLAGS_LONG)
|
||||
flags |= FLAGS_LONGLONG;
|
||||
|
@ -651,7 +642,7 @@ static int parsefmt(const char *format,
|
|||
* On success, the input array describes the type of all arguments and their
|
||||
* values.
|
||||
*
|
||||
* The function then iterates over the output sengments and outputs them one
|
||||
* The function then iterates over the output segments and outputs them one
|
||||
* by one until done. Using the appropriate input arguments (if any).
|
||||
*
|
||||
* All output is sent to the 'stream()' callback, one byte at a time.
|
||||
|
|
17
lib/mqtt.c
17
lib/mqtt.c
|
@ -119,12 +119,12 @@ static CURLcode mqtt_send(struct Curl_easy *data,
|
|||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
struct MQTT *mq = data->req.p.mqtt;
|
||||
ssize_t n;
|
||||
result = Curl_nwrite(data, FIRSTSOCKET, buf, len, &n);
|
||||
size_t n;
|
||||
result = Curl_xfer_send(data, buf, len, &n);
|
||||
if(result)
|
||||
return result;
|
||||
Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)n);
|
||||
if(len != (size_t)n) {
|
||||
if(len != n) {
|
||||
size_t nsend = len - n;
|
||||
char *sendleftovers = Curl_memdup(&buf[n], nsend);
|
||||
if(!sendleftovers)
|
||||
|
@ -366,8 +366,7 @@ static CURLcode mqtt_recv_atleast(struct Curl_easy *data, size_t nbytes)
|
|||
ssize_t nread;
|
||||
|
||||
DEBUGASSERT(nbytes - rlen < sizeof(readbuf));
|
||||
result = Curl_read(data, data->conn->sock[FIRSTSOCKET],
|
||||
(char *)readbuf, nbytes - rlen, &nread);
|
||||
result = Curl_xfer_recv(data, (char *)readbuf, nbytes - rlen, &nread);
|
||||
if(result)
|
||||
return result;
|
||||
DEBUGASSERT(nread >= 0);
|
||||
|
@ -622,7 +621,6 @@ static CURLcode mqtt_read_publish(struct Curl_easy *data, bool *done)
|
|||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
struct connectdata *conn = data->conn;
|
||||
curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
|
||||
ssize_t nread;
|
||||
size_t remlen;
|
||||
struct mqtt_conn *mqtt = &conn->proto.mqtt;
|
||||
|
@ -679,7 +677,7 @@ MQTT_SUBACK_COMING:
|
|||
size_t rest = mq->npacket;
|
||||
if(rest > sizeof(buffer))
|
||||
rest = sizeof(buffer);
|
||||
result = Curl_read(data, sockfd, buffer, rest, &nread);
|
||||
result = Curl_xfer_recv(data, buffer, rest, &nread);
|
||||
if(result) {
|
||||
if(CURLE_AGAIN == result) {
|
||||
infof(data, "EEEE AAAAGAIN");
|
||||
|
@ -744,7 +742,6 @@ static CURLcode mqtt_doing(struct Curl_easy *data, bool *done)
|
|||
struct mqtt_conn *mqtt = &conn->proto.mqtt;
|
||||
struct MQTT *mq = data->req.p.mqtt;
|
||||
ssize_t nread;
|
||||
curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
|
||||
unsigned char byte;
|
||||
|
||||
*done = FALSE;
|
||||
|
@ -762,7 +759,7 @@ static CURLcode mqtt_doing(struct Curl_easy *data, bool *done)
|
|||
switch(mqtt->state) {
|
||||
case MQTT_FIRST:
|
||||
/* Read the initial byte only */
|
||||
result = Curl_read(data, sockfd, (char *)&mq->firstbyte, 1, &nread);
|
||||
result = Curl_xfer_recv(data, (char *)&mq->firstbyte, 1, &nread);
|
||||
if(result)
|
||||
break;
|
||||
else if(!nread) {
|
||||
|
@ -778,7 +775,7 @@ static CURLcode mqtt_doing(struct Curl_easy *data, bool *done)
|
|||
FALLTHROUGH();
|
||||
case MQTT_REMAINING_LENGTH:
|
||||
do {
|
||||
result = Curl_read(data, sockfd, (char *)&byte, 1, &nread);
|
||||
result = Curl_xfer_recv(data, (char *)&byte, 1, &nread);
|
||||
if(!nread)
|
||||
break;
|
||||
Curl_debug(data, CURLINFO_HEADER_IN, (char *)&byte, 1);
|
||||
|
|
264
lib/multi.c
264
lib/multi.c
|
@ -94,6 +94,7 @@ static CURLMcode add_next_timeout(struct curltime now,
|
|||
static CURLMcode multi_timeout(struct Curl_multi *multi,
|
||||
long *timeout_ms);
|
||||
static void process_pending_handles(struct Curl_multi *multi);
|
||||
static void multi_xfer_bufs_free(struct Curl_multi *multi);
|
||||
|
||||
#ifdef DEBUGBUILD
|
||||
static const char * const multi_statename[]={
|
||||
|
@ -189,6 +190,10 @@ static void mstate(struct Curl_easy *data, CURLMstate state
|
|||
/* changing to COMPLETED means there's one less easy handle 'alive' */
|
||||
DEBUGASSERT(data->multi->num_alive > 0);
|
||||
data->multi->num_alive--;
|
||||
if(!data->multi->num_alive) {
|
||||
/* free the transfer buffer when we have no more active transfers */
|
||||
multi_xfer_bufs_free(data->multi);
|
||||
}
|
||||
}
|
||||
|
||||
/* if this state has an init-function, run it */
|
||||
|
@ -525,6 +530,13 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi,
|
|||
multi->dead = FALSE;
|
||||
}
|
||||
|
||||
if(data->multi_easy) {
|
||||
/* if this easy handle was previously used for curl_easy_perform(), there
|
||||
is a private multi handle here that we can kill */
|
||||
curl_multi_cleanup(data->multi_easy);
|
||||
data->multi_easy = NULL;
|
||||
}
|
||||
|
||||
/* Initialize timeout list for this handle */
|
||||
Curl_llist_init(&data->state.timeoutlist, NULL);
|
||||
|
||||
|
@ -640,7 +652,7 @@ static CURLcode multi_done(struct Curl_easy *data,
|
|||
after an error was detected */
|
||||
bool premature)
|
||||
{
|
||||
CURLcode result;
|
||||
CURLcode result, r2;
|
||||
struct connectdata *conn = data->conn;
|
||||
|
||||
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
|
||||
|
@ -691,14 +703,18 @@ static CURLcode multi_done(struct Curl_easy *data,
|
|||
result = CURLE_ABORTED_BY_CALLBACK;
|
||||
}
|
||||
|
||||
/* Make sure that transfer client writes are really done now. */
|
||||
r2 = Curl_xfer_write_done(data, premature);
|
||||
if(r2 && !result)
|
||||
result = r2;
|
||||
|
||||
/* Inform connection filters that this transfer is done */
|
||||
Curl_conn_ev_data_done(data, premature);
|
||||
|
||||
process_pending_handles(data->multi); /* connection / multiplex */
|
||||
|
||||
Curl_safefree(data->state.ulbuf);
|
||||
|
||||
Curl_client_cleanup(data);
|
||||
if(!result)
|
||||
result = Curl_req_done(&data->req, data, premature);
|
||||
|
||||
CONNCACHE_LOCK(data);
|
||||
Curl_detach_connection(data);
|
||||
|
@ -784,7 +800,6 @@ static CURLcode multi_done(struct Curl_easy *data,
|
|||
data->state.lastconnect_id = -1;
|
||||
}
|
||||
|
||||
Curl_safefree(data->state.buffer);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -998,7 +1013,7 @@ static int connecting_getsock(struct Curl_easy *data, curl_socket_t *socks)
|
|||
{
|
||||
struct connectdata *conn = data->conn;
|
||||
(void)socks;
|
||||
/* Not using `conn->sockfd` as `Curl_setup_transfer()` initializes
|
||||
/* Not using `conn->sockfd` as `Curl_xfer_setup()` initializes
|
||||
* that *after* the connect. */
|
||||
if(conn && conn->sock[FIRSTSOCKET] != CURL_SOCKET_BAD) {
|
||||
/* Default is to wait to something from the server */
|
||||
|
@ -1801,89 +1816,6 @@ static CURLcode protocol_connect(struct Curl_easy *data,
|
|||
return result; /* pass back status */
|
||||
}
|
||||
|
||||
/*
|
||||
* readrewind() rewinds the read stream. This is typically used for HTTP
|
||||
* POST/PUT with multi-pass authentication when a sending was denied and a
|
||||
* resend is necessary.
|
||||
*/
|
||||
static CURLcode readrewind(struct Curl_easy *data)
|
||||
{
|
||||
curl_mimepart *mimepart = &data->set.mimepost;
|
||||
DEBUGASSERT(data->conn);
|
||||
|
||||
data->state.rewindbeforesend = FALSE; /* we rewind now */
|
||||
|
||||
/* explicitly switch off sending data on this connection now since we are
|
||||
about to restart a new transfer and thus we want to avoid inadvertently
|
||||
sending more data on the existing connection until the next transfer
|
||||
starts */
|
||||
data->req.keepon &= ~KEEP_SEND;
|
||||
|
||||
/* We have sent away data. If not using CURLOPT_POSTFIELDS or
|
||||
CURLOPT_HTTPPOST, call app to rewind
|
||||
*/
|
||||
#ifndef CURL_DISABLE_HTTP
|
||||
if(data->conn->handler->protocol & PROTO_FAMILY_HTTP) {
|
||||
if(data->state.mimepost)
|
||||
mimepart = data->state.mimepost;
|
||||
}
|
||||
#endif
|
||||
if(data->set.postfields ||
|
||||
(data->state.httpreq == HTTPREQ_GET) ||
|
||||
(data->state.httpreq == HTTPREQ_HEAD))
|
||||
; /* no need to rewind */
|
||||
else if(data->state.httpreq == HTTPREQ_POST_MIME ||
|
||||
data->state.httpreq == HTTPREQ_POST_FORM) {
|
||||
CURLcode result = Curl_mime_rewind(mimepart);
|
||||
if(result) {
|
||||
failf(data, "Cannot rewind mime/post data");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(data->set.seek_func) {
|
||||
int err;
|
||||
|
||||
Curl_set_in_callback(data, true);
|
||||
err = (data->set.seek_func)(data->set.seek_client, 0, SEEK_SET);
|
||||
Curl_set_in_callback(data, false);
|
||||
if(err) {
|
||||
failf(data, "seek callback returned error %d", (int)err);
|
||||
return CURLE_SEND_FAIL_REWIND;
|
||||
}
|
||||
}
|
||||
else if(data->set.ioctl_func) {
|
||||
curlioerr err;
|
||||
|
||||
Curl_set_in_callback(data, true);
|
||||
err = (data->set.ioctl_func)(data, CURLIOCMD_RESTARTREAD,
|
||||
data->set.ioctl_client);
|
||||
Curl_set_in_callback(data, false);
|
||||
infof(data, "the ioctl callback returned %d", (int)err);
|
||||
|
||||
if(err) {
|
||||
failf(data, "ioctl callback returned error %d", (int)err);
|
||||
return CURLE_SEND_FAIL_REWIND;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* If no CURLOPT_READFUNCTION is used, we know that we operate on a
|
||||
given FILE * stream and we can actually attempt to rewind that
|
||||
ourselves with fseek() */
|
||||
if(data->state.fread_func == (curl_read_callback)fread) {
|
||||
if(-1 != fseek(data->state.in, 0, SEEK_SET))
|
||||
/* successful rewind */
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/* no callback set or failure above, makes us fail at once */
|
||||
failf(data, "necessary data rewind wasn't possible");
|
||||
return CURLE_SEND_FAIL_REWIND;
|
||||
}
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Curl_preconnect() is called immediately before a connect starts. When a
|
||||
* redirect is followed, this is then called multiple times during a single
|
||||
|
@ -1891,12 +1823,9 @@ static CURLcode readrewind(struct Curl_easy *data)
|
|||
*/
|
||||
CURLcode Curl_preconnect(struct Curl_easy *data)
|
||||
{
|
||||
if(!data->state.buffer) {
|
||||
data->state.buffer = malloc(data->set.buffer_size + 1);
|
||||
if(!data->state.buffer)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
/* this used to do data->state.buffer allocation,
|
||||
maybe remove completely now? */
|
||||
(void)data;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
|
@ -1914,7 +1843,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
|
|||
bool async;
|
||||
bool protocol_connected = FALSE;
|
||||
bool dophase_done = FALSE;
|
||||
bool done = FALSE;
|
||||
CURLMcode rc;
|
||||
CURLcode result = CURLE_OK;
|
||||
timediff_t recv_timeout_ms;
|
||||
|
@ -2058,7 +1986,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
|
|||
hostname = conn->host.name;
|
||||
|
||||
/* check if we have the name resolved by now */
|
||||
dns = Curl_fetch_addr(data, hostname, (int)conn->port);
|
||||
dns = Curl_fetch_addr(data, hostname, conn->primary.remote_port);
|
||||
|
||||
if(dns) {
|
||||
#ifdef CURLRES_ASYNCH
|
||||
|
@ -2153,9 +2081,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
|
|||
break;
|
||||
|
||||
case MSTATE_PROTOCONNECT:
|
||||
if(data->state.rewindbeforesend)
|
||||
result = readrewind(data);
|
||||
|
||||
if(!result && data->conn->bits.reuse) {
|
||||
/* ftp seems to hang when protoconnect on reused connection
|
||||
* since we handle PROTOCONNECT in general inside the filers, it
|
||||
|
@ -2207,10 +2132,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
|
|||
/* call the prerequest callback function */
|
||||
Curl_set_in_callback(data, true);
|
||||
prereq_rc = data->set.fprereq(data->set.prereq_userp,
|
||||
data->info.conn_primary_ip,
|
||||
data->info.conn_local_ip,
|
||||
data->info.conn_primary_port,
|
||||
data->info.conn_local_port);
|
||||
data->info.primary.remote_ip,
|
||||
data->info.primary.local_ip,
|
||||
data->info.primary.remote_port,
|
||||
data->info.primary.local_port);
|
||||
Curl_set_in_callback(data, false);
|
||||
if(prereq_rc != CURL_PREREQFUNC_OK) {
|
||||
failf(data, "operation aborted by pre-request callback");
|
||||
|
@ -2450,7 +2375,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
|
|||
{
|
||||
char *newurl = NULL;
|
||||
bool retry = FALSE;
|
||||
DEBUGASSERT(data->state.buffer);
|
||||
/* check if over send speed */
|
||||
send_timeout_ms = 0;
|
||||
if(data->set.max_send_speed)
|
||||
|
@ -2480,9 +2404,9 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
|
|||
}
|
||||
|
||||
/* read/write data if it is ready to do so */
|
||||
result = Curl_readwrite(data, &done);
|
||||
result = Curl_readwrite(data);
|
||||
|
||||
if(done || (result == CURLE_RECV_ERROR)) {
|
||||
if(data->req.done || (result == CURLE_RECV_ERROR)) {
|
||||
/* If CURLE_RECV_ERROR happens early enough, we assume it was a race
|
||||
* condition and the server closed the reused connection exactly when
|
||||
* we wanted to use it, so figure out if that is indeed the case.
|
||||
|
@ -2497,7 +2421,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
|
|||
/* if we are to retry, set the result to OK and consider the
|
||||
request as done */
|
||||
result = CURLE_OK;
|
||||
done = TRUE;
|
||||
data->req.done = TRUE;
|
||||
}
|
||||
}
|
||||
else if((CURLE_HTTP2_STREAM == result) &&
|
||||
|
@ -2517,7 +2441,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
|
|||
as done */
|
||||
retry = TRUE;
|
||||
result = CURLE_OK;
|
||||
done = TRUE;
|
||||
data->req.done = TRUE;
|
||||
}
|
||||
else
|
||||
result = ret;
|
||||
|
@ -2539,7 +2463,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
|
|||
Curl_posttransfer(data);
|
||||
multi_done(data, result, TRUE);
|
||||
}
|
||||
else if(done) {
|
||||
else if(data->req.done) {
|
||||
|
||||
/* call this even if the readwrite function returned error */
|
||||
Curl_posttransfer(data);
|
||||
|
@ -2883,6 +2807,7 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi)
|
|||
Curl_free_multi_ssl_backend_data(multi->ssl_backend_data);
|
||||
#endif
|
||||
|
||||
multi_xfer_bufs_free(multi);
|
||||
free(multi);
|
||||
|
||||
return CURLM_OK;
|
||||
|
@ -3242,7 +3167,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
|
|||
|
||||
if(data->conn && !(data->conn->handler->flags & PROTOPT_DIRLOCK))
|
||||
/* set socket event bitmask if they're not locked */
|
||||
data->state.select_bits = (unsigned char)ev_bitmask;
|
||||
data->state.select_bits |= (unsigned char)ev_bitmask;
|
||||
|
||||
Curl_expire(data, 0, EXPIRE_RUN_NOW);
|
||||
}
|
||||
|
@ -3819,3 +3744,120 @@ struct Curl_easy **curl_multi_get_handles(struct Curl_multi *multi)
|
|||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
CURLcode Curl_multi_xfer_buf_borrow(struct Curl_easy *data,
|
||||
char **pbuf, size_t *pbuflen)
|
||||
{
|
||||
DEBUGASSERT(data);
|
||||
DEBUGASSERT(data->multi);
|
||||
*pbuf = NULL;
|
||||
*pbuflen = 0;
|
||||
if(!data->multi) {
|
||||
failf(data, "transfer has no multi handle");
|
||||
return CURLE_FAILED_INIT;
|
||||
}
|
||||
if(!data->set.buffer_size) {
|
||||
failf(data, "transfer buffer size is 0");
|
||||
return CURLE_FAILED_INIT;
|
||||
}
|
||||
if(data->multi->xfer_buf_borrowed) {
|
||||
failf(data, "attempt to borrow xfer_buf when already borrowed");
|
||||
return CURLE_AGAIN;
|
||||
}
|
||||
|
||||
if(data->multi->xfer_buf &&
|
||||
data->set.buffer_size > data->multi->xfer_buf_len) {
|
||||
/* not large enough, get a new one */
|
||||
free(data->multi->xfer_buf);
|
||||
data->multi->xfer_buf = NULL;
|
||||
data->multi->xfer_buf_len = 0;
|
||||
}
|
||||
|
||||
if(!data->multi->xfer_buf) {
|
||||
data->multi->xfer_buf = malloc((size_t)data->set.buffer_size);
|
||||
if(!data->multi->xfer_buf) {
|
||||
failf(data, "could not allocate xfer_buf of %zu bytes",
|
||||
(size_t)data->set.buffer_size);
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
data->multi->xfer_buf_len = data->set.buffer_size;
|
||||
}
|
||||
|
||||
data->multi->xfer_buf_borrowed = TRUE;
|
||||
*pbuf = data->multi->xfer_buf;
|
||||
*pbuflen = data->multi->xfer_buf_len;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
void Curl_multi_xfer_buf_release(struct Curl_easy *data, char *buf)
|
||||
{
|
||||
(void)buf;
|
||||
DEBUGASSERT(data);
|
||||
DEBUGASSERT(data->multi);
|
||||
DEBUGASSERT(!buf || data->multi->xfer_buf == buf);
|
||||
data->multi->xfer_buf_borrowed = FALSE;
|
||||
}
|
||||
|
||||
CURLcode Curl_multi_xfer_ulbuf_borrow(struct Curl_easy *data,
|
||||
char **pbuf, size_t *pbuflen)
|
||||
{
|
||||
DEBUGASSERT(data);
|
||||
DEBUGASSERT(data->multi);
|
||||
*pbuf = NULL;
|
||||
*pbuflen = 0;
|
||||
if(!data->multi) {
|
||||
failf(data, "transfer has no multi handle");
|
||||
return CURLE_FAILED_INIT;
|
||||
}
|
||||
if(!data->set.upload_buffer_size) {
|
||||
failf(data, "transfer upload buffer size is 0");
|
||||
return CURLE_FAILED_INIT;
|
||||
}
|
||||
if(data->multi->xfer_ulbuf_borrowed) {
|
||||
failf(data, "attempt to borrow xfer_ulbuf when already borrowed");
|
||||
return CURLE_AGAIN;
|
||||
}
|
||||
|
||||
if(data->multi->xfer_ulbuf &&
|
||||
data->set.upload_buffer_size > data->multi->xfer_ulbuf_len) {
|
||||
/* not large enough, get a new one */
|
||||
free(data->multi->xfer_ulbuf);
|
||||
data->multi->xfer_ulbuf = NULL;
|
||||
data->multi->xfer_ulbuf_len = 0;
|
||||
}
|
||||
|
||||
if(!data->multi->xfer_ulbuf) {
|
||||
data->multi->xfer_ulbuf = malloc((size_t)data->set.upload_buffer_size);
|
||||
if(!data->multi->xfer_ulbuf) {
|
||||
failf(data, "could not allocate xfer_ulbuf of %zu bytes",
|
||||
(size_t)data->set.upload_buffer_size);
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
data->multi->xfer_ulbuf_len = data->set.upload_buffer_size;
|
||||
}
|
||||
|
||||
data->multi->xfer_ulbuf_borrowed = TRUE;
|
||||
*pbuf = data->multi->xfer_ulbuf;
|
||||
*pbuflen = data->multi->xfer_ulbuf_len;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
void Curl_multi_xfer_ulbuf_release(struct Curl_easy *data, char *buf)
|
||||
{
|
||||
(void)buf;
|
||||
DEBUGASSERT(data);
|
||||
DEBUGASSERT(data->multi);
|
||||
DEBUGASSERT(!buf || data->multi->xfer_ulbuf == buf);
|
||||
data->multi->xfer_ulbuf_borrowed = FALSE;
|
||||
}
|
||||
|
||||
static void multi_xfer_bufs_free(struct Curl_multi *multi)
|
||||
{
|
||||
DEBUGASSERT(multi);
|
||||
Curl_safefree(multi->xfer_buf);
|
||||
multi->xfer_buf_len = 0;
|
||||
multi->xfer_buf_borrowed = FALSE;
|
||||
Curl_safefree(multi->xfer_ulbuf);
|
||||
multi->xfer_ulbuf_len = 0;
|
||||
multi->xfer_ulbuf_borrowed = FALSE;
|
||||
}
|
||||
|
|
|
@ -124,6 +124,13 @@ struct Curl_multi {
|
|||
times of all currently set timers */
|
||||
struct Curl_tree *timetree;
|
||||
|
||||
/* buffer used for transfer data, lazy initialized */
|
||||
char *xfer_buf; /* the actual buffer */
|
||||
size_t xfer_buf_len; /* the allocated length */
|
||||
/* buffer used for upload data, lazy initialized */
|
||||
char *xfer_ulbuf; /* the actual buffer */
|
||||
size_t xfer_ulbuf_len; /* the allocated length */
|
||||
|
||||
#if defined(USE_SSL)
|
||||
struct multi_ssl_backend_data *ssl_backend_data;
|
||||
#endif
|
||||
|
@ -171,6 +178,8 @@ struct Curl_multi {
|
|||
#endif
|
||||
BIT(dead); /* a callback returned error, everything needs to crash and
|
||||
burn */
|
||||
BIT(xfer_buf_borrowed); /* xfer_buf is currently being borrowed */
|
||||
BIT(xfer_ulbuf_borrowed); /* xfer_buf is currently being borrowed */
|
||||
#ifdef DEBUGBUILD
|
||||
BIT(warned); /* true after user warned of DEBUGBUILD */
|
||||
#endif
|
||||
|
|
|
@ -94,4 +94,53 @@ CURLMcode Curl_multi_add_perform(struct Curl_multi *multi,
|
|||
/* Return the value of the CURLMOPT_MAX_CONCURRENT_STREAMS option */
|
||||
unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi);
|
||||
|
||||
/**
|
||||
* Borrow the transfer buffer from the multi, suitable
|
||||
* for the given transfer `data`. The buffer may only be used in one
|
||||
* multi processing of the easy handle. It MUST be returned to the
|
||||
* multi before it can be borrowed again.
|
||||
* Pointers into the buffer remain only valid as long as it is borrowed.
|
||||
*
|
||||
* @param data the easy handle
|
||||
* @param pbuf on return, the buffer to use or NULL on error
|
||||
* @param pbuflen on return, the size of *pbuf or 0 on error
|
||||
* @return CURLE_OK when buffer is available and is returned.
|
||||
* CURLE_OUT_OF_MEMORy on failure to allocate the buffer,
|
||||
* CURLE_FAILED_INIT if the easy handle is without multi.
|
||||
* CURLE_AGAIN if the buffer is borrowed already.
|
||||
*/
|
||||
CURLcode Curl_multi_xfer_buf_borrow(struct Curl_easy *data,
|
||||
char **pbuf, size_t *pbuflen);
|
||||
/**
|
||||
* Release the borrowed buffer. All references into the buffer become
|
||||
* invalid after this.
|
||||
* @param buf the buffer pointer borrowed for coding error checks.
|
||||
*/
|
||||
void Curl_multi_xfer_buf_release(struct Curl_easy *data, char *buf);
|
||||
|
||||
/**
|
||||
* Borrow the upload buffer from the multi, suitable
|
||||
* for the given transfer `data`. The buffer may only be used in one
|
||||
* multi processing of the easy handle. It MUST be returned to the
|
||||
* multi before it can be borrowed again.
|
||||
* Pointers into the buffer remain only valid as long as it is borrowed.
|
||||
*
|
||||
* @param data the easy handle
|
||||
* @param pbuf on return, the buffer to use or NULL on error
|
||||
* @param pbuflen on return, the size of *pbuf or 0 on error
|
||||
* @return CURLE_OK when buffer is available and is returned.
|
||||
* CURLE_OUT_OF_MEMORy on failure to allocate the buffer,
|
||||
* CURLE_FAILED_INIT if the easy handle is without multi.
|
||||
* CURLE_AGAIN if the buffer is borrowed already.
|
||||
*/
|
||||
CURLcode Curl_multi_xfer_ulbuf_borrow(struct Curl_easy *data,
|
||||
char **pbuf, size_t *pbuflen);
|
||||
|
||||
/**
|
||||
* Release the borrowed upload buffer. All references into the buffer become
|
||||
* invalid after this.
|
||||
* @param buf the upload buffer pointer borrowed for coding error checks.
|
||||
*/
|
||||
void Curl_multi_xfer_ulbuf_release(struct Curl_easy *data, char *buf);
|
||||
|
||||
#endif /* HEADER_CURL_MULTIIF_H */
|
||||
|
|
10
lib/netrc.c
10
lib/netrc.c
|
@ -53,6 +53,8 @@ enum host_lookup_state {
|
|||
#define NETRC_FAILED -1
|
||||
#define NETRC_SUCCESS 0
|
||||
|
||||
#define MAX_NETRC_LINE 4096
|
||||
|
||||
/*
|
||||
* Returns zero on success.
|
||||
*/
|
||||
|
@ -80,13 +82,14 @@ static int parsenetrc(const char *host,
|
|||
file = fopen(netrcfile, FOPEN_READTEXT);
|
||||
if(file) {
|
||||
bool done = FALSE;
|
||||
char netrcbuffer[4096];
|
||||
int netrcbuffsize = (int)sizeof(netrcbuffer);
|
||||
struct dynbuf buf;
|
||||
Curl_dyn_init(&buf, MAX_NETRC_LINE);
|
||||
|
||||
while(!done && Curl_get_line(netrcbuffer, netrcbuffsize, file)) {
|
||||
while(!done && Curl_get_line(&buf, file)) {
|
||||
char *tok;
|
||||
char *tok_end;
|
||||
bool quoted;
|
||||
char *netrcbuffer = Curl_dyn_ptr(&buf);
|
||||
if(state == MACDEF) {
|
||||
if((netrcbuffer[0] == '\n') || (netrcbuffer[0] == '\r'))
|
||||
state = NOTHING;
|
||||
|
@ -245,6 +248,7 @@ static int parsenetrc(const char *host,
|
|||
} /* while Curl_get_line() */
|
||||
|
||||
out:
|
||||
Curl_dyn_free(&buf);
|
||||
if(!retcode) {
|
||||
/* success */
|
||||
if(login_alloc) {
|
||||
|
|
|
@ -916,7 +916,7 @@ static CURLcode oldap_do(struct Curl_easy *data, bool *done)
|
|||
else {
|
||||
lr->msgid = msgid;
|
||||
data->req.p.ldap = lr;
|
||||
Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
|
||||
Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1);
|
||||
*done = TRUE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -164,7 +164,7 @@ CURLcode Curl_pp_vsendf(struct Curl_easy *data,
|
|||
const char *fmt,
|
||||
va_list args)
|
||||
{
|
||||
ssize_t bytes_written = 0;
|
||||
size_t bytes_written = 0;
|
||||
size_t write_len;
|
||||
char *s;
|
||||
CURLcode result;
|
||||
|
@ -199,8 +199,11 @@ CURLcode Curl_pp_vsendf(struct Curl_easy *data,
|
|||
#ifdef HAVE_GSSAPI
|
||||
conn->data_prot = PROT_CMD;
|
||||
#endif
|
||||
result = Curl_nwrite(data, FIRSTSOCKET, s, write_len, &bytes_written);
|
||||
if(result)
|
||||
result = Curl_conn_send(data, FIRSTSOCKET, s, write_len, &bytes_written);
|
||||
if(result == CURLE_AGAIN) {
|
||||
bytes_written = 0;
|
||||
}
|
||||
else if(result)
|
||||
return result;
|
||||
#ifdef HAVE_GSSAPI
|
||||
data_sec = conn->data_prot;
|
||||
|
@ -208,9 +211,9 @@ CURLcode Curl_pp_vsendf(struct Curl_easy *data,
|
|||
conn->data_prot = (unsigned char)data_sec;
|
||||
#endif
|
||||
|
||||
Curl_debug(data, CURLINFO_HEADER_OUT, s, (size_t)bytes_written);
|
||||
Curl_debug(data, CURLINFO_HEADER_OUT, s, bytes_written);
|
||||
|
||||
if(bytes_written != (ssize_t)write_len) {
|
||||
if(bytes_written != write_len) {
|
||||
/* the whole chunk was not sent, keep it around and adjust sizes */
|
||||
pp->sendthis = s;
|
||||
pp->sendsize = write_len;
|
||||
|
@ -251,7 +254,7 @@ CURLcode Curl_pp_sendf(struct Curl_easy *data, struct pingpong *pp,
|
|||
}
|
||||
|
||||
static CURLcode pingpong_read(struct Curl_easy *data,
|
||||
curl_socket_t sockfd,
|
||||
int sockindex,
|
||||
char *buffer,
|
||||
size_t buflen,
|
||||
ssize_t *nread)
|
||||
|
@ -261,7 +264,7 @@ static CURLcode pingpong_read(struct Curl_easy *data,
|
|||
enum protection_level prot = data->conn->data_prot;
|
||||
data->conn->data_prot = PROT_CLEAR;
|
||||
#endif
|
||||
result = Curl_read(data, sockfd, buffer, buflen, nread);
|
||||
result = Curl_conn_recv(data, sockindex, buffer, buflen, nread);
|
||||
#ifdef HAVE_GSSAPI
|
||||
DEBUGASSERT(prot > PROT_NONE && prot < PROT_LAST);
|
||||
data->conn->data_prot = (unsigned char)prot;
|
||||
|
@ -275,7 +278,7 @@ static CURLcode pingpong_read(struct Curl_easy *data,
|
|||
* Reads a piece of a server response.
|
||||
*/
|
||||
CURLcode Curl_pp_readresp(struct Curl_easy *data,
|
||||
curl_socket_t sockfd,
|
||||
int sockindex,
|
||||
struct pingpong *pp,
|
||||
int *code, /* return the server code if done */
|
||||
size_t *size) /* size of the response */
|
||||
|
@ -300,7 +303,7 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data,
|
|||
ssize_t gotbytes = 0;
|
||||
char buffer[900];
|
||||
|
||||
result = pingpong_read(data, sockfd, buffer, sizeof(buffer), &gotbytes);
|
||||
result = pingpong_read(data, sockindex, buffer, sizeof(buffer), &gotbytes);
|
||||
if(result == CURLE_AGAIN)
|
||||
return CURLE_OK;
|
||||
|
||||
|
@ -395,14 +398,20 @@ CURLcode Curl_pp_flushsend(struct Curl_easy *data,
|
|||
struct pingpong *pp)
|
||||
{
|
||||
/* we have a piece of a command still left to send */
|
||||
ssize_t written;
|
||||
CURLcode result = Curl_nwrite(data, FIRSTSOCKET,
|
||||
pp->sendthis + pp->sendsize - pp->sendleft,
|
||||
pp->sendleft, &written);
|
||||
size_t written;
|
||||
CURLcode result;
|
||||
|
||||
result = Curl_conn_send(data, FIRSTSOCKET,
|
||||
pp->sendthis + pp->sendsize - pp->sendleft,
|
||||
pp->sendleft, &written);
|
||||
if(result == CURLE_AGAIN) {
|
||||
result = CURLE_OK;
|
||||
written = 0;
|
||||
}
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
if(written != (ssize_t)pp->sendleft) {
|
||||
if(written != pp->sendleft) {
|
||||
/* only a fraction was sent */
|
||||
pp->sendleft -= written;
|
||||
}
|
||||
|
@ -423,7 +432,7 @@ CURLcode Curl_pp_disconnect(struct pingpong *pp)
|
|||
|
||||
bool Curl_pp_moredata(struct pingpong *pp)
|
||||
{
|
||||
return (!pp->sendleft && Curl_dyn_len(&pp->recvbuf));
|
||||
return (!pp->sendleft && Curl_dyn_len(&pp->recvbuf) > pp->nfinal);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -132,7 +132,7 @@ CURLcode Curl_pp_vsendf(struct Curl_easy *data,
|
|||
* Reads a piece of a server response.
|
||||
*/
|
||||
CURLcode Curl_pp_readresp(struct Curl_easy *data,
|
||||
curl_socket_t sockfd,
|
||||
int sockindex,
|
||||
struct pingpong *pp,
|
||||
int *code, /* return the server code if done */
|
||||
size_t *size); /* size of the response */
|
||||
|
|
|
@ -934,7 +934,7 @@ static CURLcode pop3_state_command_resp(struct Curl_easy *data,
|
|||
|
||||
if(pop3->transfer == PPTRANSFER_BODY) {
|
||||
/* POP3 download */
|
||||
Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
|
||||
Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1);
|
||||
|
||||
if(pp->overflow) {
|
||||
/* The recv buffer contains data that is actually body content so send
|
||||
|
@ -970,7 +970,6 @@ static CURLcode pop3_statemachine(struct Curl_easy *data,
|
|||
struct connectdata *conn)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
curl_socket_t sock = conn->sock[FIRSTSOCKET];
|
||||
int pop3code;
|
||||
struct pop3_conn *pop3c = &conn->proto.pop3c;
|
||||
struct pingpong *pp = &pop3c->pp;
|
||||
|
@ -987,7 +986,7 @@ static CURLcode pop3_statemachine(struct Curl_easy *data,
|
|||
|
||||
do {
|
||||
/* Read the response from the server */
|
||||
result = Curl_pp_readresp(data, sock, pp, &pop3code, &nread);
|
||||
result = Curl_pp_readresp(data, FIRSTSOCKET, pp, &pop3code, &nread);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
|
|
|
@ -0,0 +1,409 @@
|
|||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at https://curl.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#include "curl_setup.h"
|
||||
|
||||
#include "urldata.h"
|
||||
#include "cfilters.h"
|
||||
#include "dynbuf.h"
|
||||
#include "doh.h"
|
||||
#include "multiif.h"
|
||||
#include "progress.h"
|
||||
#include "request.h"
|
||||
#include "sendf.h"
|
||||
#include "transfer.h"
|
||||
#include "url.h"
|
||||
|
||||
/* The last 3 #include files should be in this order */
|
||||
#include "curl_printf.h"
|
||||
#include "curl_memory.h"
|
||||
#include "memdebug.h"
|
||||
|
||||
CURLcode Curl_req_init(struct SingleRequest *req)
|
||||
{
|
||||
memset(req, 0, sizeof(*req));
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
CURLcode Curl_req_soft_reset(struct SingleRequest *req,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
CURLcode result;
|
||||
|
||||
req->done = FALSE;
|
||||
req->upload_done = FALSE;
|
||||
req->download_done = FALSE;
|
||||
req->ignorebody = FALSE;
|
||||
req->bytecount = 0;
|
||||
req->writebytecount = 0;
|
||||
req->header = TRUE; /* assume header */
|
||||
req->headerline = 0;
|
||||
req->headerbytecount = 0;
|
||||
req->allheadercount = 0;
|
||||
req->deductheadercount = 0;
|
||||
|
||||
result = Curl_client_start(data);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
if(!req->sendbuf_init) {
|
||||
Curl_bufq_init2(&req->sendbuf, data->set.upload_buffer_size, 1,
|
||||
BUFQ_OPT_SOFT_LIMIT);
|
||||
req->sendbuf_init = TRUE;
|
||||
}
|
||||
else {
|
||||
Curl_bufq_reset(&req->sendbuf);
|
||||
if(data->set.upload_buffer_size != req->sendbuf.chunk_size) {
|
||||
Curl_bufq_free(&req->sendbuf);
|
||||
Curl_bufq_init2(&req->sendbuf, data->set.upload_buffer_size, 1,
|
||||
BUFQ_OPT_SOFT_LIMIT);
|
||||
}
|
||||
}
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
CURLcode Curl_req_start(struct SingleRequest *req,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
req->start = Curl_now();
|
||||
return Curl_req_soft_reset(req, data);
|
||||
}
|
||||
|
||||
static CURLcode req_flush(struct Curl_easy *data);
|
||||
|
||||
CURLcode Curl_req_done(struct SingleRequest *req,
|
||||
struct Curl_easy *data, bool aborted)
|
||||
{
|
||||
(void)req;
|
||||
if(!aborted)
|
||||
(void)req_flush(data);
|
||||
Curl_client_reset(data);
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data)
|
||||
{
|
||||
struct curltime t0 = {0, 0};
|
||||
|
||||
/* This is a bit ugly. `req->p` is a union and we assume we can
|
||||
* free this safely without leaks. */
|
||||
Curl_safefree(req->p.http);
|
||||
Curl_safefree(req->newurl);
|
||||
Curl_client_reset(data);
|
||||
if(req->sendbuf_init)
|
||||
Curl_bufq_reset(&req->sendbuf);
|
||||
|
||||
#ifndef CURL_DISABLE_DOH
|
||||
if(req->doh) {
|
||||
Curl_close(&req->doh->probe[0].easy);
|
||||
Curl_close(&req->doh->probe[1].easy);
|
||||
}
|
||||
#endif
|
||||
/* Can no longer memset() this struct as we need to keep some state */
|
||||
req->size = -1;
|
||||
req->maxdownload = -1;
|
||||
req->bytecount = 0;
|
||||
req->writebytecount = 0;
|
||||
req->start = t0;
|
||||
req->headerbytecount = 0;
|
||||
req->allheadercount = 0;
|
||||
req->deductheadercount = 0;
|
||||
req->headerline = 0;
|
||||
req->offset = 0;
|
||||
req->httpcode = 0;
|
||||
req->keepon = 0;
|
||||
req->upgr101 = UPGR101_INIT;
|
||||
req->timeofdoc = 0;
|
||||
req->bodywrites = 0;
|
||||
req->location = NULL;
|
||||
req->newurl = NULL;
|
||||
#ifndef CURL_DISABLE_COOKIES
|
||||
req->setcookies = 0;
|
||||
#endif
|
||||
req->header = FALSE;
|
||||
req->content_range = FALSE;
|
||||
req->download_done = FALSE;
|
||||
req->eos_written = FALSE;
|
||||
req->eos_read = FALSE;
|
||||
req->upload_done = FALSE;
|
||||
req->upload_aborted = FALSE;
|
||||
req->ignorebody = FALSE;
|
||||
req->http_bodyless = FALSE;
|
||||
req->chunk = FALSE;
|
||||
req->ignore_cl = FALSE;
|
||||
req->upload_chunky = FALSE;
|
||||
req->getheader = FALSE;
|
||||
req->no_body = data->set.opt_no_body;
|
||||
req->authneg = FALSE;
|
||||
}
|
||||
|
||||
void Curl_req_free(struct SingleRequest *req, struct Curl_easy *data)
|
||||
{
|
||||
/* This is a bit ugly. `req->p` is a union and we assume we can
|
||||
* free this safely without leaks. */
|
||||
Curl_safefree(req->p.http);
|
||||
Curl_safefree(req->newurl);
|
||||
if(req->sendbuf_init)
|
||||
Curl_bufq_free(&req->sendbuf);
|
||||
Curl_client_cleanup(data);
|
||||
|
||||
#ifndef CURL_DISABLE_DOH
|
||||
if(req->doh) {
|
||||
Curl_close(&req->doh->probe[0].easy);
|
||||
Curl_close(&req->doh->probe[1].easy);
|
||||
Curl_dyn_free(&req->doh->probe[0].serverdoh);
|
||||
Curl_dyn_free(&req->doh->probe[1].serverdoh);
|
||||
curl_slist_free_all(req->doh->headers);
|
||||
Curl_safefree(req->doh);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static CURLcode xfer_send(struct Curl_easy *data,
|
||||
const char *buf, size_t blen,
|
||||
size_t hds_len, size_t *pnwritten)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
*pnwritten = 0;
|
||||
#ifdef CURLDEBUG
|
||||
{
|
||||
/* Allow debug builds to override this logic to force short initial
|
||||
sends
|
||||
*/
|
||||
char *p = getenv("CURL_SMALLREQSEND");
|
||||
if(p) {
|
||||
size_t altsize = (size_t)strtoul(p, NULL, 10);
|
||||
if(altsize && altsize < blen)
|
||||
blen = altsize;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/* Make sure this doesn't send more body bytes than what the max send
|
||||
speed says. The headers do not count to the max speed. */
|
||||
if(data->set.max_send_speed) {
|
||||
size_t body_bytes = blen - hds_len;
|
||||
if((curl_off_t)body_bytes > data->set.max_send_speed)
|
||||
blen = hds_len + (size_t)data->set.max_send_speed;
|
||||
}
|
||||
|
||||
result = Curl_xfer_send(data, buf, blen, pnwritten);
|
||||
if(!result && *pnwritten) {
|
||||
if(hds_len)
|
||||
Curl_debug(data, CURLINFO_HEADER_OUT, (char *)buf,
|
||||
CURLMIN(hds_len, *pnwritten));
|
||||
if(*pnwritten > hds_len) {
|
||||
size_t body_len = *pnwritten - hds_len;
|
||||
Curl_debug(data, CURLINFO_DATA_OUT, (char *)buf + hds_len, body_len);
|
||||
data->req.writebytecount += body_len;
|
||||
Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode req_send_buffer_flush(struct Curl_easy *data)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
const unsigned char *buf;
|
||||
size_t blen;
|
||||
|
||||
while(Curl_bufq_peek(&data->req.sendbuf, &buf, &blen)) {
|
||||
size_t nwritten, hds_len = CURLMIN(data->req.sendbuf_hds_len, blen);
|
||||
result = xfer_send(data, (const char *)buf, blen, hds_len, &nwritten);
|
||||
if(result)
|
||||
break;
|
||||
|
||||
Curl_bufq_skip(&data->req.sendbuf, nwritten);
|
||||
if(hds_len) {
|
||||
data->req.sendbuf_hds_len -= CURLMIN(hds_len, nwritten);
|
||||
}
|
||||
/* leave if we could not send all. Maybe network blocking or
|
||||
* speed limits on transfer */
|
||||
if(nwritten < blen)
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode req_set_upload_done(struct Curl_easy *data)
|
||||
{
|
||||
DEBUGASSERT(!data->req.upload_done);
|
||||
data->req.upload_done = TRUE;
|
||||
data->req.keepon &= ~(KEEP_SEND|KEEP_SEND_TIMED); /* we're done sending */
|
||||
|
||||
Curl_creader_done(data, data->req.upload_aborted);
|
||||
|
||||
if(data->req.upload_aborted) {
|
||||
if(data->req.writebytecount)
|
||||
infof(data, "abort upload after having sent %" CURL_FORMAT_CURL_OFF_T
|
||||
" bytes", data->req.writebytecount);
|
||||
else
|
||||
infof(data, "abort upload");
|
||||
}
|
||||
else if(data->req.writebytecount)
|
||||
infof(data, "upload completely sent off: %" CURL_FORMAT_CURL_OFF_T
|
||||
" bytes", data->req.writebytecount);
|
||||
else
|
||||
infof(data, Curl_creader_total_length(data)?
|
||||
"We are completely uploaded and fine" :
|
||||
"Request completely sent off");
|
||||
|
||||
return Curl_xfer_send_close(data);
|
||||
}
|
||||
|
||||
static CURLcode req_flush(struct Curl_easy *data)
|
||||
{
|
||||
CURLcode result;
|
||||
|
||||
if(!data || !data->conn)
|
||||
return CURLE_FAILED_INIT;
|
||||
|
||||
if(!Curl_bufq_is_empty(&data->req.sendbuf)) {
|
||||
result = req_send_buffer_flush(data);
|
||||
if(result)
|
||||
return result;
|
||||
if(!Curl_bufq_is_empty(&data->req.sendbuf)) {
|
||||
return CURLE_AGAIN;
|
||||
}
|
||||
}
|
||||
|
||||
if(!data->req.upload_done && data->req.eos_read &&
|
||||
Curl_bufq_is_empty(&data->req.sendbuf)) {
|
||||
return req_set_upload_done(data);
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static ssize_t add_from_client(void *reader_ctx,
|
||||
unsigned char *buf, size_t buflen,
|
||||
CURLcode *err)
|
||||
{
|
||||
struct Curl_easy *data = reader_ctx;
|
||||
size_t nread;
|
||||
bool eos;
|
||||
|
||||
*err = Curl_client_read(data, (char *)buf, buflen, &nread, &eos);
|
||||
if(*err)
|
||||
return -1;
|
||||
if(eos)
|
||||
data->req.eos_read = TRUE;
|
||||
return (ssize_t)nread;
|
||||
}
|
||||
|
||||
#ifndef USE_HYPER
|
||||
|
||||
static CURLcode req_send_buffer_add(struct Curl_easy *data,
|
||||
const char *buf, size_t blen,
|
||||
size_t hds_len)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
ssize_t n;
|
||||
n = Curl_bufq_write(&data->req.sendbuf,
|
||||
(const unsigned char *)buf, blen, &result);
|
||||
if(n < 0)
|
||||
return result;
|
||||
/* We rely on a SOFTLIMIT on sendbuf, so it can take all data in */
|
||||
DEBUGASSERT((size_t)n == blen);
|
||||
data->req.sendbuf_hds_len += hds_len;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *req)
|
||||
{
|
||||
CURLcode result;
|
||||
const char *buf;
|
||||
size_t blen, nwritten;
|
||||
|
||||
if(!data || !data->conn)
|
||||
return CURLE_FAILED_INIT;
|
||||
|
||||
buf = Curl_dyn_ptr(req);
|
||||
blen = Curl_dyn_len(req);
|
||||
if(!Curl_creader_total_length(data)) {
|
||||
/* Request without body. Try to send directly from the buf given. */
|
||||
data->req.eos_read = TRUE;
|
||||
result = xfer_send(data, buf, blen, blen, &nwritten);
|
||||
if(result)
|
||||
return result;
|
||||
buf += nwritten;
|
||||
blen -= nwritten;
|
||||
}
|
||||
|
||||
if(blen) {
|
||||
/* Either we have a request body, or we could not send the complete
|
||||
* request in one go. Buffer the remainder and try to add as much
|
||||
* body bytes as room is left in the buffer. Then flush. */
|
||||
result = req_send_buffer_add(data, buf, blen, blen);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
return Curl_req_send_more(data);
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
#endif /* !USE_HYPER */
|
||||
|
||||
bool Curl_req_want_send(struct Curl_easy *data)
|
||||
{
|
||||
return data->req.sendbuf_init && !Curl_bufq_is_empty(&data->req.sendbuf);
|
||||
}
|
||||
|
||||
bool Curl_req_done_sending(struct Curl_easy *data)
|
||||
{
|
||||
if(data->req.upload_done) {
|
||||
DEBUGASSERT(Curl_bufq_is_empty(&data->req.sendbuf));
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CURLcode Curl_req_send_more(struct Curl_easy *data)
|
||||
{
|
||||
CURLcode result;
|
||||
|
||||
/* Fill our send buffer if more from client can be read. */
|
||||
if(!data->req.eos_read && !Curl_bufq_is_full(&data->req.sendbuf)) {
|
||||
ssize_t nread = Curl_bufq_sipn(&data->req.sendbuf, 0,
|
||||
add_from_client, data, &result);
|
||||
if(nread < 0 && result != CURLE_AGAIN)
|
||||
return result;
|
||||
}
|
||||
|
||||
result = req_flush(data);
|
||||
if(result == CURLE_AGAIN)
|
||||
result = CURLE_OK;
|
||||
return result;
|
||||
}
|
||||
|
||||
CURLcode Curl_req_abort_sending(struct Curl_easy *data)
|
||||
{
|
||||
if(!data->req.upload_done) {
|
||||
Curl_bufq_reset(&data->req.sendbuf);
|
||||
data->req.upload_aborted = TRUE;
|
||||
return req_set_upload_done(data);
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
|
@ -0,0 +1,227 @@
|
|||
#ifndef HEADER_CURL_REQUEST_H
|
||||
#define HEADER_CURL_REQUEST_H
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at https://curl.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
/* This file is for lib internal stuff */
|
||||
|
||||
#include "curl_setup.h"
|
||||
|
||||
#include "bufq.h"
|
||||
|
||||
/* forward declarations */
|
||||
struct UserDefined;
|
||||
|
||||
enum expect100 {
|
||||
EXP100_SEND_DATA, /* enough waiting, just send the body now */
|
||||
EXP100_AWAITING_CONTINUE, /* waiting for the 100 Continue header */
|
||||
EXP100_SENDING_REQUEST, /* still sending the request but will wait for
|
||||
the 100 header once done with the request */
|
||||
EXP100_FAILED /* used on 417 Expectation Failed */
|
||||
};
|
||||
|
||||
enum upgrade101 {
|
||||
UPGR101_INIT, /* default state */
|
||||
UPGR101_WS, /* upgrade to WebSockets requested */
|
||||
UPGR101_H2, /* upgrade to HTTP/2 requested */
|
||||
UPGR101_RECEIVED, /* 101 response received */
|
||||
UPGR101_WORKING /* talking upgraded protocol */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Request specific data in the easy handle (Curl_easy). Previously,
|
||||
* these members were on the connectdata struct but since a conn struct may
|
||||
* now be shared between different Curl_easys, we store connection-specific
|
||||
* data here. This struct only keeps stuff that's interesting for *this*
|
||||
* request, as it will be cleared between multiple ones
|
||||
*/
|
||||
struct SingleRequest {
|
||||
curl_off_t size; /* -1 if unknown at this point */
|
||||
curl_off_t maxdownload; /* in bytes, the maximum amount of data to fetch,
|
||||
-1 means unlimited */
|
||||
curl_off_t bytecount; /* total number of bytes read */
|
||||
curl_off_t writebytecount; /* number of bytes written */
|
||||
|
||||
struct curltime start; /* transfer started at this time */
|
||||
unsigned int headerbytecount; /* received server headers (not CONNECT
|
||||
headers) */
|
||||
unsigned int allheadercount; /* all received headers (server + CONNECT) */
|
||||
unsigned int deductheadercount; /* this amount of bytes doesn't count when
|
||||
we check if anything has been transferred
|
||||
at the end of a connection. We use this
|
||||
counter to make only a 100 reply (without
|
||||
a following second response code) result
|
||||
in a CURLE_GOT_NOTHING error code */
|
||||
int headerline; /* counts header lines to better track the
|
||||
first one */
|
||||
curl_off_t offset; /* possible resume offset read from the
|
||||
Content-Range: header */
|
||||
int httpversion; /* Version in response (09, 10, 11, etc.) */
|
||||
int httpcode; /* error code from the 'HTTP/1.? XXX' or
|
||||
'RTSP/1.? XXX' line */
|
||||
int keepon;
|
||||
enum upgrade101 upgr101; /* 101 upgrade state */
|
||||
|
||||
/* Client Writer stack, handles transfer- and content-encodings, protocol
|
||||
* checks, pausing by client callbacks. */
|
||||
struct Curl_cwriter *writer_stack;
|
||||
/* Client Reader stack, handles transfer- and content-encodings, protocol
|
||||
* checks, pausing by client callbacks. */
|
||||
struct Curl_creader *reader_stack;
|
||||
struct bufq sendbuf; /* data which needs to be send to the server */
|
||||
size_t sendbuf_hds_len; /* amount of header bytes in sendbuf */
|
||||
time_t timeofdoc;
|
||||
long bodywrites;
|
||||
char *location; /* This points to an allocated version of the Location:
|
||||
header data */
|
||||
char *newurl; /* Set to the new URL to use when a redirect or a retry is
|
||||
wanted */
|
||||
|
||||
/* Allocated protocol-specific data. Each protocol handler makes sure this
|
||||
points to data it needs. */
|
||||
union {
|
||||
struct FILEPROTO *file;
|
||||
struct FTP *ftp;
|
||||
struct HTTP *http;
|
||||
struct IMAP *imap;
|
||||
struct ldapreqinfo *ldap;
|
||||
struct MQTT *mqtt;
|
||||
struct POP3 *pop3;
|
||||
struct RTSP *rtsp;
|
||||
struct smb_request *smb;
|
||||
struct SMTP *smtp;
|
||||
struct SSHPROTO *ssh;
|
||||
struct TELNET *telnet;
|
||||
} p;
|
||||
#ifndef CURL_DISABLE_DOH
|
||||
struct dohdata *doh; /* DoH specific data for this request */
|
||||
#endif
|
||||
#ifndef CURL_DISABLE_COOKIES
|
||||
unsigned char setcookies;
|
||||
#endif
|
||||
BIT(header); /* incoming data has HTTP header */
|
||||
BIT(done); /* request is done, e.g. no more send/recv should
|
||||
* happen. This can be TRUE before `upload_done` or
|
||||
* `download_done` is TRUE. */
|
||||
BIT(content_range); /* set TRUE if Content-Range: was found */
|
||||
BIT(download_done); /* set to TRUE when download is complete */
|
||||
BIT(eos_written); /* iff EOS has been written to client */
|
||||
BIT(eos_read); /* iff EOS has been read from the client */
|
||||
BIT(rewind_read); /* iff reader needs rewind at next start */
|
||||
BIT(upload_done); /* set to TRUE when all request data has been sent */
|
||||
BIT(upload_aborted); /* set to TRUE when upload was aborted. Will also
|
||||
* show `upload_done` as TRUE. */
|
||||
BIT(ignorebody); /* we read a response-body but we ignore it! */
|
||||
BIT(http_bodyless); /* HTTP response status code is between 100 and 199,
|
||||
204 or 304 */
|
||||
BIT(chunk); /* if set, this is a chunked transfer-encoding */
|
||||
BIT(ignore_cl); /* ignore content-length */
|
||||
BIT(upload_chunky); /* set TRUE if we are doing chunked transfer-encoding
|
||||
on upload */
|
||||
BIT(getheader); /* TRUE if header parsing is wanted */
|
||||
BIT(no_body); /* the response has no body */
|
||||
BIT(authneg); /* TRUE when the auth phase has started, which means
|
||||
that we are creating a request with an auth header,
|
||||
but it is not the final request in the auth
|
||||
negotiation. */
|
||||
BIT(sendbuf_init); /* sendbuf is initialized */
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the state of the request for first use.
|
||||
*/
|
||||
CURLcode Curl_req_init(struct SingleRequest *req);
|
||||
|
||||
/**
|
||||
* The request is about to start. Record time and do a soft reset.
|
||||
*/
|
||||
CURLcode Curl_req_start(struct SingleRequest *req,
|
||||
struct Curl_easy *data);
|
||||
|
||||
/**
|
||||
* The request may continue with a follow up. Reset
|
||||
* members, but keep start time for overall duration calc.
|
||||
*/
|
||||
CURLcode Curl_req_soft_reset(struct SingleRequest *req,
|
||||
struct Curl_easy *data);
|
||||
|
||||
/**
|
||||
* The request is done. If not aborted, make sure that buffers are
|
||||
* flushed to the client.
|
||||
* @param req the request
|
||||
* @param data the transfer
|
||||
* @param aborted TRUE iff the request was aborted/errored
|
||||
*/
|
||||
CURLcode Curl_req_done(struct SingleRequest *req,
|
||||
struct Curl_easy *data, bool aborted);
|
||||
|
||||
/**
|
||||
* Free the state of the request, not usable afterwards.
|
||||
*/
|
||||
void Curl_req_free(struct SingleRequest *req, struct Curl_easy *data);
|
||||
|
||||
/**
|
||||
* Hard reset the state of the request to virgin state base on
|
||||
* transfer settings.
|
||||
*/
|
||||
void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data);
|
||||
|
||||
#ifndef USE_HYPER
|
||||
/**
|
||||
* Send request headers. If not all could be sent
|
||||
* they will be buffered. Use `Curl_req_flush()` to make sure
|
||||
* bytes are really send.
|
||||
* @param data the transfer making the request
|
||||
* @param buf the complete header bytes, no body
|
||||
* @return CURLE_OK (on blocking with *pnwritten == 0) or error.
|
||||
*/
|
||||
CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *buf);
|
||||
|
||||
#endif /* !USE_HYPER */
|
||||
|
||||
/**
|
||||
* TRUE iff the request has sent all request headers and data.
|
||||
*/
|
||||
bool Curl_req_done_sending(struct Curl_easy *data);
|
||||
|
||||
/*
|
||||
* Read more from client and flush all buffered request bytes.
|
||||
* @return CURLE_OK on success or the error on the sending.
|
||||
* Never returns CURLE_AGAIN.
|
||||
*/
|
||||
CURLcode Curl_req_send_more(struct Curl_easy *data);
|
||||
|
||||
/**
|
||||
* TRUE iff the request wants to send, e.g. has buffered bytes.
|
||||
*/
|
||||
bool Curl_req_want_send(struct Curl_easy *data);
|
||||
|
||||
/**
|
||||
* Stop sending any more request data to the server.
|
||||
* Will clear the send buffer and mark request sending as done.
|
||||
*/
|
||||
CURLcode Curl_req_abort_sending(struct Curl_easy *data);
|
||||
|
||||
#endif /* HEADER_CURL_REQUEST_H */
|
121
lib/rtsp.c
121
lib/rtsp.c
|
@ -70,8 +70,7 @@ static int rtsp_getsock_do(struct Curl_easy *data,
|
|||
static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data,
|
||||
const char *buf,
|
||||
size_t blen,
|
||||
bool is_eos,
|
||||
bool *done);
|
||||
bool is_eos);
|
||||
|
||||
static CURLcode rtsp_setup_connection(struct Curl_easy *data,
|
||||
struct connectdata *conn);
|
||||
|
@ -225,8 +224,6 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
|
|||
Curl_RtspReq rtspreq = data->set.rtspreq;
|
||||
struct RTSP *rtsp = data->req.p.rtsp;
|
||||
struct dynbuf req_buffer;
|
||||
curl_off_t postsize = 0; /* for ANNOUNCE and SET_PARAMETER */
|
||||
curl_off_t putsize = 0; /* for ANNOUNCE and SET_PARAMETER */
|
||||
|
||||
const char *p_request = NULL;
|
||||
const char *p_session_id = NULL;
|
||||
|
@ -241,6 +238,8 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
|
|||
const char *p_userpwd = NULL;
|
||||
|
||||
*done = TRUE;
|
||||
/* Initialize a dynamic send buffer */
|
||||
Curl_dyn_init(&req_buffer, DYN_RTSP_REQ_HEADER);
|
||||
|
||||
rtsp->CSeq_sent = data->state.rtsp_next_client_CSeq;
|
||||
rtsp->CSeq_recv = 0;
|
||||
|
@ -310,9 +309,8 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
|
|||
}
|
||||
|
||||
if(rtspreq == RTSPREQ_RECEIVE) {
|
||||
Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, -1);
|
||||
|
||||
return result;
|
||||
Curl_xfer_setup(data, FIRSTSOCKET, -1, TRUE, -1);
|
||||
goto out;
|
||||
}
|
||||
|
||||
p_session_id = data->set.str[STRING_RTSP_SESSION_ID];
|
||||
|
@ -320,7 +318,8 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
|
|||
(rtspreq & ~(RTSPREQ_OPTIONS | RTSPREQ_DESCRIBE | RTSPREQ_SETUP))) {
|
||||
failf(data, "Refusing to issue an RTSP request [%s] without a session ID.",
|
||||
p_request);
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
result = CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Stream URI. Default to server '*' if not specified */
|
||||
|
@ -347,7 +346,8 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
|
|||
else {
|
||||
failf(data,
|
||||
"Refusing to issue an RTSP SETUP without a Transport: header.");
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
result = CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
p_transport = data->state.aptr.rtsp_transport;
|
||||
|
@ -366,9 +366,10 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
|
|||
data->state.aptr.accept_encoding =
|
||||
aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]);
|
||||
|
||||
if(!data->state.aptr.accept_encoding)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
if(!data->state.aptr.accept_encoding) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
p_accept_encoding = data->state.aptr.accept_encoding;
|
||||
}
|
||||
}
|
||||
|
@ -390,7 +391,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
|
|||
result = Curl_http_output_auth(data, conn, p_request, HTTPREQ_GET,
|
||||
p_stream_uri, FALSE);
|
||||
if(result)
|
||||
return result;
|
||||
goto out;
|
||||
|
||||
p_proxyuserpwd = data->state.aptr.proxyuserpwd;
|
||||
p_userpwd = data->state.aptr.userpwd;
|
||||
|
@ -424,23 +425,22 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
|
|||
*/
|
||||
if(Curl_checkheaders(data, STRCONST("CSeq"))) {
|
||||
failf(data, "CSeq cannot be set as a custom header.");
|
||||
return CURLE_RTSP_CSEQ_ERROR;
|
||||
result = CURLE_RTSP_CSEQ_ERROR;
|
||||
goto out;
|
||||
}
|
||||
if(Curl_checkheaders(data, STRCONST("Session"))) {
|
||||
failf(data, "Session ID cannot be set as a custom header.");
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
result = CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Initialize a dynamic send buffer */
|
||||
Curl_dyn_init(&req_buffer, DYN_RTSP_REQ_HEADER);
|
||||
|
||||
result =
|
||||
Curl_dyn_addf(&req_buffer,
|
||||
"%s %s RTSP/1.0\r\n" /* Request Stream-URI RTSP/1.0 */
|
||||
"CSeq: %ld\r\n", /* CSeq */
|
||||
p_request, p_stream_uri, rtsp->CSeq_sent);
|
||||
if(result)
|
||||
return result;
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Rather than do a normal alloc line, keep the session_id unformatted
|
||||
|
@ -449,7 +449,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
|
|||
if(p_session_id) {
|
||||
result = Curl_dyn_addf(&req_buffer, "Session: %s\r\n", p_session_id);
|
||||
if(result)
|
||||
return result;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -481,44 +481,58 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
|
|||
Curl_safefree(data->state.aptr.userpwd);
|
||||
|
||||
if(result)
|
||||
return result;
|
||||
goto out;
|
||||
|
||||
if((rtspreq == RTSPREQ_SETUP) || (rtspreq == RTSPREQ_DESCRIBE)) {
|
||||
result = Curl_add_timecondition(data, &req_buffer);
|
||||
if(result)
|
||||
return result;
|
||||
goto out;
|
||||
}
|
||||
|
||||
result = Curl_add_custom_headers(data, FALSE, &req_buffer);
|
||||
if(result)
|
||||
return result;
|
||||
goto out;
|
||||
|
||||
if(rtspreq == RTSPREQ_ANNOUNCE ||
|
||||
rtspreq == RTSPREQ_SET_PARAMETER ||
|
||||
rtspreq == RTSPREQ_GET_PARAMETER) {
|
||||
curl_off_t req_clen; /* request content length */
|
||||
|
||||
if(data->state.upload) {
|
||||
putsize = data->state.infilesize;
|
||||
req_clen = data->state.infilesize;
|
||||
data->state.httpreq = HTTPREQ_PUT;
|
||||
|
||||
result = Curl_creader_set_fread(data, req_clen);
|
||||
if(result)
|
||||
goto out;
|
||||
}
|
||||
else {
|
||||
postsize = (data->state.infilesize != -1)?
|
||||
data->state.infilesize:
|
||||
(data->set.postfields? (curl_off_t)strlen(data->set.postfields):0);
|
||||
data->state.httpreq = HTTPREQ_POST;
|
||||
if(data->set.postfields) {
|
||||
size_t plen = strlen(data->set.postfields);
|
||||
req_clen = (curl_off_t)plen;
|
||||
result = Curl_creader_set_buf(data, data->set.postfields, plen);
|
||||
}
|
||||
else if(data->state.infilesize >= 0) {
|
||||
req_clen = data->state.infilesize;
|
||||
result = Curl_creader_set_fread(data, req_clen);
|
||||
}
|
||||
else {
|
||||
req_clen = 0;
|
||||
result = Curl_creader_set_null(data);
|
||||
}
|
||||
if(result)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(putsize > 0 || postsize > 0) {
|
||||
if(req_clen > 0) {
|
||||
/* As stated in the http comments, it is probably not wise to
|
||||
* actually set a custom Content-Length in the headers */
|
||||
if(!Curl_checkheaders(data, STRCONST("Content-Length"))) {
|
||||
result =
|
||||
Curl_dyn_addf(&req_buffer,
|
||||
"Content-Length: %" CURL_FORMAT_CURL_OFF_T"\r\n",
|
||||
(data->state.upload ? putsize : postsize));
|
||||
req_clen);
|
||||
if(result)
|
||||
return result;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(rtspreq == RTSPREQ_SET_PARAMETER ||
|
||||
|
@ -528,7 +542,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
|
|||
STRCONST("Content-Type: "
|
||||
"text/parameters\r\n"));
|
||||
if(result)
|
||||
return result;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -538,11 +552,9 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
|
|||
STRCONST("Content-Type: "
|
||||
"application/sdp\r\n"));
|
||||
if(result)
|
||||
return result;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
data->state.expect100header = FALSE; /* RTSP posts are simple/small */
|
||||
}
|
||||
else if(rtspreq == RTSPREQ_GET_PARAMETER) {
|
||||
/* Check for an empty GET_PARAMETER (heartbeat) request */
|
||||
|
@ -550,31 +562,26 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
|
|||
data->req.no_body = TRUE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
result = Curl_creader_set_null(data);
|
||||
if(result)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* RTSP never allows chunked transfer */
|
||||
data->req.forbidchunk = TRUE;
|
||||
/* Finish the request buffer */
|
||||
result = Curl_dyn_addn(&req_buffer, STRCONST("\r\n"));
|
||||
if(result)
|
||||
return result;
|
||||
goto out;
|
||||
|
||||
if(postsize > 0) {
|
||||
result = Curl_dyn_addn(&req_buffer, data->set.postfields,
|
||||
(size_t)postsize);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
Curl_xfer_setup(data, FIRSTSOCKET, -1, TRUE, FIRSTSOCKET);
|
||||
|
||||
/* issue the request */
|
||||
result = Curl_buffer_send(&req_buffer, data, data->req.p.http,
|
||||
&data->info.request_size, 0, FIRSTSOCKET);
|
||||
result = Curl_req_send(data, &req_buffer);
|
||||
if(result) {
|
||||
failf(data, "Failed sending RTSP request");
|
||||
return result;
|
||||
goto out;
|
||||
}
|
||||
|
||||
Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, putsize?FIRSTSOCKET:-1);
|
||||
|
||||
/* Increment the CSeq on success */
|
||||
data->state.rtsp_next_client_CSeq++;
|
||||
|
||||
|
@ -585,7 +592,8 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
|
|||
if(Curl_pgrsUpdate(data))
|
||||
result = CURLE_ABORTED_BY_CALLBACK;
|
||||
}
|
||||
|
||||
out:
|
||||
Curl_dyn_free(&req_buffer);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -779,8 +787,7 @@ out:
|
|||
static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data,
|
||||
const char *buf,
|
||||
size_t blen,
|
||||
bool is_eos,
|
||||
bool *done)
|
||||
bool is_eos)
|
||||
{
|
||||
struct rtsp_conn *rtspc = &(data->conn->proto.rtspc);
|
||||
CURLcode result = CURLE_OK;
|
||||
|
@ -788,7 +795,6 @@ static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data,
|
|||
|
||||
if(!data->req.header)
|
||||
rtspc->in_header = FALSE;
|
||||
*done = FALSE;
|
||||
if(!blen) {
|
||||
goto out;
|
||||
}
|
||||
|
@ -812,7 +818,7 @@ static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data,
|
|||
/* we want to parse headers, do so */
|
||||
if(data->req.header && blen) {
|
||||
rtspc->in_header = TRUE;
|
||||
result = Curl_http_write_resp_hds(data, buf, blen, &consumed, done);
|
||||
result = Curl_http_write_resp_hds(data, buf, blen, &consumed);
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
|
@ -838,13 +844,14 @@ static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data,
|
|||
}
|
||||
|
||||
if(rtspc->state != RTP_PARSE_SKIP)
|
||||
*done = FALSE;
|
||||
data->req.done = FALSE;
|
||||
/* we SHOULD have consumed all bytes, unless the response is borked.
|
||||
* In which case we write out the left over bytes, letting the client
|
||||
* writer deal with it (it will report EXCESS and fail the transfer). */
|
||||
DEBUGF(infof(data, "rtsp_rtp_write_resp(len=%zu, in_header=%d, done=%d "
|
||||
" rtspc->state=%d, req.size=%" CURL_FORMAT_CURL_OFF_T ")",
|
||||
blen, rtspc->in_header, *done, rtspc->state, data->req.size));
|
||||
blen, rtspc->in_header, data->req.done, rtspc->state,
|
||||
data->req.size));
|
||||
if(!result && (is_eos || blen)) {
|
||||
result = Curl_client_write(data, CLIENTWRITE_BODY|
|
||||
(is_eos? CLIENTWRITE_EOS:0),
|
||||
|
|
1387
lib/sendf.c
1387
lib/sendf.c
File diff suppressed because it is too large
Load Diff
252
lib/sendf.h
252
lib/sendf.h
|
@ -55,21 +55,26 @@
|
|||
* Write `len` bytes at `prt` to the client. `type` indicates what
|
||||
* kind of data is being written.
|
||||
*/
|
||||
CURLcode Curl_client_write(struct Curl_easy *data, int type, char *ptr,
|
||||
CURLcode Curl_client_write(struct Curl_easy *data, int type, const char *ptr,
|
||||
size_t len) WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* For a paused transfer, there might be buffered data held back.
|
||||
* Attempt to flush this data to the client. This *may* trigger
|
||||
* another pause of the transfer.
|
||||
*/
|
||||
CURLcode Curl_client_unpause(struct Curl_easy *data);
|
||||
|
||||
/**
|
||||
* Free all resources related to client writing.
|
||||
*/
|
||||
void Curl_client_cleanup(struct Curl_easy *data);
|
||||
|
||||
/**
|
||||
* Reset readers and writer chains, keep rewind information
|
||||
* when necessary.
|
||||
*/
|
||||
void Curl_client_reset(struct Curl_easy *data);
|
||||
|
||||
/**
|
||||
* A new request is starting, perform any ops like rewinding
|
||||
* previous readers when needed.
|
||||
*/
|
||||
CURLcode Curl_client_start(struct Curl_easy *data);
|
||||
|
||||
/**
|
||||
* Client Writers - a chain passing transfer BODY data to the client.
|
||||
* Main application: HTTP and related protocols
|
||||
|
@ -112,10 +117,16 @@ struct Curl_cwtype {
|
|||
size_t cwriter_size; /* sizeof() allocated struct Curl_cwriter */
|
||||
};
|
||||
|
||||
/* Client writer instance */
|
||||
/* Client writer instance, allocated on creation.
|
||||
* `void *ctx` is the pointer from the allocation of
|
||||
* the `struct Curl_cwriter` itself. This is suitable for "downcasting"
|
||||
* by the writers implementation. See https://github.com/curl/curl/pull/13054
|
||||
* for the alignment problems that arise otherwise.
|
||||
*/
|
||||
struct Curl_cwriter {
|
||||
const struct Curl_cwtype *cwt; /* type implementation */
|
||||
struct Curl_cwriter *next; /* Downstream writer. */
|
||||
void *ctx; /* allocated instance pointer */
|
||||
Curl_cwriter_phase phase; /* phase at which it operates */
|
||||
};
|
||||
|
||||
|
@ -148,9 +159,19 @@ size_t Curl_cwriter_count(struct Curl_easy *data, Curl_cwriter_phase phase);
|
|||
CURLcode Curl_cwriter_add(struct Curl_easy *data,
|
||||
struct Curl_cwriter *writer);
|
||||
|
||||
/**
|
||||
* Look up an installed client writer on `data` by its type.
|
||||
* @return first writer with that type or NULL
|
||||
*/
|
||||
struct Curl_cwriter *Curl_cwriter_get_by_type(struct Curl_easy *data,
|
||||
const struct Curl_cwtype *cwt);
|
||||
|
||||
void Curl_cwriter_remove_by_name(struct Curl_easy *data,
|
||||
const char *name);
|
||||
|
||||
struct Curl_cwriter *Curl_cwriter_get_by_name(struct Curl_easy *data,
|
||||
const char *name);
|
||||
|
||||
/**
|
||||
* Convenience method for calling `writer->do_write()` that
|
||||
* checks for NULL writer.
|
||||
|
@ -172,22 +193,205 @@ void Curl_cwriter_def_close(struct Curl_easy *data,
|
|||
struct Curl_cwriter *writer);
|
||||
|
||||
|
||||
/* internal read-function, does plain socket, SSL and krb4 */
|
||||
CURLcode Curl_read(struct Curl_easy *data, curl_socket_t sockfd,
|
||||
char *buf, size_t buffersize,
|
||||
ssize_t *n);
|
||||
|
||||
/* internal write-function, does plain socket, SSL, SCP, SFTP and krb4 */
|
||||
CURLcode Curl_write(struct Curl_easy *data,
|
||||
curl_socket_t sockfd,
|
||||
const void *mem, size_t len,
|
||||
ssize_t *written);
|
||||
/* Client Reader Type, provides the implementation */
|
||||
struct Curl_crtype {
|
||||
const char *name; /* writer name. */
|
||||
CURLcode (*do_init)(struct Curl_easy *data, struct Curl_creader *reader);
|
||||
CURLcode (*do_read)(struct Curl_easy *data, struct Curl_creader *reader,
|
||||
char *buf, size_t blen, size_t *nread, bool *eos);
|
||||
void (*do_close)(struct Curl_easy *data, struct Curl_creader *reader);
|
||||
bool (*needs_rewind)(struct Curl_easy *data, struct Curl_creader *reader);
|
||||
curl_off_t (*total_length)(struct Curl_easy *data,
|
||||
struct Curl_creader *reader);
|
||||
CURLcode (*resume_from)(struct Curl_easy *data,
|
||||
struct Curl_creader *reader, curl_off_t offset);
|
||||
CURLcode (*rewind)(struct Curl_easy *data, struct Curl_creader *reader);
|
||||
CURLcode (*unpause)(struct Curl_easy *data, struct Curl_creader *reader);
|
||||
void (*done)(struct Curl_easy *data,
|
||||
struct Curl_creader *reader, int premature);
|
||||
size_t creader_size; /* sizeof() allocated struct Curl_creader */
|
||||
};
|
||||
|
||||
/* internal write-function, using sockindex for connection destination */
|
||||
CURLcode Curl_nwrite(struct Curl_easy *data,
|
||||
int sockindex,
|
||||
const void *buf,
|
||||
size_t blen,
|
||||
ssize_t *pnwritten);
|
||||
/* Phase a reader operates at. */
|
||||
typedef enum {
|
||||
CURL_CR_NET, /* data send to the network (connection filters) */
|
||||
CURL_CR_TRANSFER_ENCODE, /* add transfer-encodings */
|
||||
CURL_CR_PROTOCOL, /* before transfer, but after content decoding */
|
||||
CURL_CR_CONTENT_ENCODE, /* add content-encodings */
|
||||
CURL_CR_CLIENT /* data read from client */
|
||||
} Curl_creader_phase;
|
||||
|
||||
/* Client reader instance, allocated on creation.
|
||||
* `void *ctx` is the pointer from the allocation of
|
||||
* the `struct Curl_cwriter` itself. This is suitable for "downcasting"
|
||||
* by the writers implementation. See https://github.com/curl/curl/pull/13054
|
||||
* for the alignment problems that arise otherwise.
|
||||
*/
|
||||
struct Curl_creader {
|
||||
const struct Curl_crtype *crt; /* type implementation */
|
||||
struct Curl_creader *next; /* Downstream reader. */
|
||||
void *ctx;
|
||||
Curl_creader_phase phase; /* phase at which it operates */
|
||||
};
|
||||
|
||||
/**
|
||||
* Default implementations for do_init, do_write, do_close that
|
||||
* do nothing and pass the data through.
|
||||
*/
|
||||
CURLcode Curl_creader_def_init(struct Curl_easy *data,
|
||||
struct Curl_creader *reader);
|
||||
void Curl_creader_def_close(struct Curl_easy *data,
|
||||
struct Curl_creader *reader);
|
||||
CURLcode Curl_creader_def_read(struct Curl_easy *data,
|
||||
struct Curl_creader *reader,
|
||||
char *buf, size_t blen,
|
||||
size_t *nread, bool *eos);
|
||||
bool Curl_creader_def_needs_rewind(struct Curl_easy *data,
|
||||
struct Curl_creader *reader);
|
||||
curl_off_t Curl_creader_def_total_length(struct Curl_easy *data,
|
||||
struct Curl_creader *reader);
|
||||
CURLcode Curl_creader_def_resume_from(struct Curl_easy *data,
|
||||
struct Curl_creader *reader,
|
||||
curl_off_t offset);
|
||||
CURLcode Curl_creader_def_rewind(struct Curl_easy *data,
|
||||
struct Curl_creader *reader);
|
||||
CURLcode Curl_creader_def_unpause(struct Curl_easy *data,
|
||||
struct Curl_creader *reader);
|
||||
void Curl_creader_def_done(struct Curl_easy *data,
|
||||
struct Curl_creader *reader, int premature);
|
||||
|
||||
/**
|
||||
* Convenience method for calling `reader->do_read()` that
|
||||
* checks for NULL reader.
|
||||
*/
|
||||
CURLcode Curl_creader_read(struct Curl_easy *data,
|
||||
struct Curl_creader *reader,
|
||||
char *buf, size_t blen, size_t *nread, bool *eos);
|
||||
|
||||
/**
|
||||
* Create a new creader instance with given type and phase. Is not
|
||||
* inserted into the writer chain by this call.
|
||||
* Invokes `reader->do_init()`.
|
||||
*/
|
||||
CURLcode Curl_creader_create(struct Curl_creader **preader,
|
||||
struct Curl_easy *data,
|
||||
const struct Curl_crtype *cr_handler,
|
||||
Curl_creader_phase phase);
|
||||
|
||||
/**
|
||||
* Free a creader instance.
|
||||
* Invokes `reader->do_close()`.
|
||||
*/
|
||||
void Curl_creader_free(struct Curl_easy *data, struct Curl_creader *reader);
|
||||
|
||||
/**
|
||||
* Adds a reader to the transfer's reader chain.
|
||||
* The readers `phase` determines where in the chain it is inserted.
|
||||
*/
|
||||
CURLcode Curl_creader_add(struct Curl_easy *data,
|
||||
struct Curl_creader *reader);
|
||||
|
||||
/**
|
||||
* Set the given reader, which needs to be of type CURL_CR_CLIENT,
|
||||
* as the new first reader. Discard any installed readers and init
|
||||
* the reader chain anew.
|
||||
* The function takes ownership of `r`.
|
||||
*/
|
||||
CURLcode Curl_creader_set(struct Curl_easy *data, struct Curl_creader *r);
|
||||
|
||||
/**
|
||||
* Read at most `blen` bytes at `buf` from the client.
|
||||
* @param date the transfer to read client bytes for
|
||||
* @param buf the memory location to read to
|
||||
* @param blen the amount of memory at `buf`
|
||||
* @param nread on return the number of bytes read into `buf`
|
||||
* @param eos TRUE iff bytes are the end of data from client
|
||||
* @return CURLE_OK on successful read (even 0 length) or error
|
||||
*/
|
||||
CURLcode Curl_client_read(struct Curl_easy *data, char *buf, size_t blen,
|
||||
size_t *nread, bool *eos) WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* TRUE iff client reader needs rewing before it can be used for
|
||||
* a retry request.
|
||||
*/
|
||||
bool Curl_creader_needs_rewind(struct Curl_easy *data);
|
||||
|
||||
/**
|
||||
* TRUE iff client reader will rewind at next start
|
||||
*/
|
||||
bool Curl_creader_will_rewind(struct Curl_easy *data);
|
||||
|
||||
/**
|
||||
* En-/disable rewind of client reader at next start.
|
||||
*/
|
||||
void Curl_creader_set_rewind(struct Curl_easy *data, bool enable);
|
||||
|
||||
/**
|
||||
* Get the total length of bytes provided by the installed readers.
|
||||
* This is independent of the amount already delivered and is calculated
|
||||
* by all readers in the stack. If a reader like "chunked" or
|
||||
* "crlf conversion" is installed, the returned length will be -1.
|
||||
* @return -1 if length is indeterminate
|
||||
*/
|
||||
curl_off_t Curl_creader_total_length(struct Curl_easy *data);
|
||||
|
||||
/**
|
||||
* Get the total length of bytes provided by the reader at phase
|
||||
* CURL_CR_CLIENT. This may not match the amount of bytes read
|
||||
* for a request, depending if other, encoding readers are also installed.
|
||||
* However it allows for rough estimation of the overall length.
|
||||
* @return -1 if length is indeterminate
|
||||
*/
|
||||
curl_off_t Curl_creader_client_length(struct Curl_easy *data);
|
||||
|
||||
/**
|
||||
* Ask the installed reader at phase CURL_CR_CLIENT to start
|
||||
* reading from the given offset. On success, this will reduce
|
||||
* the `total_length()` by the amount.
|
||||
* @param date the transfer to read client bytes for
|
||||
* param offset the offset where to start reads from, negative
|
||||
* values will be ignored.
|
||||
* @return CURLE_OK if offset could be set
|
||||
* CURLE_READ_ERROR if not supported by reader or seek/read failed
|
||||
* of offset larger then total length
|
||||
* CURLE_PARTIAL_FILE if offset led to 0 total length
|
||||
*/
|
||||
CURLcode Curl_creader_resume_from(struct Curl_easy *data, curl_off_t offset);
|
||||
|
||||
/**
|
||||
* Unpause all installed readers.
|
||||
*/
|
||||
CURLcode Curl_creader_unpause(struct Curl_easy *data);
|
||||
|
||||
/**
|
||||
* Tell all client readers that they are done.
|
||||
*/
|
||||
void Curl_creader_done(struct Curl_easy *data, int premature);
|
||||
|
||||
/**
|
||||
* Look up an installed client reader on `data` by its type.
|
||||
* @return first reader with that type or NULL
|
||||
*/
|
||||
struct Curl_creader *Curl_creader_get_by_type(struct Curl_easy *data,
|
||||
const struct Curl_crtype *crt);
|
||||
|
||||
|
||||
/**
|
||||
* Set the client reader to provide 0 bytes, immediate EOS.
|
||||
*/
|
||||
CURLcode Curl_creader_set_null(struct Curl_easy *data);
|
||||
|
||||
/**
|
||||
* Set the client reader the reads from fread callback.
|
||||
*/
|
||||
CURLcode Curl_creader_set_fread(struct Curl_easy *data, curl_off_t len);
|
||||
|
||||
/**
|
||||
* Set the client reader the reads from the supplied buf (NOT COPIED).
|
||||
*/
|
||||
CURLcode Curl_creader_set_buf(struct Curl_easy *data,
|
||||
const char *buf, size_t blen);
|
||||
|
||||
#endif /* HEADER_CURL_SENDF_H */
|
||||
|
|
24
lib/setopt.c
24
lib/setopt.c
|
@ -155,6 +155,12 @@ static CURLcode setstropt_userpwd(char *option, char **userp, char **passwdp)
|
|||
|
||||
static CURLcode protocol2num(const char *str, curl_prot_t *val)
|
||||
{
|
||||
/*
|
||||
* We are asked to cherry-pick protocols, so play it safe and disallow all
|
||||
* protocols to start with, and re-add the wanted ones back in.
|
||||
*/
|
||||
*val = 0;
|
||||
|
||||
if(!str)
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
|
||||
|
@ -163,8 +169,6 @@ static CURLcode protocol2num(const char *str, curl_prot_t *val)
|
|||
return CURLE_OK;
|
||||
}
|
||||
|
||||
*val = 0;
|
||||
|
||||
do {
|
||||
const char *token = str;
|
||||
size_t tlen;
|
||||
|
@ -2210,9 +2214,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
|
|||
* The application kindly asks for a differently sized receive buffer.
|
||||
* If it seems reasonable, we'll use it.
|
||||
*/
|
||||
if(data->state.buffer)
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
|
||||
arg = va_arg(param, long);
|
||||
|
||||
if(arg > READBUFFER_MAX)
|
||||
|
@ -2238,7 +2239,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
|
|||
arg = UPLOADBUFFER_MIN;
|
||||
|
||||
data->set.upload_buffer_size = (unsigned int)arg;
|
||||
Curl_safefree(data->state.ulbuf); /* force a realloc next opportunity */
|
||||
break;
|
||||
|
||||
case CURLOPT_NOSIGNAL:
|
||||
|
@ -2657,22 +2657,18 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
|
|||
break;
|
||||
|
||||
case CURLOPT_PROTOCOLS_STR: {
|
||||
curl_prot_t prot;
|
||||
argptr = va_arg(param, char *);
|
||||
result = protocol2num(argptr, &prot);
|
||||
result = protocol2num(argptr, &data->set.allowed_protocols);
|
||||
if(result)
|
||||
return result;
|
||||
data->set.allowed_protocols = prot;
|
||||
break;
|
||||
}
|
||||
|
||||
case CURLOPT_REDIR_PROTOCOLS_STR: {
|
||||
curl_prot_t prot;
|
||||
argptr = va_arg(param, char *);
|
||||
result = protocol2num(argptr, &prot);
|
||||
result = protocol2num(argptr, &data->set.redir_protocols);
|
||||
if(result)
|
||||
return result;
|
||||
data->set.redir_protocols = prot;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -2867,13 +2863,13 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
|
|||
#endif
|
||||
case CURLOPT_TLSAUTH_TYPE:
|
||||
argptr = va_arg(param, char *);
|
||||
if(argptr && !strncasecompare(argptr, "SRP", strlen("SRP")))
|
||||
if(argptr && !strcasecompare(argptr, "SRP"))
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
break;
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
case CURLOPT_PROXY_TLSAUTH_TYPE:
|
||||
argptr = va_arg(param, char *);
|
||||
if(argptr || !strncasecompare(argptr, "SRP", strlen("SRP")))
|
||||
if(argptr && !strcasecompare(argptr, "SRP"))
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
break;
|
||||
#endif
|
||||
|
|
51
lib/smb.c
51
lib/smb.c
|
@ -456,6 +456,9 @@ static CURLcode smb_connect(struct Curl_easy *data, bool *done)
|
|||
smbc->recv_buf = malloc(MAX_MESSAGE_SIZE);
|
||||
if(!smbc->recv_buf)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
smbc->send_buf = malloc(MAX_MESSAGE_SIZE);
|
||||
if(!smbc->send_buf)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
/* Multiple requests are allowed with this connection */
|
||||
connkeep(conn, "SMB default");
|
||||
|
@ -485,7 +488,6 @@ static CURLcode smb_connect(struct Curl_easy *data, bool *done)
|
|||
static CURLcode smb_recv_message(struct Curl_easy *data, void **msg)
|
||||
{
|
||||
struct connectdata *conn = data->conn;
|
||||
curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
|
||||
struct smb_conn *smbc = &conn->proto.smbc;
|
||||
char *buf = smbc->recv_buf;
|
||||
ssize_t bytes_read;
|
||||
|
@ -494,7 +496,7 @@ static CURLcode smb_recv_message(struct Curl_easy *data, void **msg)
|
|||
size_t len = MAX_MESSAGE_SIZE - smbc->got;
|
||||
CURLcode result;
|
||||
|
||||
result = Curl_read(data, sockfd, buf + smbc->got, len, &bytes_read);
|
||||
result = Curl_xfer_recv(data, buf + smbc->got, len, &bytes_read);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
|
@ -560,16 +562,15 @@ static void smb_format_message(struct Curl_easy *data, struct smb_header *h,
|
|||
h->pid = smb_swap16((unsigned short) pid);
|
||||
}
|
||||
|
||||
static CURLcode smb_send(struct Curl_easy *data, ssize_t len,
|
||||
static CURLcode smb_send(struct Curl_easy *data, size_t len,
|
||||
size_t upload_size)
|
||||
{
|
||||
struct connectdata *conn = data->conn;
|
||||
struct smb_conn *smbc = &conn->proto.smbc;
|
||||
ssize_t bytes_written;
|
||||
size_t bytes_written;
|
||||
CURLcode result;
|
||||
|
||||
result = Curl_nwrite(data, FIRSTSOCKET, data->state.ulbuf,
|
||||
len, &bytes_written);
|
||||
result = Curl_xfer_send(data, smbc->send_buf, len, &bytes_written);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
|
@ -587,16 +588,15 @@ static CURLcode smb_flush(struct Curl_easy *data)
|
|||
{
|
||||
struct connectdata *conn = data->conn;
|
||||
struct smb_conn *smbc = &conn->proto.smbc;
|
||||
ssize_t bytes_written;
|
||||
ssize_t len = smbc->send_size - smbc->sent;
|
||||
size_t bytes_written;
|
||||
size_t len = smbc->send_size - smbc->sent;
|
||||
CURLcode result;
|
||||
|
||||
if(!smbc->send_size)
|
||||
return CURLE_OK;
|
||||
|
||||
result = Curl_nwrite(data, FIRSTSOCKET,
|
||||
data->state.ulbuf + smbc->sent,
|
||||
len, &bytes_written);
|
||||
result = Curl_xfer_send(data, smbc->send_buf + smbc->sent, len,
|
||||
&bytes_written);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
|
@ -611,13 +611,13 @@ static CURLcode smb_flush(struct Curl_easy *data)
|
|||
static CURLcode smb_send_message(struct Curl_easy *data, unsigned char cmd,
|
||||
const void *msg, size_t msg_len)
|
||||
{
|
||||
CURLcode result = Curl_get_upload_buffer(data);
|
||||
if(result)
|
||||
return result;
|
||||
smb_format_message(data, (struct smb_header *)data->state.ulbuf,
|
||||
struct connectdata *conn = data->conn;
|
||||
struct smb_conn *smbc = &conn->proto.smbc;
|
||||
|
||||
smb_format_message(data, (struct smb_header *)smbc->send_buf,
|
||||
cmd, msg_len);
|
||||
memcpy(data->state.ulbuf + sizeof(struct smb_header),
|
||||
msg, msg_len);
|
||||
DEBUGASSERT((sizeof(struct smb_header) + msg_len) <= MAX_MESSAGE_SIZE);
|
||||
memcpy(smbc->send_buf + sizeof(struct smb_header), msg, msg_len);
|
||||
|
||||
return smb_send(data, sizeof(struct smb_header) + msg_len, 0);
|
||||
}
|
||||
|
@ -775,15 +775,14 @@ static CURLcode smb_send_read(struct Curl_easy *data)
|
|||
|
||||
static CURLcode smb_send_write(struct Curl_easy *data)
|
||||
{
|
||||
struct connectdata *conn = data->conn;
|
||||
struct smb_conn *smbc = &conn->proto.smbc;
|
||||
struct smb_write *msg;
|
||||
struct smb_request *req = data->req.p.smb;
|
||||
curl_off_t offset = data->req.offset;
|
||||
curl_off_t upload_size = data->req.size - data->req.bytecount;
|
||||
CURLcode result = Curl_get_upload_buffer(data);
|
||||
if(result)
|
||||
return result;
|
||||
msg = (struct smb_write *)data->state.ulbuf;
|
||||
|
||||
msg = (struct smb_write *)smbc->send_buf;
|
||||
if(upload_size >= MAX_PAYLOAD_SIZE - 1) /* There is one byte of padding */
|
||||
upload_size = MAX_PAYLOAD_SIZE - 1;
|
||||
|
||||
|
@ -812,10 +811,11 @@ static CURLcode smb_send_and_recv(struct Curl_easy *data, void **msg)
|
|||
|
||||
/* Check if there is data in the transfer buffer */
|
||||
if(!smbc->send_size && smbc->upload_size) {
|
||||
size_t nread = smbc->upload_size > (size_t)data->set.upload_buffer_size ?
|
||||
(size_t)data->set.upload_buffer_size : smbc->upload_size;
|
||||
data->req.upload_fromhere = data->state.ulbuf;
|
||||
result = Curl_fillreadbuffer(data, nread, &nread);
|
||||
size_t nread = smbc->upload_size > (size_t)MAX_MESSAGE_SIZE ?
|
||||
(size_t)MAX_MESSAGE_SIZE : smbc->upload_size;
|
||||
bool eos;
|
||||
|
||||
result = Curl_client_read(data, smbc->send_buf, nread, &nread, &eos);
|
||||
if(result && result != CURLE_AGAIN)
|
||||
return result;
|
||||
if(!nread)
|
||||
|
@ -1133,6 +1133,7 @@ static CURLcode smb_disconnect(struct Curl_easy *data,
|
|||
Curl_safefree(smbc->share);
|
||||
Curl_safefree(smbc->domain);
|
||||
Curl_safefree(smbc->recv_buf);
|
||||
Curl_safefree(smbc->send_buf);
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ struct smb_conn {
|
|||
unsigned int session_key;
|
||||
unsigned short uid;
|
||||
char *recv_buf;
|
||||
char *send_buf;
|
||||
size_t upload_size;
|
||||
size_t send_size;
|
||||
size_t sent;
|
||||
|
|
380
lib/smtp.c
380
lib/smtp.c
|
@ -111,6 +111,7 @@ static CURLcode smtp_continue_auth(struct Curl_easy *data, const char *mech,
|
|||
const struct bufref *resp);
|
||||
static CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech);
|
||||
static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out);
|
||||
static CURLcode cr_eob_add(struct Curl_easy *data);
|
||||
|
||||
/*
|
||||
* SMTP protocol handler.
|
||||
|
@ -618,7 +619,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
|
|||
result = smtp_parse_address(data->set.str[STRING_MAIL_FROM],
|
||||
&address, &host);
|
||||
if(result)
|
||||
return result;
|
||||
goto out;
|
||||
|
||||
/* Establish whether we should report SMTPUTF8 to the server for this
|
||||
mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */
|
||||
|
@ -642,8 +643,10 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
|
|||
/* Null reverse-path, RFC-5321, sect. 3.6.3 */
|
||||
from = strdup("<>");
|
||||
|
||||
if(!from)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
if(!from) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Calculate the optional AUTH parameter */
|
||||
if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.sasl.authused) {
|
||||
|
@ -655,10 +658,8 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
|
|||
converting the host name to an IDN A-label if necessary */
|
||||
result = smtp_parse_address(data->set.str[STRING_MAIL_AUTH],
|
||||
&address, &host);
|
||||
if(result) {
|
||||
free(from);
|
||||
return result;
|
||||
}
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
/* Establish whether we should report SMTPUTF8 to the server for this
|
||||
mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */
|
||||
|
@ -676,7 +677,6 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
|
|||
/* An invalid mailbox was provided but we'll simply let the server
|
||||
worry about it */
|
||||
auth = aprintf("<%s>", address);
|
||||
|
||||
free(address);
|
||||
}
|
||||
else
|
||||
|
@ -684,12 +684,12 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
|
|||
auth = strdup("<>");
|
||||
|
||||
if(!auth) {
|
||||
free(from);
|
||||
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef CURL_DISABLE_MIME
|
||||
/* Prepare the mime data if some. */
|
||||
if(data->set.mimepost.kind != MIMEKIND_NONE) {
|
||||
/* Use the whole structure as data. */
|
||||
|
@ -705,22 +705,18 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
|
|||
result = Curl_mime_add_header(&data->set.mimepost.curlheaders,
|
||||
"Mime-Version: 1.0");
|
||||
|
||||
/* Make sure we will read the entire mime structure. */
|
||||
if(!result)
|
||||
result = Curl_mime_rewind(&data->set.mimepost);
|
||||
|
||||
if(result) {
|
||||
free(from);
|
||||
free(auth);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
data->state.infilesize = Curl_mime_size(&data->set.mimepost);
|
||||
|
||||
/* Read from mime structure. */
|
||||
data->state.fread_func = (curl_read_callback) Curl_mime_read;
|
||||
data->state.in = (void *) &data->set.mimepost;
|
||||
result = Curl_creader_set_mime(data, &data->set.mimepost);
|
||||
if(result)
|
||||
goto out;
|
||||
data->state.infilesize = Curl_creader_total_length(data);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
result = Curl_creader_set_fread(data, data->state.infilesize);
|
||||
if(result)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Calculate the optional SIZE parameter */
|
||||
|
@ -728,10 +724,8 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
|
|||
size = aprintf("%" CURL_FORMAT_CURL_OFF_T, data->state.infilesize);
|
||||
|
||||
if(!size) {
|
||||
free(from);
|
||||
free(auth);
|
||||
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -752,6 +746,11 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
|
|||
}
|
||||
}
|
||||
|
||||
/* Add the client reader doing STMP EOB escaping */
|
||||
result = cr_eob_add(data);
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
/* Send the MAIL command */
|
||||
result = Curl_pp_sendf(data, &conn->proto.smtpc.pp,
|
||||
"MAIL FROM:%s%s%s%s%s%s",
|
||||
|
@ -763,6 +762,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
|
|||
utf8 ? " SMTPUTF8" /* Internationalised mailbox */
|
||||
: ""); /* included in our envelope */
|
||||
|
||||
out:
|
||||
free(from);
|
||||
free(auth);
|
||||
free(size);
|
||||
|
@ -1162,7 +1162,7 @@ static CURLcode smtp_state_data_resp(struct Curl_easy *data, int smtpcode,
|
|||
Curl_pgrsSetUploadSize(data, data->state.infilesize);
|
||||
|
||||
/* SMTP upload */
|
||||
Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
|
||||
Curl_xfer_setup(data, -1, -1, FALSE, FIRSTSOCKET);
|
||||
|
||||
/* End of DO phase */
|
||||
smtp_state(data, SMTP_STOP);
|
||||
|
@ -1194,7 +1194,6 @@ static CURLcode smtp_statemachine(struct Curl_easy *data,
|
|||
struct connectdata *conn)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
curl_socket_t sock = conn->sock[FIRSTSOCKET];
|
||||
int smtpcode;
|
||||
struct smtp_conn *smtpc = &conn->proto.smtpc;
|
||||
struct pingpong *pp = &smtpc->pp;
|
||||
|
@ -1210,7 +1209,7 @@ static CURLcode smtp_statemachine(struct Curl_easy *data,
|
|||
|
||||
do {
|
||||
/* Read the response from the server */
|
||||
result = Curl_pp_readresp(data, sock, pp, &smtpcode, &nread);
|
||||
result = Curl_pp_readresp(data, FIRSTSOCKET, pp, &smtpcode, &nread);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
|
@ -1392,10 +1391,6 @@ static CURLcode smtp_done(struct Curl_easy *data, CURLcode status,
|
|||
CURLcode result = CURLE_OK;
|
||||
struct connectdata *conn = data->conn;
|
||||
struct SMTP *smtp = data->req.p.smtp;
|
||||
struct pingpong *pp = &conn->proto.smtpc.pp;
|
||||
char *eob;
|
||||
ssize_t len;
|
||||
ssize_t bytes_written;
|
||||
|
||||
(void)premature;
|
||||
|
||||
|
@ -1410,47 +1405,7 @@ static CURLcode smtp_done(struct Curl_easy *data, CURLcode status,
|
|||
result = status; /* use the already set error code */
|
||||
}
|
||||
else if(!data->set.connect_only && data->set.mail_rcpt &&
|
||||
(data->state.upload || data->set.mimepost.kind)) {
|
||||
/* Calculate the EOB taking into account any terminating CRLF from the
|
||||
previous line of the email or the CRLF of the DATA command when there
|
||||
is "no mail data". RFC-5321, sect. 4.1.1.4.
|
||||
|
||||
Note: As some SSL backends, such as OpenSSL, will cause Curl_write() to
|
||||
fail when using a different pointer following a previous write, that
|
||||
returned CURLE_AGAIN, we duplicate the EOB now rather than when the
|
||||
bytes written doesn't equal len. */
|
||||
if(smtp->trailing_crlf || !data->state.infilesize) {
|
||||
eob = strdup(&SMTP_EOB[2]);
|
||||
len = SMTP_EOB_LEN - 2;
|
||||
}
|
||||
else {
|
||||
eob = strdup(SMTP_EOB);
|
||||
len = SMTP_EOB_LEN;
|
||||
}
|
||||
|
||||
if(!eob)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
/* Send the end of block data */
|
||||
result = Curl_write(data, conn->writesockfd, eob, len, &bytes_written);
|
||||
if(result) {
|
||||
free(eob);
|
||||
return result;
|
||||
}
|
||||
|
||||
if(bytes_written != len) {
|
||||
/* The whole chunk was not sent so keep it around and adjust the
|
||||
pingpong structure accordingly */
|
||||
pp->sendthis = eob;
|
||||
pp->sendsize = len;
|
||||
pp->sendleft = len - bytes_written;
|
||||
}
|
||||
else {
|
||||
/* Successfully sent so adjust the response timeout relative to now */
|
||||
pp->response = Curl_now();
|
||||
|
||||
free(eob);
|
||||
}
|
||||
(data->state.upload || IS_MIME_POST(data))) {
|
||||
|
||||
smtp_state(data, SMTP_POSTDATA);
|
||||
|
||||
|
@ -1502,7 +1457,7 @@ static CURLcode smtp_perform(struct Curl_easy *data, bool *connected,
|
|||
smtp->eob = 2;
|
||||
|
||||
/* Start the first command in the DO phase */
|
||||
if((data->state.upload || data->set.mimepost.kind) && data->set.mail_rcpt)
|
||||
if((data->state.upload || IS_MIME_POST(data)) && data->set.mail_rcpt)
|
||||
/* MAIL transfer */
|
||||
result = smtp_perform_mail(data);
|
||||
else
|
||||
|
@ -1593,7 +1548,7 @@ static CURLcode smtp_dophase_done(struct Curl_easy *data, bool connected)
|
|||
|
||||
if(smtp->transfer != PPTRANSFER_BODY)
|
||||
/* no data to transfer */
|
||||
Curl_setup_transfer(data, -1, -1, FALSE, -1);
|
||||
Curl_xfer_setup(data, -1, -1, FALSE, -1);
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
@ -1818,108 +1773,173 @@ static CURLcode smtp_parse_address(const char *fqma, char **address,
|
|||
return result;
|
||||
}
|
||||
|
||||
CURLcode Curl_smtp_escape_eob(struct Curl_easy *data,
|
||||
const ssize_t nread,
|
||||
const ssize_t offset)
|
||||
struct cr_eob_ctx {
|
||||
struct Curl_creader super;
|
||||
struct bufq buf;
|
||||
size_t n_eob; /* how many EOB bytes we matched so far */
|
||||
size_t eob; /* Number of bytes of the EOB (End Of Body) that
|
||||
have been received so far */
|
||||
BIT(read_eos); /* we read an EOS from the next reader */
|
||||
BIT(eos); /* we have returned an EOS */
|
||||
};
|
||||
|
||||
static CURLcode cr_eob_init(struct Curl_easy *data,
|
||||
struct Curl_creader *reader)
|
||||
{
|
||||
/* When sending a SMTP payload we must detect CRLF. sequences making sure
|
||||
they are sent as CRLF.. instead, as a . on the beginning of a line will
|
||||
be deleted by the server when not part of an EOB terminator and a
|
||||
genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of
|
||||
data by the server
|
||||
*/
|
||||
ssize_t i;
|
||||
ssize_t si;
|
||||
struct SMTP *smtp = data->req.p.smtp;
|
||||
char *scratch = data->state.scratch;
|
||||
char *newscratch = NULL;
|
||||
char *oldscratch = NULL;
|
||||
size_t eob_sent;
|
||||
|
||||
/* Do we need to allocate a scratch buffer? */
|
||||
if(!scratch || data->set.crlf) {
|
||||
oldscratch = scratch;
|
||||
|
||||
scratch = newscratch = malloc(2 * data->set.upload_buffer_size);
|
||||
if(!newscratch) {
|
||||
failf(data, "Failed to alloc scratch buffer");
|
||||
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
DEBUGASSERT((size_t)data->set.upload_buffer_size >= (size_t)nread);
|
||||
|
||||
/* Have we already sent part of the EOB? */
|
||||
eob_sent = smtp->eob;
|
||||
|
||||
/* This loop can be improved by some kind of Boyer-Moore style of
|
||||
approach but that is saved for later... */
|
||||
if(offset)
|
||||
memcpy(scratch, data->req.upload_fromhere, offset);
|
||||
for(i = offset, si = offset; i < nread; i++) {
|
||||
if(SMTP_EOB[smtp->eob] == data->req.upload_fromhere[i]) {
|
||||
smtp->eob++;
|
||||
|
||||
/* Is the EOB potentially the terminating CRLF? */
|
||||
if(2 == smtp->eob || SMTP_EOB_LEN == smtp->eob)
|
||||
smtp->trailing_crlf = TRUE;
|
||||
else
|
||||
smtp->trailing_crlf = FALSE;
|
||||
}
|
||||
else if(smtp->eob) {
|
||||
/* A previous substring matched so output that first */
|
||||
memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent);
|
||||
si += smtp->eob - eob_sent;
|
||||
|
||||
/* Then compare the first byte */
|
||||
if(SMTP_EOB[0] == data->req.upload_fromhere[i])
|
||||
smtp->eob = 1;
|
||||
else
|
||||
smtp->eob = 0;
|
||||
|
||||
eob_sent = 0;
|
||||
|
||||
/* Reset the trailing CRLF flag as there was more data */
|
||||
smtp->trailing_crlf = FALSE;
|
||||
}
|
||||
|
||||
/* Do we have a match for CRLF. as per RFC-5321, sect. 4.5.2 */
|
||||
if(SMTP_EOB_FIND_LEN == smtp->eob) {
|
||||
/* Copy the replacement data to the target buffer */
|
||||
memcpy(&scratch[si], &SMTP_EOB_REPL[eob_sent],
|
||||
SMTP_EOB_REPL_LEN - eob_sent);
|
||||
si += SMTP_EOB_REPL_LEN - eob_sent;
|
||||
smtp->eob = 0;
|
||||
eob_sent = 0;
|
||||
}
|
||||
else if(!smtp->eob)
|
||||
scratch[si++] = data->req.upload_fromhere[i];
|
||||
}
|
||||
|
||||
if(smtp->eob - eob_sent) {
|
||||
/* A substring matched before processing ended so output that now */
|
||||
memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent);
|
||||
si += smtp->eob - eob_sent;
|
||||
}
|
||||
|
||||
/* Only use the new buffer if we replaced something */
|
||||
if(si != nread) {
|
||||
/* Upload from the new (replaced) buffer instead */
|
||||
data->req.upload_fromhere = scratch;
|
||||
|
||||
/* Save the buffer so it can be freed later */
|
||||
data->state.scratch = scratch;
|
||||
|
||||
/* Free the old scratch buffer */
|
||||
free(oldscratch);
|
||||
|
||||
/* Set the new amount too */
|
||||
data->req.upload_present = si;
|
||||
}
|
||||
else
|
||||
free(newscratch);
|
||||
|
||||
struct cr_eob_ctx *ctx = reader->ctx;
|
||||
(void)data;
|
||||
/* The first char we read is the first on a line, as if we had
|
||||
* read CRLF just before */
|
||||
ctx->n_eob = 2;
|
||||
Curl_bufq_init2(&ctx->buf, (16 * 1024), 1, BUFQ_OPT_SOFT_LIMIT);
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static void cr_eob_close(struct Curl_easy *data, struct Curl_creader *reader)
|
||||
{
|
||||
struct cr_eob_ctx *ctx = reader->ctx;
|
||||
(void)data;
|
||||
Curl_bufq_free(&ctx->buf);
|
||||
}
|
||||
|
||||
/* this is the 5-bytes End-Of-Body marker for SMTP */
|
||||
#define SMTP_EOB "\r\n.\r\n"
|
||||
#define SMTP_EOB_FIND_LEN 3
|
||||
|
||||
/* client reader doing SMTP End-Of-Body escaping. */
|
||||
static CURLcode cr_eob_read(struct Curl_easy *data,
|
||||
struct Curl_creader *reader,
|
||||
char *buf, size_t blen,
|
||||
size_t *pnread, bool *peos)
|
||||
{
|
||||
struct cr_eob_ctx *ctx = reader->ctx;
|
||||
CURLcode result = CURLE_OK;
|
||||
size_t nread, i, start, n;
|
||||
bool eos;
|
||||
|
||||
if(!ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) {
|
||||
/* Get more and convert it when needed */
|
||||
result = Curl_creader_read(data, reader->next, buf, blen, &nread, &eos);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
ctx->read_eos = eos;
|
||||
if(nread) {
|
||||
if(!ctx->n_eob && !memchr(buf, SMTP_EOB[0], nread)) {
|
||||
/* not in the middle of a match, no EOB start found, just pass */
|
||||
*pnread = nread;
|
||||
*peos = FALSE;
|
||||
return CURLE_OK;
|
||||
}
|
||||
/* scan for EOB (continuation) and convert */
|
||||
for(i = start = 0; i < nread; ++i) {
|
||||
if(ctx->n_eob >= SMTP_EOB_FIND_LEN) {
|
||||
/* matched the EOB prefix and seeing additional char, add '.' */
|
||||
result = Curl_bufq_cwrite(&ctx->buf, buf + start, i - start, &n);
|
||||
if(result)
|
||||
return result;
|
||||
result = Curl_bufq_cwrite(&ctx->buf, ".", 1, &n);
|
||||
if(result)
|
||||
return result;
|
||||
ctx->n_eob = 0;
|
||||
start = i;
|
||||
if(data->state.infilesize > 0)
|
||||
data->state.infilesize++;
|
||||
}
|
||||
|
||||
if(buf[i] != SMTP_EOB[ctx->n_eob])
|
||||
ctx->n_eob = 0;
|
||||
|
||||
if(buf[i] == SMTP_EOB[ctx->n_eob]) {
|
||||
/* matching another char of the EOB */
|
||||
++ctx->n_eob;
|
||||
}
|
||||
}
|
||||
|
||||
/* add any remainder to buf */
|
||||
if(start < nread) {
|
||||
result = Curl_bufq_cwrite(&ctx->buf, buf + start, nread - start, &n);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if(ctx->read_eos) {
|
||||
/* if we last matched a CRLF or if the data was empty, add ".\r\n"
|
||||
* to end the body. If we sent something and it did not end with "\r\n",
|
||||
* add "\r\n.\r\n" to end the body */
|
||||
const char *eob = SMTP_EOB;
|
||||
switch(ctx->n_eob) {
|
||||
case 2:
|
||||
/* seen a CRLF at the end, just add the remainder */
|
||||
eob = &SMTP_EOB[2];
|
||||
break;
|
||||
case 3:
|
||||
/* ended with '\r\n.', we should escpe the last '.' */
|
||||
eob = "." SMTP_EOB;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
result = Curl_bufq_cwrite(&ctx->buf, eob, strlen(eob), &n);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
*peos = FALSE;
|
||||
if(!Curl_bufq_is_empty(&ctx->buf)) {
|
||||
result = Curl_bufq_cread(&ctx->buf, buf, blen, pnread);
|
||||
}
|
||||
else
|
||||
*pnread = 0;
|
||||
|
||||
if(ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) {
|
||||
/* no more data, read all, done. */
|
||||
ctx->eos = TRUE;
|
||||
}
|
||||
*peos = ctx->eos;
|
||||
DEBUGF(infof(data, "cr_eob_read(%zu) -> %d, %zd, %d",
|
||||
blen, result, *pnread, *peos));
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static curl_off_t cr_eob_total_length(struct Curl_easy *data,
|
||||
struct Curl_creader *reader)
|
||||
{
|
||||
/* this reader changes length depending on input */
|
||||
(void)data;
|
||||
(void)reader;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static const struct Curl_crtype cr_eob = {
|
||||
"cr-smtp-eob",
|
||||
cr_eob_init,
|
||||
cr_eob_read,
|
||||
cr_eob_close,
|
||||
Curl_creader_def_needs_rewind,
|
||||
cr_eob_total_length,
|
||||
Curl_creader_def_resume_from,
|
||||
Curl_creader_def_rewind,
|
||||
Curl_creader_def_unpause,
|
||||
Curl_creader_def_done,
|
||||
sizeof(struct cr_eob_ctx)
|
||||
};
|
||||
|
||||
static CURLcode cr_eob_add(struct Curl_easy *data)
|
||||
{
|
||||
struct Curl_creader *reader = NULL;
|
||||
CURLcode result;
|
||||
|
||||
result = Curl_creader_create(&reader, data, &cr_eob,
|
||||
CURL_CR_CONTENT_ENCODE);
|
||||
if(!result)
|
||||
result = Curl_creader_add(data, reader);
|
||||
|
||||
if(result && reader)
|
||||
Curl_creader_free(data, reader);
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif /* CURL_DISABLE_SMTP */
|
||||
|
|
13
lib/smtp.h
13
lib/smtp.h
|
@ -84,17 +84,4 @@ struct smtp_conn {
|
|||
extern const struct Curl_handler Curl_handler_smtp;
|
||||
extern const struct Curl_handler Curl_handler_smtps;
|
||||
|
||||
/* this is the 5-bytes End-Of-Body marker for SMTP */
|
||||
#define SMTP_EOB "\x0d\x0a\x2e\x0d\x0a"
|
||||
#define SMTP_EOB_LEN 5
|
||||
#define SMTP_EOB_FIND_LEN 3
|
||||
|
||||
/* if found in data, replace it with this string instead */
|
||||
#define SMTP_EOB_REPL "\x0d\x0a\x2e\x2e"
|
||||
#define SMTP_EOB_REPL_LEN 4
|
||||
|
||||
CURLcode Curl_smtp_escape_eob(struct Curl_easy *data,
|
||||
const ssize_t nread,
|
||||
const ssize_t offset);
|
||||
|
||||
#endif /* HEADER_CURL_SMTP_H */
|
||||
|
|
|
@ -341,7 +341,7 @@ static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf,
|
|||
|
||||
case CONNECT_RESOLVING:
|
||||
/* check if we have the name resolved by now */
|
||||
dns = Curl_fetch_addr(data, sx->hostname, (int)conn->port);
|
||||
dns = Curl_fetch_addr(data, sx->hostname, conn->primary.remote_port);
|
||||
|
||||
if(dns) {
|
||||
#ifdef CURLRES_ASYNCH
|
||||
|
@ -1175,7 +1175,7 @@ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf,
|
|||
result = connect_SOCKS(cf, sx, data);
|
||||
if(!result && sx->state == CONNECT_DONE) {
|
||||
cf->connected = TRUE;
|
||||
Curl_verboseconnect(data, conn);
|
||||
Curl_verboseconnect(data, conn, cf->sockindex);
|
||||
socks_proxy_cf_free(cf);
|
||||
}
|
||||
|
||||
|
|
|
@ -475,7 +475,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
|
|||
gss_recv_token.length, &actualread);
|
||||
|
||||
if(result || (actualread != us_length)) {
|
||||
failf(data, "Failed to receive GSS-API encryptrion type.");
|
||||
failf(data, "Failed to receive GSS-API encryption type.");
|
||||
gss_release_buffer(&gss_status, &gss_recv_token);
|
||||
gss_delete_sec_context(&gss_status, &gss_context, NULL);
|
||||
return CURLE_COULDNT_CONNECT;
|
||||
|
|
|
@ -79,11 +79,10 @@ static int get_char(char c, int base);
|
|||
static curl_off_t strtooff(const char *nptr, char **endptr, int base)
|
||||
{
|
||||
char *end;
|
||||
int is_negative = 0;
|
||||
int overflow;
|
||||
bool is_negative = FALSE;
|
||||
bool overflow = FALSE;
|
||||
int i;
|
||||
curl_off_t value = 0;
|
||||
curl_off_t newval;
|
||||
|
||||
/* Skip leading whitespace. */
|
||||
end = (char *)nptr;
|
||||
|
@ -93,7 +92,7 @@ static curl_off_t strtooff(const char *nptr, char **endptr, int base)
|
|||
|
||||
/* Handle the sign, if any. */
|
||||
if(end[0] == '-') {
|
||||
is_negative = 1;
|
||||
is_negative = TRUE;
|
||||
end++;
|
||||
}
|
||||
else if(end[0] == '+') {
|
||||
|
@ -129,19 +128,15 @@ static curl_off_t strtooff(const char *nptr, char **endptr, int base)
|
|||
}
|
||||
|
||||
/* Loop handling digits. */
|
||||
value = 0;
|
||||
overflow = 0;
|
||||
for(i = get_char(end[0], base);
|
||||
i != -1;
|
||||
end++, i = get_char(end[0], base)) {
|
||||
newval = base * value + i;
|
||||
if(newval < value) {
|
||||
/* We've overflowed. */
|
||||
overflow = 1;
|
||||
|
||||
if(value > (CURL_OFF_T_MAX - i) / base) {
|
||||
overflow = TRUE;
|
||||
break;
|
||||
}
|
||||
else
|
||||
value = newval;
|
||||
value = base * value + i;
|
||||
}
|
||||
|
||||
if(!overflow) {
|
||||
|
@ -217,7 +212,7 @@ static int get_char(char c, int base)
|
|||
CURLofft curlx_strtoofft(const char *str, char **endp, int base,
|
||||
curl_off_t *num)
|
||||
{
|
||||
char *end;
|
||||
char *end = NULL;
|
||||
curl_off_t number;
|
||||
errno = 0;
|
||||
*num = 0; /* clear by default */
|
||||
|
|
22
lib/telnet.c
22
lib/telnet.c
|
@ -1231,20 +1231,24 @@ process_iac:
|
|||
static CURLcode send_telnet_data(struct Curl_easy *data,
|
||||
char *buffer, ssize_t nread)
|
||||
{
|
||||
ssize_t i, outlen;
|
||||
size_t i, outlen;
|
||||
unsigned char *outbuf;
|
||||
CURLcode result = CURLE_OK;
|
||||
ssize_t bytes_written, total_written = 0;
|
||||
size_t bytes_written;
|
||||
size_t total_written = 0;
|
||||
struct connectdata *conn = data->conn;
|
||||
struct TELNET *tn = data->req.p.telnet;
|
||||
|
||||
DEBUGASSERT(tn);
|
||||
DEBUGASSERT(nread > 0);
|
||||
if(nread < 0)
|
||||
return CURLE_TOO_LARGE;
|
||||
|
||||
if(memchr(buffer, CURL_IAC, nread)) {
|
||||
/* only use the escape buffer when necessary */
|
||||
Curl_dyn_reset(&tn->out);
|
||||
|
||||
for(i = 0; i < nread && !result; i++) {
|
||||
for(i = 0; i < (size_t)nread && !result; i++) {
|
||||
result = Curl_dyn_addn(&tn->out, &buffer[i], 1);
|
||||
if(!result && ((unsigned char)buffer[i] == CURL_IAC))
|
||||
/* IAC is FF in hex */
|
||||
|
@ -1255,7 +1259,7 @@ static CURLcode send_telnet_data(struct Curl_easy *data,
|
|||
outbuf = Curl_dyn_uptr(&tn->out);
|
||||
}
|
||||
else {
|
||||
outlen = nread;
|
||||
outlen = (size_t)nread;
|
||||
outbuf = (unsigned char *)buffer;
|
||||
}
|
||||
while(!result && total_written < outlen) {
|
||||
|
@ -1270,8 +1274,8 @@ static CURLcode send_telnet_data(struct Curl_easy *data,
|
|||
break;
|
||||
default: /* write! */
|
||||
bytes_written = 0;
|
||||
result = Curl_nwrite(data, FIRSTSOCKET, outbuf + total_written,
|
||||
outlen - total_written, &bytes_written);
|
||||
result = Curl_xfer_send(data, outbuf + total_written,
|
||||
outlen - total_written, &bytes_written);
|
||||
total_written += bytes_written;
|
||||
break;
|
||||
}
|
||||
|
@ -1464,7 +1468,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
|
|||
}
|
||||
if(events.lNetworkEvents & FD_READ) {
|
||||
/* read data from network */
|
||||
result = Curl_read(data, sockfd, buffer, sizeof(buffer), &nread);
|
||||
result = Curl_xfer_recv(data, buffer, sizeof(buffer), &nread);
|
||||
/* read would've blocked. Loop again */
|
||||
if(result == CURLE_AGAIN)
|
||||
break;
|
||||
|
@ -1545,7 +1549,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
|
|||
default: /* read! */
|
||||
if(pfd[0].revents & POLLIN) {
|
||||
/* read data from network */
|
||||
result = Curl_read(data, sockfd, buffer, sizeof(buffer), &nread);
|
||||
result = Curl_xfer_recv(data, buffer, sizeof(buffer), &nread);
|
||||
/* read would've blocked. Loop again */
|
||||
if(result == CURLE_AGAIN)
|
||||
break;
|
||||
|
@ -1635,7 +1639,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
|
|||
}
|
||||
#endif
|
||||
/* mark this as "no further transfer wanted" */
|
||||
Curl_setup_transfer(data, -1, -1, FALSE, -1);
|
||||
Curl_xfer_setup(data, -1, -1, FALSE, -1);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
15
lib/tftp.c
15
lib/tftp.c
|
@ -452,8 +452,6 @@ static CURLcode tftp_send_first(struct tftp_state_data *state,
|
|||
if(data->state.upload) {
|
||||
/* If we are uploading, send an WRQ */
|
||||
setpacketevent(&state->spacket, TFTP_EVENT_WRQ);
|
||||
state->data->req.upload_fromhere =
|
||||
(char *)state->spacket.data + 4;
|
||||
if(data->state.infilesize != -1)
|
||||
Curl_pgrsSetUploadSize(data, data->state.infilesize);
|
||||
}
|
||||
|
@ -708,6 +706,8 @@ static CURLcode tftp_tx(struct tftp_state_data *state, tftp_event_t event)
|
|||
struct SingleRequest *k = &data->req;
|
||||
size_t cb; /* Bytes currently read */
|
||||
char buffer[STRERROR_LEN];
|
||||
char *bufptr;
|
||||
bool eos;
|
||||
|
||||
switch(event) {
|
||||
|
||||
|
@ -771,13 +771,14 @@ static CURLcode tftp_tx(struct tftp_state_data *state, tftp_event_t event)
|
|||
* data block.
|
||||
* */
|
||||
state->sbytes = 0;
|
||||
state->data->req.upload_fromhere = (char *)state->spacket.data + 4;
|
||||
bufptr = (char *)state->spacket.data + 4;
|
||||
do {
|
||||
result = Curl_fillreadbuffer(data, state->blksize - state->sbytes, &cb);
|
||||
result = Curl_client_read(data, bufptr, state->blksize - state->sbytes,
|
||||
&cb, &eos);
|
||||
if(result)
|
||||
return result;
|
||||
state->sbytes += (int)cb;
|
||||
state->data->req.upload_fromhere += cb;
|
||||
bufptr += cb;
|
||||
} while(state->sbytes < state->blksize && cb);
|
||||
|
||||
sbytes = sendto(state->sockfd, (void *) state->spacket.data,
|
||||
|
@ -1240,7 +1241,7 @@ static CURLcode tftp_multi_statemach(struct Curl_easy *data, bool *done)
|
|||
*done = (state->state == TFTP_STATE_FIN) ? TRUE : FALSE;
|
||||
if(*done)
|
||||
/* Tell curl we're done */
|
||||
Curl_setup_transfer(data, -1, -1, FALSE, -1);
|
||||
Curl_xfer_setup(data, -1, -1, FALSE, -1);
|
||||
}
|
||||
else {
|
||||
/* no timeouts to handle, check our socket */
|
||||
|
@ -1263,7 +1264,7 @@ static CURLcode tftp_multi_statemach(struct Curl_easy *data, bool *done)
|
|||
*done = (state->state == TFTP_STATE_FIN) ? TRUE : FALSE;
|
||||
if(*done)
|
||||
/* Tell curl we're done */
|
||||
Curl_setup_transfer(data, -1, -1, FALSE, -1);
|
||||
Curl_xfer_setup(data, -1, -1, FALSE, -1);
|
||||
}
|
||||
/* if rc == 0, then select() timed out */
|
||||
}
|
||||
|
|
713
lib/transfer.c
713
lib/transfer.c
|
@ -63,6 +63,7 @@
|
|||
#include "content_encoding.h"
|
||||
#include "hostip.h"
|
||||
#include "cfilters.h"
|
||||
#include "cw-out.h"
|
||||
#include "transfer.h"
|
||||
#include "sendf.h"
|
||||
#include "speedcheck.h"
|
||||
|
@ -114,260 +115,6 @@ char *Curl_checkheaders(const struct Curl_easy *data,
|
|||
}
|
||||
#endif
|
||||
|
||||
CURLcode Curl_get_upload_buffer(struct Curl_easy *data)
|
||||
{
|
||||
if(!data->state.ulbuf) {
|
||||
data->state.ulbuf = malloc(data->set.upload_buffer_size);
|
||||
if(!data->state.ulbuf)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
#ifndef CURL_DISABLE_HTTP
|
||||
/*
|
||||
* This function will be called to loop through the trailers buffer
|
||||
* until no more data is available for sending.
|
||||
*/
|
||||
static size_t trailers_read(char *buffer, size_t size, size_t nitems,
|
||||
void *raw)
|
||||
{
|
||||
struct Curl_easy *data = (struct Curl_easy *)raw;
|
||||
struct dynbuf *trailers_buf = &data->state.trailers_buf;
|
||||
size_t bytes_left = Curl_dyn_len(trailers_buf) -
|
||||
data->state.trailers_bytes_sent;
|
||||
size_t to_copy = (size*nitems < bytes_left) ? size*nitems : bytes_left;
|
||||
if(to_copy) {
|
||||
memcpy(buffer,
|
||||
Curl_dyn_ptr(trailers_buf) + data->state.trailers_bytes_sent,
|
||||
to_copy);
|
||||
data->state.trailers_bytes_sent += to_copy;
|
||||
}
|
||||
return to_copy;
|
||||
}
|
||||
|
||||
static size_t trailers_left(void *raw)
|
||||
{
|
||||
struct Curl_easy *data = (struct Curl_easy *)raw;
|
||||
struct dynbuf *trailers_buf = &data->state.trailers_buf;
|
||||
return Curl_dyn_len(trailers_buf) - data->state.trailers_bytes_sent;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This function will call the read callback to fill our buffer with data
|
||||
* to upload.
|
||||
*/
|
||||
CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes,
|
||||
size_t *nreadp)
|
||||
{
|
||||
size_t buffersize = bytes;
|
||||
size_t nread;
|
||||
curl_read_callback readfunc = NULL;
|
||||
void *extra_data = NULL;
|
||||
int eof_index = 0;
|
||||
|
||||
#ifndef CURL_DISABLE_HTTP
|
||||
if(data->state.trailers_state == TRAILERS_INITIALIZED) {
|
||||
struct curl_slist *trailers = NULL;
|
||||
CURLcode result;
|
||||
int trailers_ret_code;
|
||||
|
||||
/* at this point we already verified that the callback exists
|
||||
so we compile and store the trailers buffer, then proceed */
|
||||
infof(data,
|
||||
"Moving trailers state machine from initialized to sending.");
|
||||
data->state.trailers_state = TRAILERS_SENDING;
|
||||
Curl_dyn_init(&data->state.trailers_buf, DYN_TRAILERS);
|
||||
|
||||
data->state.trailers_bytes_sent = 0;
|
||||
Curl_set_in_callback(data, true);
|
||||
trailers_ret_code = data->set.trailer_callback(&trailers,
|
||||
data->set.trailer_data);
|
||||
Curl_set_in_callback(data, false);
|
||||
if(trailers_ret_code == CURL_TRAILERFUNC_OK) {
|
||||
result = Curl_http_compile_trailers(trailers, &data->state.trailers_buf,
|
||||
data);
|
||||
}
|
||||
else {
|
||||
failf(data, "operation aborted by trailing headers callback");
|
||||
*nreadp = 0;
|
||||
result = CURLE_ABORTED_BY_CALLBACK;
|
||||
}
|
||||
if(result) {
|
||||
Curl_dyn_free(&data->state.trailers_buf);
|
||||
curl_slist_free_all(trailers);
|
||||
return result;
|
||||
}
|
||||
infof(data, "Successfully compiled trailers.");
|
||||
curl_slist_free_all(trailers);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef CURL_DISABLE_HTTP
|
||||
/* if we are transmitting trailing data, we don't need to write
|
||||
a chunk size so we skip this */
|
||||
if(data->req.upload_chunky &&
|
||||
data->state.trailers_state == TRAILERS_NONE) {
|
||||
/* if chunked Transfer-Encoding */
|
||||
buffersize -= (8 + 2 + 2); /* 32bit hex + CRLF + CRLF */
|
||||
data->req.upload_fromhere += (8 + 2); /* 32bit hex + CRLF */
|
||||
}
|
||||
|
||||
if(data->state.trailers_state == TRAILERS_SENDING) {
|
||||
/* if we're here then that means that we already sent the last empty chunk
|
||||
but we didn't send a final CR LF, so we sent 0 CR LF. We then start
|
||||
pulling trailing data until we have no more at which point we
|
||||
simply return to the previous point in the state machine as if
|
||||
nothing happened.
|
||||
*/
|
||||
readfunc = trailers_read;
|
||||
extra_data = (void *)data;
|
||||
eof_index = 1;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
readfunc = data->state.fread_func;
|
||||
extra_data = data->state.in;
|
||||
}
|
||||
|
||||
if(!data->req.fread_eof[eof_index]) {
|
||||
Curl_set_in_callback(data, true);
|
||||
nread = readfunc(data->req.upload_fromhere, 1, buffersize, extra_data);
|
||||
Curl_set_in_callback(data, false);
|
||||
/* make sure the callback is not called again after EOF */
|
||||
data->req.fread_eof[eof_index] = !nread;
|
||||
}
|
||||
else
|
||||
nread = 0;
|
||||
|
||||
if(nread == CURL_READFUNC_ABORT) {
|
||||
failf(data, "operation aborted by callback");
|
||||
*nreadp = 0;
|
||||
return CURLE_ABORTED_BY_CALLBACK;
|
||||
}
|
||||
if(nread == CURL_READFUNC_PAUSE) {
|
||||
struct SingleRequest *k = &data->req;
|
||||
|
||||
if(data->conn->handler->flags & PROTOPT_NONETWORK) {
|
||||
/* protocols that work without network cannot be paused. This is
|
||||
actually only FILE:// just now, and it can't pause since the transfer
|
||||
isn't done using the "normal" procedure. */
|
||||
failf(data, "Read callback asked for PAUSE when not supported");
|
||||
return CURLE_READ_ERROR;
|
||||
}
|
||||
|
||||
/* CURL_READFUNC_PAUSE pauses read callbacks that feed socket writes */
|
||||
k->keepon |= KEEP_SEND_PAUSE; /* mark socket send as paused */
|
||||
if(data->req.upload_chunky) {
|
||||
/* Back out the preallocation done above */
|
||||
data->req.upload_fromhere -= (8 + 2);
|
||||
}
|
||||
*nreadp = 0;
|
||||
|
||||
return CURLE_OK; /* nothing was read */
|
||||
}
|
||||
else if(nread > buffersize) {
|
||||
/* the read function returned a too large value */
|
||||
*nreadp = 0;
|
||||
failf(data, "read function returned funny value");
|
||||
return CURLE_READ_ERROR;
|
||||
}
|
||||
|
||||
#ifndef CURL_DISABLE_HTTP
|
||||
if(!data->req.forbidchunk && data->req.upload_chunky) {
|
||||
/* if chunked Transfer-Encoding
|
||||
* build chunk:
|
||||
*
|
||||
* <HEX SIZE> CRLF
|
||||
* <DATA> CRLF
|
||||
*/
|
||||
/* On non-ASCII platforms the <DATA> may or may not be
|
||||
translated based on state.prefer_ascii while the protocol
|
||||
portion must always be translated to the network encoding.
|
||||
To further complicate matters, line end conversion might be
|
||||
done later on, so we need to prevent CRLFs from becoming
|
||||
CRCRLFs if that's the case. To do this we use bare LFs
|
||||
here, knowing they'll become CRLFs later on.
|
||||
*/
|
||||
|
||||
bool added_crlf = FALSE;
|
||||
int hexlen = 0;
|
||||
const char *endofline_native;
|
||||
const char *endofline_network;
|
||||
|
||||
if(
|
||||
#ifdef CURL_DO_LINEEND_CONV
|
||||
(data->state.prefer_ascii) ||
|
||||
#endif
|
||||
(data->set.crlf)) {
|
||||
/* \n will become \r\n later on */
|
||||
endofline_native = "\n";
|
||||
endofline_network = "\x0a";
|
||||
}
|
||||
else {
|
||||
endofline_native = "\r\n";
|
||||
endofline_network = "\x0d\x0a";
|
||||
}
|
||||
|
||||
/* if we're not handling trailing data, proceed as usual */
|
||||
if(data->state.trailers_state != TRAILERS_SENDING) {
|
||||
char hexbuffer[11] = "";
|
||||
hexlen = msnprintf(hexbuffer, sizeof(hexbuffer),
|
||||
"%zx%s", nread, endofline_native);
|
||||
|
||||
/* move buffer pointer */
|
||||
data->req.upload_fromhere -= hexlen;
|
||||
nread += hexlen;
|
||||
|
||||
/* copy the prefix to the buffer, leaving out the NUL */
|
||||
memcpy(data->req.upload_fromhere, hexbuffer, hexlen);
|
||||
|
||||
/* always append ASCII CRLF to the data unless
|
||||
we have a valid trailer callback */
|
||||
if((nread-hexlen) == 0 &&
|
||||
data->set.trailer_callback != NULL &&
|
||||
data->state.trailers_state == TRAILERS_NONE) {
|
||||
data->state.trailers_state = TRAILERS_INITIALIZED;
|
||||
}
|
||||
else {
|
||||
memcpy(data->req.upload_fromhere + nread,
|
||||
endofline_network,
|
||||
strlen(endofline_network));
|
||||
added_crlf = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if(data->state.trailers_state == TRAILERS_SENDING &&
|
||||
!trailers_left(data)) {
|
||||
Curl_dyn_free(&data->state.trailers_buf);
|
||||
data->state.trailers_state = TRAILERS_DONE;
|
||||
data->set.trailer_data = NULL;
|
||||
data->set.trailer_callback = NULL;
|
||||
/* mark the transfer as done */
|
||||
data->req.upload_done = TRUE;
|
||||
infof(data, "Signaling end of chunked upload after trailers.");
|
||||
}
|
||||
else
|
||||
if((nread - hexlen) == 0 &&
|
||||
data->state.trailers_state != TRAILERS_INITIALIZED) {
|
||||
/* mark this as done once this chunk is transferred */
|
||||
data->req.upload_done = TRUE;
|
||||
infof(data,
|
||||
"Signaling end of chunked upload via terminating chunk.");
|
||||
}
|
||||
|
||||
if(added_crlf)
|
||||
nread += strlen(endofline_network); /* for the added end of line */
|
||||
}
|
||||
#endif
|
||||
|
||||
*nreadp = nread;
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static int data_pending(struct Curl_easy *data)
|
||||
{
|
||||
struct connectdata *conn = data->conn;
|
||||
|
@ -447,7 +194,7 @@ static ssize_t Curl_xfer_recv_resp(struct Curl_easy *data,
|
|||
return 0;
|
||||
}
|
||||
|
||||
*err = Curl_read(data, data->conn->sockfd, buf, blen, &nread);
|
||||
*err = Curl_xfer_recv(data, buf, blen, &nread);
|
||||
if(*err)
|
||||
return -1;
|
||||
DEBUGASSERT(nread >= 0);
|
||||
|
@ -462,18 +209,19 @@ static ssize_t Curl_xfer_recv_resp(struct Curl_easy *data,
|
|||
*/
|
||||
static CURLcode readwrite_data(struct Curl_easy *data,
|
||||
struct SingleRequest *k,
|
||||
int *didwhat, bool *done)
|
||||
int *didwhat)
|
||||
{
|
||||
struct connectdata *conn = data->conn;
|
||||
CURLcode result = CURLE_OK;
|
||||
char *buf;
|
||||
size_t blen;
|
||||
char *buf, *xfer_buf;
|
||||
size_t blen, xfer_blen;
|
||||
int maxloops = 10;
|
||||
curl_off_t total_received = 0;
|
||||
bool is_multiplex = FALSE;
|
||||
|
||||
DEBUGASSERT(data->state.buffer);
|
||||
*done = FALSE;
|
||||
result = Curl_multi_xfer_buf_borrow(data, &xfer_buf, &xfer_blen);
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
/* This is where we loop until we have read everything there is to
|
||||
read or we get a CURLE_AGAIN */
|
||||
|
@ -489,16 +237,17 @@ static CURLcode readwrite_data(struct Curl_easy *data,
|
|||
is_multiplex = Curl_conn_is_multiplex(conn, FIRSTSOCKET);
|
||||
}
|
||||
|
||||
buf = data->state.buffer;
|
||||
bytestoread = data->set.buffer_size;
|
||||
buf = xfer_buf;
|
||||
bytestoread = xfer_blen;
|
||||
|
||||
/* Observe any imposed speed limit */
|
||||
if(bytestoread && data->set.max_recv_speed) {
|
||||
curl_off_t net_limit = data->set.max_recv_speed - total_received;
|
||||
if(net_limit <= 0)
|
||||
/* In case of speed limit on receiving: if this loop already got
|
||||
* data, break out. If not, limit the amount of bytes to receive.
|
||||
* The overall, timed, speed limiting is done in multi.c */
|
||||
if(total_received)
|
||||
break;
|
||||
if((size_t)net_limit < bytestoread)
|
||||
bytestoread = (size_t)net_limit;
|
||||
if((size_t)data->set.max_recv_speed < bytestoread)
|
||||
bytestoread = (size_t)data->set.max_recv_speed;
|
||||
}
|
||||
|
||||
nread = Curl_xfer_recv_resp(data, buf, bytestoread,
|
||||
|
@ -530,8 +279,8 @@ static CURLcode readwrite_data(struct Curl_easy *data,
|
|||
}
|
||||
total_received += blen;
|
||||
|
||||
result = Curl_xfer_write_resp(data, buf, blen, is_eos, done);
|
||||
if(result || *done)
|
||||
result = Curl_xfer_write_resp(data, buf, blen, is_eos);
|
||||
if(result || data->req.done)
|
||||
goto out;
|
||||
|
||||
/* if we are done, we stop receiving. On multiplexed connections,
|
||||
|
@ -564,22 +313,12 @@ static CURLcode readwrite_data(struct Curl_easy *data,
|
|||
}
|
||||
|
||||
out:
|
||||
Curl_multi_xfer_buf_release(data, xfer_buf);
|
||||
if(result)
|
||||
DEBUGF(infof(data, "readwrite_data() -> %d", result));
|
||||
return result;
|
||||
}
|
||||
|
||||
CURLcode Curl_done_sending(struct Curl_easy *data,
|
||||
struct SingleRequest *k)
|
||||
{
|
||||
k->keepon &= ~KEEP_SEND; /* we're done writing */
|
||||
|
||||
/* These functions should be moved into the handler struct! */
|
||||
Curl_conn_ev_data_done_send(data);
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
#if defined(_WIN32) && defined(USE_WINSOCK)
|
||||
#ifndef SIO_IDEAL_SEND_BACKLOG_QUERY
|
||||
#define SIO_IDEAL_SEND_BACKLOG_QUERY 0x4004747B
|
||||
|
@ -602,245 +341,42 @@ static void win_update_buffer_size(curl_socket_t sockfd)
|
|||
#endif
|
||||
|
||||
#define curl_upload_refill_watermark(data) \
|
||||
((ssize_t)((data)->set.upload_buffer_size >> 5))
|
||||
((size_t)((data)->set.upload_buffer_size >> 5))
|
||||
|
||||
/*
|
||||
* Send data to upload to the server, when the socket is writable.
|
||||
*/
|
||||
static CURLcode readwrite_upload(struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
int *didwhat)
|
||||
static CURLcode readwrite_upload(struct Curl_easy *data, int *didwhat)
|
||||
{
|
||||
ssize_t i, si;
|
||||
ssize_t bytes_written;
|
||||
CURLcode result;
|
||||
ssize_t nread; /* number of bytes read */
|
||||
bool sending_http_headers = FALSE;
|
||||
struct SingleRequest *k = &data->req;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
*didwhat |= KEEP_SEND;
|
||||
if((data->req.keepon & KEEP_SEND_PAUSE))
|
||||
return CURLE_OK;
|
||||
|
||||
do {
|
||||
curl_off_t nbody;
|
||||
ssize_t offset = 0;
|
||||
/* We should not get here when the sending is already done. It
|
||||
* probably means that someone set `data-req.keepon |= KEEP_SEND`
|
||||
* when it should not. */
|
||||
DEBUGASSERT(!Curl_req_done_sending(data));
|
||||
|
||||
if(0 != k->upload_present &&
|
||||
k->upload_present < curl_upload_refill_watermark(data) &&
|
||||
!k->upload_chunky &&/*(variable sized chunked header; append not safe)*/
|
||||
!k->upload_done && /*!(k->upload_done once k->upload_present sent)*/
|
||||
!(k->writebytecount + k->upload_present - k->pendingheader ==
|
||||
data->state.infilesize)) {
|
||||
offset = k->upload_present;
|
||||
}
|
||||
|
||||
/* only read more data if there's no upload data already
|
||||
present in the upload buffer, or if appending to upload buffer */
|
||||
if(0 == k->upload_present || offset) {
|
||||
result = Curl_get_upload_buffer(data);
|
||||
if(result)
|
||||
return result;
|
||||
if(offset && k->upload_fromhere != data->state.ulbuf)
|
||||
memmove(data->state.ulbuf, k->upload_fromhere, offset);
|
||||
/* init the "upload from here" pointer */
|
||||
k->upload_fromhere = data->state.ulbuf;
|
||||
|
||||
if(!k->upload_done) {
|
||||
/* HTTP pollution, this should be written nicer to become more
|
||||
protocol agnostic. */
|
||||
size_t fillcount;
|
||||
struct HTTP *http = k->p.http;
|
||||
|
||||
if((k->exp100 == EXP100_SENDING_REQUEST) &&
|
||||
(http->sending == HTTPSEND_BODY)) {
|
||||
/* If this call is to send body data, we must take some action:
|
||||
We have sent off the full HTTP 1.1 request, and we shall now
|
||||
go into the Expect: 100 state and await such a header */
|
||||
k->exp100 = EXP100_AWAITING_CONTINUE; /* wait for the header */
|
||||
k->keepon &= ~KEEP_SEND; /* disable writing */
|
||||
k->start100 = Curl_now(); /* timeout count starts now */
|
||||
*didwhat &= ~KEEP_SEND; /* we didn't write anything actually */
|
||||
/* set a timeout for the multi interface */
|
||||
Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT);
|
||||
break;
|
||||
}
|
||||
|
||||
if(conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_RTSP)) {
|
||||
if(http->sending == HTTPSEND_REQUEST)
|
||||
/* We're sending the HTTP request headers, not the data.
|
||||
Remember that so we don't change the line endings. */
|
||||
sending_http_headers = TRUE;
|
||||
else
|
||||
sending_http_headers = FALSE;
|
||||
}
|
||||
|
||||
k->upload_fromhere += offset;
|
||||
result = Curl_fillreadbuffer(data, data->set.upload_buffer_size-offset,
|
||||
&fillcount);
|
||||
k->upload_fromhere -= offset;
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
nread = offset + fillcount;
|
||||
}
|
||||
else
|
||||
nread = 0; /* we're done uploading/reading */
|
||||
|
||||
if(!nread && (k->keepon & KEEP_SEND_PAUSE)) {
|
||||
/* this is a paused transfer */
|
||||
break;
|
||||
}
|
||||
if(nread <= 0) {
|
||||
result = Curl_done_sending(data, k);
|
||||
if(result)
|
||||
return result;
|
||||
break;
|
||||
}
|
||||
|
||||
/* store number of bytes available for upload */
|
||||
k->upload_present = nread;
|
||||
|
||||
/* convert LF to CRLF if so asked */
|
||||
if((!sending_http_headers) && (
|
||||
#ifdef CURL_DO_LINEEND_CONV
|
||||
/* always convert if we're FTPing in ASCII mode */
|
||||
(data->state.prefer_ascii) ||
|
||||
#endif
|
||||
(data->set.crlf))) {
|
||||
/* Do we need to allocate a scratch buffer? */
|
||||
if(!data->state.scratch) {
|
||||
data->state.scratch = malloc(2 * data->set.upload_buffer_size);
|
||||
if(!data->state.scratch) {
|
||||
failf(data, "Failed to alloc scratch buffer");
|
||||
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ASCII/EBCDIC Note: This is presumably a text (not binary)
|
||||
* transfer so the data should already be in ASCII.
|
||||
* That means the hex values for ASCII CR (0x0d) & LF (0x0a)
|
||||
* must be used instead of the escape sequences \r & \n.
|
||||
*/
|
||||
if(offset)
|
||||
memcpy(data->state.scratch, k->upload_fromhere, offset);
|
||||
for(i = offset, si = offset; i < nread; i++, si++) {
|
||||
if(k->upload_fromhere[i] == 0x0a) {
|
||||
data->state.scratch[si++] = 0x0d;
|
||||
data->state.scratch[si] = 0x0a;
|
||||
if(!data->set.crlf) {
|
||||
/* we're here only because FTP is in ASCII mode...
|
||||
bump infilesize for the LF we just added */
|
||||
if(data->state.infilesize != -1)
|
||||
data->state.infilesize++;
|
||||
}
|
||||
}
|
||||
else
|
||||
data->state.scratch[si] = k->upload_fromhere[i];
|
||||
}
|
||||
|
||||
if(si != nread) {
|
||||
/* only perform the special operation if we really did replace
|
||||
anything */
|
||||
nread = si;
|
||||
|
||||
/* upload from the new (replaced) buffer instead */
|
||||
k->upload_fromhere = data->state.scratch;
|
||||
|
||||
/* set the new amount too */
|
||||
k->upload_present = nread;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef CURL_DISABLE_SMTP
|
||||
if(conn->handler->protocol & PROTO_FAMILY_SMTP) {
|
||||
result = Curl_smtp_escape_eob(data, nread, offset);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
#endif /* CURL_DISABLE_SMTP */
|
||||
} /* if 0 == k->upload_present or appended to upload buffer */
|
||||
else {
|
||||
/* We have a partial buffer left from a previous "round". Use
|
||||
that instead of reading more data */
|
||||
}
|
||||
|
||||
/* write to socket (send away data) */
|
||||
result = Curl_write(data,
|
||||
conn->writesockfd, /* socket to send to */
|
||||
k->upload_fromhere, /* buffer pointer */
|
||||
k->upload_present, /* buffer size */
|
||||
&bytes_written); /* actually sent */
|
||||
if(!Curl_req_done_sending(data)) {
|
||||
*didwhat |= KEEP_SEND;
|
||||
result = Curl_req_send_more(data);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
#if defined(_WIN32) && defined(USE_WINSOCK)
|
||||
/* FIXME: this looks like it would fit better into cf-socket.c
|
||||
* but then I do not know enough Windows to say... */
|
||||
{
|
||||
struct curltime n = Curl_now();
|
||||
if(Curl_timediff(n, k->last_sndbuf_update) > 1000) {
|
||||
win_update_buffer_size(conn->writesockfd);
|
||||
k->last_sndbuf_update = n;
|
||||
if(Curl_timediff(n, data->conn->last_sndbuf_update) > 1000) {
|
||||
win_update_buffer_size(data->conn->writesockfd);
|
||||
data->conn->last_sndbuf_update = n;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if(k->pendingheader) {
|
||||
/* parts of what was sent was header */
|
||||
curl_off_t n = CURLMIN(k->pendingheader, bytes_written);
|
||||
/* show the data before we change the pointer upload_fromhere */
|
||||
Curl_debug(data, CURLINFO_HEADER_OUT, k->upload_fromhere, (size_t)n);
|
||||
k->pendingheader -= n;
|
||||
nbody = bytes_written - n; /* size of the written body part */
|
||||
}
|
||||
else
|
||||
nbody = bytes_written;
|
||||
|
||||
if(nbody) {
|
||||
/* show the data before we change the pointer upload_fromhere */
|
||||
Curl_debug(data, CURLINFO_DATA_OUT,
|
||||
&k->upload_fromhere[bytes_written - nbody],
|
||||
(size_t)nbody);
|
||||
|
||||
k->writebytecount += nbody;
|
||||
Curl_pgrsSetUploadCounter(data, k->writebytecount);
|
||||
}
|
||||
|
||||
if((!k->upload_chunky || k->forbidchunk) &&
|
||||
(k->writebytecount == data->state.infilesize)) {
|
||||
/* we have sent all data we were supposed to */
|
||||
k->upload_done = TRUE;
|
||||
infof(data, "We are completely uploaded and fine");
|
||||
}
|
||||
|
||||
if(k->upload_present != bytes_written) {
|
||||
/* we only wrote a part of the buffer (if anything), deal with it! */
|
||||
|
||||
/* store the amount of bytes left in the buffer to write */
|
||||
k->upload_present -= bytes_written;
|
||||
|
||||
/* advance the pointer where to find the buffer when the next send
|
||||
is to happen */
|
||||
k->upload_fromhere += bytes_written;
|
||||
}
|
||||
else {
|
||||
/* we've uploaded that buffer now */
|
||||
result = Curl_get_upload_buffer(data);
|
||||
if(result)
|
||||
return result;
|
||||
k->upload_fromhere = data->state.ulbuf;
|
||||
k->upload_present = 0; /* no more bytes left */
|
||||
|
||||
if(k->upload_done) {
|
||||
result = Curl_done_sending(data, k);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} while(0); /* just to break out from! */
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int select_bits_paused(struct Curl_easy *data, int select_bits)
|
||||
|
@ -865,8 +401,7 @@ static int select_bits_paused(struct Curl_easy *data, int select_bits)
|
|||
* Curl_readwrite() is the low-level function to be called when data is to
|
||||
* be read and written to/from the connection.
|
||||
*/
|
||||
CURLcode Curl_readwrite(struct Curl_easy *data,
|
||||
bool *done)
|
||||
CURLcode Curl_readwrite(struct Curl_easy *data)
|
||||
{
|
||||
struct connectdata *conn = data->conn;
|
||||
struct SingleRequest *k = &data->req;
|
||||
|
@ -912,8 +447,8 @@ CURLcode Curl_readwrite(struct Curl_easy *data,
|
|||
|
||||
#ifdef USE_HYPER
|
||||
if(conn->datastream) {
|
||||
result = conn->datastream(data, conn, &didwhat, done, select_bits);
|
||||
if(result || *done)
|
||||
result = conn->datastream(data, conn, &didwhat, select_bits);
|
||||
if(result || data->req.done)
|
||||
goto out;
|
||||
}
|
||||
else {
|
||||
|
@ -922,16 +457,17 @@ CURLcode Curl_readwrite(struct Curl_easy *data,
|
|||
the stream was rewound (in which case we have data in a
|
||||
buffer) */
|
||||
if((k->keepon & KEEP_RECV) && (select_bits & CURL_CSELECT_IN)) {
|
||||
result = readwrite_data(data, k, &didwhat, done);
|
||||
if(result || *done)
|
||||
result = readwrite_data(data, k, &didwhat);
|
||||
if(result || data->req.done)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* If we still have writing to do, we check if we have a writable socket. */
|
||||
if((k->keepon & KEEP_SEND) && (select_bits & CURL_CSELECT_OUT)) {
|
||||
if(((k->keepon & KEEP_SEND) && (select_bits & CURL_CSELECT_OUT)) ||
|
||||
(k->keepon & KEEP_SEND_TIMED)) {
|
||||
/* write */
|
||||
|
||||
result = readwrite_upload(data, conn, &didwhat);
|
||||
result = readwrite_upload(data, &didwhat);
|
||||
if(result)
|
||||
goto out;
|
||||
}
|
||||
|
@ -941,31 +477,6 @@ CURLcode Curl_readwrite(struct Curl_easy *data,
|
|||
|
||||
now = Curl_now();
|
||||
if(!didwhat) {
|
||||
/* no read no write, this is a timeout? */
|
||||
if(k->exp100 == EXP100_AWAITING_CONTINUE) {
|
||||
/* This should allow some time for the header to arrive, but only a
|
||||
very short time as otherwise it'll be too much wasted time too
|
||||
often. */
|
||||
|
||||
/* Quoting RFC2616, section "8.2.3 Use of the 100 (Continue) Status":
|
||||
|
||||
Therefore, when a client sends this header field to an origin server
|
||||
(possibly via a proxy) from which it has never seen a 100 (Continue)
|
||||
status, the client SHOULD NOT wait for an indefinite period before
|
||||
sending the request body.
|
||||
|
||||
*/
|
||||
|
||||
timediff_t ms = Curl_timediff(now, k->start100);
|
||||
if(ms >= data->set.expect_100_timeout) {
|
||||
/* we've waited long enough, continue anyway */
|
||||
k->exp100 = EXP100_SEND_DATA;
|
||||
k->keepon |= KEEP_SEND;
|
||||
Curl_expire_done(data, EXPIRE_100_TIMEOUT);
|
||||
infof(data, "Done waiting for 100-continue");
|
||||
}
|
||||
}
|
||||
|
||||
result = Curl_conn_ev_data_idle(data);
|
||||
if(result)
|
||||
goto out;
|
||||
|
@ -1002,7 +513,6 @@ CURLcode Curl_readwrite(struct Curl_easy *data,
|
|||
* The transfer has been performed. Just make some general checks before
|
||||
* returning.
|
||||
*/
|
||||
|
||||
if(!(data->req.no_body) && (k->size != -1) &&
|
||||
(k->bytecount != k->size) &&
|
||||
#ifdef CURL_DO_LINEEND_CONV
|
||||
|
@ -1024,8 +534,10 @@ CURLcode Curl_readwrite(struct Curl_easy *data,
|
|||
}
|
||||
}
|
||||
|
||||
/* Now update the "done" boolean we return */
|
||||
*done = (0 == (k->keepon&(KEEP_RECVBITS|KEEP_SENDBITS))) ? TRUE : FALSE;
|
||||
/* If there is nothing more to send/recv, the request is done */
|
||||
if(0 == (k->keepon&(KEEP_RECVBITS|KEEP_SENDBITS)))
|
||||
data->req.done = TRUE;
|
||||
|
||||
out:
|
||||
if(result)
|
||||
DEBUGF(infof(data, "Curl_readwrite() -> %d", result));
|
||||
|
@ -1400,7 +912,7 @@ CURLcode Curl_follow(struct Curl_easy *data,
|
|||
|
||||
data->state.url = newurl;
|
||||
data->state.url_alloc = TRUE;
|
||||
|
||||
Curl_req_soft_reset(&data->req, data);
|
||||
infof(data, "Issue another request to this URL: '%s'", data->state.url);
|
||||
|
||||
/*
|
||||
|
@ -1446,6 +958,7 @@ CURLcode Curl_follow(struct Curl_easy *data,
|
|||
&& !(data->set.keep_post & CURL_REDIR_POST_301)) {
|
||||
infof(data, "Switch from POST to GET");
|
||||
data->state.httpreq = HTTPREQ_GET;
|
||||
Curl_creader_set_rewind(data, FALSE);
|
||||
}
|
||||
break;
|
||||
case 302: /* Found */
|
||||
|
@ -1471,6 +984,7 @@ CURLcode Curl_follow(struct Curl_easy *data,
|
|||
&& !(data->set.keep_post & CURL_REDIR_POST_302)) {
|
||||
infof(data, "Switch from POST to GET");
|
||||
data->state.httpreq = HTTPREQ_GET;
|
||||
Curl_creader_set_rewind(data, FALSE);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1573,23 +1087,16 @@ CURLcode Curl_retry_request(struct Curl_easy *data, char **url)
|
|||
prevent i.e HTTP transfers to return
|
||||
error just because nothing has been
|
||||
transferred! */
|
||||
|
||||
|
||||
if((conn->handler->protocol&PROTO_FAMILY_HTTP) &&
|
||||
data->req.writebytecount) {
|
||||
data->state.rewindbeforesend = TRUE;
|
||||
infof(data, "state.rewindbeforesend = TRUE");
|
||||
}
|
||||
Curl_creader_set_rewind(data, TRUE);
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Curl_setup_transfer() is called to setup some basic properties for the
|
||||
* Curl_xfer_setup() is called to setup some basic properties for the
|
||||
* upcoming transfer.
|
||||
*/
|
||||
void
|
||||
Curl_setup_transfer(
|
||||
void Curl_xfer_setup(
|
||||
struct Curl_easy *data, /* transfer */
|
||||
int sockindex, /* socket index to read from or -1 */
|
||||
curl_off_t size, /* -1 if unknown at this point */
|
||||
|
@ -1600,22 +1107,19 @@ Curl_setup_transfer(
|
|||
{
|
||||
struct SingleRequest *k = &data->req;
|
||||
struct connectdata *conn = data->conn;
|
||||
struct HTTP *http = data->req.p.http;
|
||||
bool httpsending;
|
||||
bool want_send = Curl_req_want_send(data);
|
||||
|
||||
DEBUGASSERT(conn != NULL);
|
||||
DEBUGASSERT((sockindex <= 1) && (sockindex >= -1));
|
||||
DEBUGASSERT((writesockindex <= 1) && (writesockindex >= -1));
|
||||
|
||||
httpsending = ((conn->handler->protocol&PROTO_FAMILY_HTTP) &&
|
||||
(http->sending == HTTPSEND_REQUEST));
|
||||
|
||||
if(conn->bits.multiplex || conn->httpversion >= 20 || httpsending) {
|
||||
if(conn->bits.multiplex || conn->httpversion >= 20 || want_send) {
|
||||
/* when multiplexing, the read/write sockets need to be the same! */
|
||||
conn->sockfd = sockindex == -1 ?
|
||||
((writesockindex == -1 ? CURL_SOCKET_BAD : conn->sock[writesockindex])) :
|
||||
conn->sock[sockindex];
|
||||
conn->writesockfd = conn->sockfd;
|
||||
if(httpsending)
|
||||
if(want_send)
|
||||
/* special and very HTTP-specific */
|
||||
writesockindex = FIRSTSOCKET;
|
||||
}
|
||||
|
@ -1644,51 +1148,22 @@ Curl_setup_transfer(
|
|||
if(sockindex != -1)
|
||||
k->keepon |= KEEP_RECV;
|
||||
|
||||
if(writesockindex != -1) {
|
||||
/* HTTP 1.1 magic:
|
||||
|
||||
Even if we require a 100-return code before uploading data, we might
|
||||
need to write data before that since the REQUEST may not have been
|
||||
finished sent off just yet.
|
||||
|
||||
Thus, we must check if the request has been sent before we set the
|
||||
state info where we wait for the 100-return code
|
||||
*/
|
||||
if((data->state.expect100header) &&
|
||||
(conn->handler->protocol&PROTO_FAMILY_HTTP) &&
|
||||
(http->sending == HTTPSEND_BODY)) {
|
||||
/* wait with write until we either got 100-continue or a timeout */
|
||||
k->exp100 = EXP100_AWAITING_CONTINUE;
|
||||
k->start100 = Curl_now();
|
||||
|
||||
/* Set a timeout for the multi interface. Add the inaccuracy margin so
|
||||
that we don't fire slightly too early and get denied to run. */
|
||||
Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT);
|
||||
}
|
||||
else {
|
||||
if(data->state.expect100header)
|
||||
/* when we've sent off the rest of the headers, we must await a
|
||||
100-continue but first finish sending the request */
|
||||
k->exp100 = EXP100_SENDING_REQUEST;
|
||||
|
||||
/* enable the write bit when we're not waiting for continue */
|
||||
k->keepon |= KEEP_SEND;
|
||||
}
|
||||
} /* if(writesockindex != -1) */
|
||||
if(writesockindex != -1)
|
||||
k->keepon |= KEEP_SEND;
|
||||
} /* if(k->getheader || !data->req.no_body) */
|
||||
|
||||
}
|
||||
|
||||
CURLcode Curl_xfer_write_resp(struct Curl_easy *data,
|
||||
char *buf, size_t blen,
|
||||
bool is_eos, bool *done)
|
||||
bool is_eos)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
if(data->conn->handler->write_resp) {
|
||||
/* protocol handlers offering this function take full responsibility
|
||||
* for writing all received download data to the client. */
|
||||
result = data->conn->handler->write_resp(data, buf, blen, is_eos, done);
|
||||
result = data->conn->handler->write_resp(data, buf, blen, is_eos);
|
||||
}
|
||||
else {
|
||||
/* No special handling by protocol handler, write all received data
|
||||
|
@ -1716,3 +1191,63 @@ CURLcode Curl_xfer_write_resp(struct Curl_easy *data,
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
CURLcode Curl_xfer_write_done(struct Curl_easy *data, bool premature)
|
||||
{
|
||||
(void)premature;
|
||||
return Curl_cw_out_done(data);
|
||||
}
|
||||
|
||||
CURLcode Curl_xfer_send(struct Curl_easy *data,
|
||||
const void *buf, size_t blen,
|
||||
size_t *pnwritten)
|
||||
{
|
||||
CURLcode result;
|
||||
int sockindex;
|
||||
|
||||
if(!data || !data->conn)
|
||||
return CURLE_FAILED_INIT;
|
||||
/* FIXME: would like to enable this, but some protocols (MQTT) do not
|
||||
* setup the transfer correctly, it seems
|
||||
if(data->conn->writesockfd == CURL_SOCKET_BAD) {
|
||||
failf(data, "transfer not setup for sending");
|
||||
DEBUGASSERT(0);
|
||||
return CURLE_SEND_ERROR;
|
||||
} */
|
||||
sockindex = ((data->conn->writesockfd != CURL_SOCKET_BAD) &&
|
||||
(data->conn->writesockfd == data->conn->sock[SECONDARYSOCKET]));
|
||||
result = Curl_conn_send(data, sockindex, buf, blen, pnwritten);
|
||||
if(result == CURLE_AGAIN) {
|
||||
result = CURLE_OK;
|
||||
*pnwritten = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
CURLcode Curl_xfer_recv(struct Curl_easy *data,
|
||||
char *buf, size_t blen,
|
||||
ssize_t *pnrcvd)
|
||||
{
|
||||
int sockindex;
|
||||
|
||||
if(!data || !data->conn)
|
||||
return CURLE_FAILED_INIT;
|
||||
/* FIXME: would like to enable this, but some protocols (MQTT) do not
|
||||
* setup the transfer correctly, it seems
|
||||
if(data->conn->sockfd == CURL_SOCKET_BAD) {
|
||||
failf(data, "transfer not setup for receiving");
|
||||
DEBUGASSERT(0);
|
||||
return CURLE_RECV_ERROR;
|
||||
} */
|
||||
sockindex = ((data->conn->sockfd != CURL_SOCKET_BAD) &&
|
||||
(data->conn->sockfd == data->conn->sock[SECONDARYSOCKET]));
|
||||
if(data->set.buffer_size > 0 && (size_t)data->set.buffer_size < blen)
|
||||
blen = (size_t)data->set.buffer_size;
|
||||
return Curl_conn_recv(data, sockindex, buf, blen, pnrcvd);
|
||||
}
|
||||
|
||||
CURLcode Curl_xfer_send_close(struct Curl_easy *data)
|
||||
{
|
||||
Curl_conn_ev_data_done_send(data);
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
|
|
@ -45,17 +45,11 @@ typedef enum {
|
|||
|
||||
CURLcode Curl_follow(struct Curl_easy *data, char *newurl,
|
||||
followtype type);
|
||||
CURLcode Curl_readwrite(struct Curl_easy *data, bool *done);
|
||||
CURLcode Curl_readwrite(struct Curl_easy *data);
|
||||
int Curl_single_getsock(struct Curl_easy *data,
|
||||
struct connectdata *conn, curl_socket_t *socks);
|
||||
CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes,
|
||||
size_t *nreadp);
|
||||
CURLcode Curl_retry_request(struct Curl_easy *data, char **url);
|
||||
bool Curl_meets_timecondition(struct Curl_easy *data, time_t timeofdoc);
|
||||
CURLcode Curl_get_upload_buffer(struct Curl_easy *data);
|
||||
|
||||
CURLcode Curl_done_sending(struct Curl_easy *data,
|
||||
struct SingleRequest *k);
|
||||
|
||||
/**
|
||||
* Write the transfer raw response bytes, as received from the connection.
|
||||
|
@ -72,11 +66,10 @@ CURLcode Curl_done_sending(struct Curl_easy *data,
|
|||
*/
|
||||
CURLcode Curl_xfer_write_resp(struct Curl_easy *data,
|
||||
char *buf, size_t blen,
|
||||
bool is_eos, bool *done);
|
||||
bool is_eos);
|
||||
|
||||
/* This sets up a forthcoming transfer */
|
||||
void
|
||||
Curl_setup_transfer (struct Curl_easy *data,
|
||||
void Curl_xfer_setup(struct Curl_easy *data,
|
||||
int sockindex, /* socket index to read from or -1 */
|
||||
curl_off_t size, /* -1 if unknown at this point */
|
||||
bool getheader, /* TRUE if header parsing is wanted */
|
||||
|
@ -85,4 +78,30 @@ Curl_setup_transfer (struct Curl_easy *data,
|
|||
disables */
|
||||
);
|
||||
|
||||
/**
|
||||
* Multi has set transfer to DONE. Last chance to trigger
|
||||
* missing response things like writing an EOS to the client.
|
||||
*/
|
||||
CURLcode Curl_xfer_write_done(struct Curl_easy *data, bool premature);
|
||||
|
||||
/**
|
||||
* Send data on the socket/connection filter designated
|
||||
* for transfer's outgoing data.
|
||||
* Will return CURLE_OK on blocking with (*pnwritten == 0).
|
||||
*/
|
||||
CURLcode Curl_xfer_send(struct Curl_easy *data,
|
||||
const void *buf, size_t blen,
|
||||
size_t *pnwritten);
|
||||
|
||||
/**
|
||||
* Receive data on the socket/connection filter designated
|
||||
* for transfer's incoming data.
|
||||
* Will return CURLE_AGAIN on blocking with (*pnrcvd == 0).
|
||||
*/
|
||||
CURLcode Curl_xfer_recv(struct Curl_easy *data,
|
||||
char *buf, size_t blen,
|
||||
ssize_t *pnrcvd);
|
||||
|
||||
CURLcode Curl_xfer_send_close(struct Curl_easy *data);
|
||||
|
||||
#endif /* HEADER_CURL_TRANSFER_H */
|
||||
|
|
124
lib/url.c
124
lib/url.c
|
@ -261,7 +261,7 @@ CURLcode Curl_close(struct Curl_easy **datap)
|
|||
free(data->state.range);
|
||||
|
||||
/* freed here just in case DONE wasn't called */
|
||||
Curl_free_request_state(data);
|
||||
Curl_req_free(&data->req, data);
|
||||
|
||||
/* Close down all open SSL info and sessions */
|
||||
Curl_ssl_close_all(data);
|
||||
|
@ -269,10 +269,6 @@ CURLcode Curl_close(struct Curl_easy **datap)
|
|||
Curl_safefree(data->state.scratch);
|
||||
Curl_ssl_free_certinfo(data);
|
||||
|
||||
/* Cleanup possible redirect junk */
|
||||
free(data->req.newurl);
|
||||
data->req.newurl = NULL;
|
||||
|
||||
if(data->state.referer_alloc) {
|
||||
Curl_safefree(data->state.referer);
|
||||
data->state.referer_alloc = FALSE;
|
||||
|
@ -280,9 +276,7 @@ CURLcode Curl_close(struct Curl_easy **datap)
|
|||
data->state.referer = NULL;
|
||||
|
||||
up_free(data);
|
||||
Curl_safefree(data->state.buffer);
|
||||
Curl_dyn_free(&data->state.headerb);
|
||||
Curl_safefree(data->state.ulbuf);
|
||||
Curl_flush_cookies(data, TRUE);
|
||||
Curl_altsvc_save(data, data->asi, data->set.str[STRING_ALTSVC]);
|
||||
Curl_altsvc_cleanup(&data->asi);
|
||||
|
@ -326,16 +320,7 @@ CURLcode Curl_close(struct Curl_easy **datap)
|
|||
Curl_safefree(data->state.aptr.proxyuser);
|
||||
Curl_safefree(data->state.aptr.proxypasswd);
|
||||
|
||||
#ifndef CURL_DISABLE_DOH
|
||||
if(data->req.doh) {
|
||||
Curl_dyn_free(&data->req.doh->probe[0].serverdoh);
|
||||
Curl_dyn_free(&data->req.doh->probe[1].serverdoh);
|
||||
curl_slist_free_all(data->req.doh->headers);
|
||||
Curl_safefree(data->req.doh);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef CURL_DISABLE_HTTP
|
||||
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_FORM_API)
|
||||
Curl_mime_cleanpart(data->state.formp);
|
||||
Curl_safefree(data->state.formp);
|
||||
#endif
|
||||
|
@ -368,7 +353,6 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
|
|||
set->fread_func_set = (curl_read_callback)fread;
|
||||
set->is_fread_set = 0;
|
||||
|
||||
set->seek_func = ZERO_NULL;
|
||||
set->seek_client = ZERO_NULL;
|
||||
|
||||
set->filesize = -1; /* we don't know the size */
|
||||
|
@ -520,9 +504,17 @@ CURLcode Curl_open(struct Curl_easy **curl)
|
|||
|
||||
data->magic = CURLEASY_MAGIC_NUMBER;
|
||||
|
||||
result = Curl_req_init(&data->req);
|
||||
if(result) {
|
||||
DEBUGF(fprintf(stderr, "Error: request init failed\n"));
|
||||
free(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
result = Curl_resolver_init(data, &data->state.async.resolver);
|
||||
if(result) {
|
||||
DEBUGF(fprintf(stderr, "Error: resolver_init failed\n"));
|
||||
Curl_req_free(&data->req, data);
|
||||
free(data);
|
||||
return result;
|
||||
}
|
||||
|
@ -546,6 +538,7 @@ CURLcode Curl_open(struct Curl_easy **curl)
|
|||
Curl_resolver_cleanup(data->state.async.resolver);
|
||||
Curl_dyn_free(&data->state.headerb);
|
||||
Curl_freeset(data);
|
||||
Curl_req_free(&data->req, data);
|
||||
free(data);
|
||||
data = NULL;
|
||||
}
|
||||
|
@ -1009,9 +1002,9 @@ ConnectionExists(struct Curl_easy *data,
|
|||
|
||||
if(!canmultiplex) {
|
||||
if(Curl_resolver_asynch() &&
|
||||
/* primary_ip[0] is NUL only if the resolving of the name hasn't
|
||||
/* remote_ip[0] is NUL only if the resolving of the name hasn't
|
||||
completed yet and until then we don't reuse this connection */
|
||||
!check->primary_ip[0])
|
||||
!check->primary.remote_ip[0])
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -1334,11 +1327,15 @@ ConnectionExists(struct Curl_easy *data,
|
|||
*/
|
||||
#ifndef CURL_DISABLE_VERBOSE_STRINGS
|
||||
void Curl_verboseconnect(struct Curl_easy *data,
|
||||
struct connectdata *conn)
|
||||
struct connectdata *conn, int sockindex)
|
||||
{
|
||||
if(data->set.verbose)
|
||||
if(data->set.verbose && sockindex == SECONDARYSOCKET)
|
||||
infof(data, "Connected 2nd connection to %s port %u",
|
||||
conn->secondary.remote_ip, conn->secondary.remote_port);
|
||||
else
|
||||
infof(data, "Connected to %s (%s) port %u",
|
||||
CURL_CONN_HOST_DISPNAME(conn), conn->primary_ip, conn->port);
|
||||
CURL_CONN_HOST_DISPNAME(conn), conn->primary.remote_ip,
|
||||
conn->primary.remote_port);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1358,7 +1355,7 @@ static struct connectdata *allocate_conn(struct Curl_easy *data)
|
|||
conn->sockfd = CURL_SOCKET_BAD;
|
||||
conn->writesockfd = CURL_SOCKET_BAD;
|
||||
conn->connection_id = -1; /* no ID */
|
||||
conn->port = -1; /* unknown at this point */
|
||||
conn->primary.remote_port = -1; /* unknown at this point */
|
||||
conn->remote_port = -1; /* unknown at this point */
|
||||
|
||||
/* Default protocol-independent behavior doesn't support persistent
|
||||
|
@ -1971,7 +1968,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data,
|
|||
}
|
||||
else {
|
||||
unsigned long port = strtoul(data->state.up.port, NULL, 10);
|
||||
conn->port = conn->remote_port =
|
||||
conn->primary.remote_port = conn->remote_port =
|
||||
(data->set.use_port && data->state.allow_port) ?
|
||||
data->set.use_port : curlx_ultous(port);
|
||||
}
|
||||
|
@ -2047,32 +2044,14 @@ static CURLcode setup_connection_internals(struct Curl_easy *data,
|
|||
p = conn->handler; /* May have changed. */
|
||||
}
|
||||
|
||||
if(conn->port < 0)
|
||||
if(conn->primary.remote_port < 0)
|
||||
/* we check for -1 here since if proxy was detected already, this
|
||||
was very likely already set to the proxy port */
|
||||
conn->port = p->defport;
|
||||
conn->primary.remote_port = p->defport;
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Curl_free_request_state() should free temp data that was allocated in the
|
||||
* Curl_easy for this single request.
|
||||
*/
|
||||
|
||||
void Curl_free_request_state(struct Curl_easy *data)
|
||||
{
|
||||
Curl_safefree(data->req.p.http);
|
||||
Curl_safefree(data->req.newurl);
|
||||
#ifndef CURL_DISABLE_DOH
|
||||
if(data->req.doh) {
|
||||
Curl_close(&data->req.doh->probe[0].easy);
|
||||
Curl_close(&data->req.doh->probe[1].easy);
|
||||
}
|
||||
#endif
|
||||
Curl_client_cleanup(data);
|
||||
}
|
||||
|
||||
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
|
||||
|
@ -2314,8 +2293,9 @@ static CURLcode parse_proxy(struct Curl_easy *data,
|
|||
}
|
||||
if(port >= 0) {
|
||||
proxyinfo->port = port;
|
||||
if(conn->port < 0 || sockstype || !conn->socks_proxy.host.rawalloc)
|
||||
conn->port = port;
|
||||
if(conn->primary.remote_port < 0 || sockstype ||
|
||||
!conn->socks_proxy.host.rawalloc)
|
||||
conn->primary.remote_port = port;
|
||||
}
|
||||
|
||||
/* now, clone the proxy host name */
|
||||
|
@ -3213,8 +3193,8 @@ static CURLcode resolve_proxy(struct Curl_easy *data,
|
|||
if(!conn->hostname_resolve)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
rc = Curl_resolv_timeout(data, conn->hostname_resolve, (int)conn->port,
|
||||
&hostaddr, timeout_ms);
|
||||
rc = Curl_resolv_timeout(data, conn->hostname_resolve,
|
||||
conn->primary.remote_port, &hostaddr, timeout_ms);
|
||||
conn->dns_entry = hostaddr;
|
||||
if(rc == CURLRESOLV_PENDING)
|
||||
*async = TRUE;
|
||||
|
@ -3244,7 +3224,7 @@ static CURLcode resolve_host(struct Curl_easy *data,
|
|||
|
||||
/* If not connecting via a proxy, extract the port from the URL, if it is
|
||||
* there, thus overriding any defaults that might have been set above. */
|
||||
conn->port = conn->bits.conn_to_port ? conn->conn_to_port :
|
||||
conn->primary.remote_port = conn->bits.conn_to_port ? conn->conn_to_port :
|
||||
conn->remote_port;
|
||||
|
||||
/* Resolve target host right on */
|
||||
|
@ -3252,8 +3232,8 @@ static CURLcode resolve_host(struct Curl_easy *data,
|
|||
if(!conn->hostname_resolve)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
rc = Curl_resolv_timeout(data, conn->hostname_resolve, (int)conn->port,
|
||||
&hostaddr, timeout_ms);
|
||||
rc = Curl_resolv_timeout(data, conn->hostname_resolve,
|
||||
conn->primary.remote_port, &hostaddr, timeout_ms);
|
||||
conn->dns_entry = hostaddr;
|
||||
if(rc == CURLRESOLV_PENDING)
|
||||
*async = TRUE;
|
||||
|
@ -3590,7 +3570,7 @@ static CURLcode create_conn(struct Curl_easy *data,
|
|||
/* this is supposed to be the connect function so we better at least check
|
||||
that the file is present here! */
|
||||
DEBUGASSERT(conn->handler->connect_it);
|
||||
Curl_persistconninfo(data, conn, NULL, -1);
|
||||
Curl_persistconninfo(data, conn, NULL);
|
||||
result = conn->handler->connect_it(data, &done);
|
||||
|
||||
/* Setup a "faked" transfer that'll do nothing */
|
||||
|
@ -3610,7 +3590,7 @@ static CURLcode create_conn(struct Curl_easy *data,
|
|||
(void)conn->handler->done(data, result, FALSE);
|
||||
goto out;
|
||||
}
|
||||
Curl_setup_transfer(data, -1, -1, FALSE, -1);
|
||||
Curl_xfer_setup(data, -1, -1, FALSE, -1);
|
||||
}
|
||||
|
||||
/* since we skip do_init() */
|
||||
|
@ -3621,10 +3601,10 @@ static CURLcode create_conn(struct Curl_easy *data,
|
|||
#endif
|
||||
|
||||
/* Setup filter for network connections */
|
||||
conn->recv[FIRSTSOCKET] = Curl_conn_recv;
|
||||
conn->send[FIRSTSOCKET] = Curl_conn_send;
|
||||
conn->recv[SECONDARYSOCKET] = Curl_conn_recv;
|
||||
conn->send[SECONDARYSOCKET] = Curl_conn_send;
|
||||
conn->recv[FIRSTSOCKET] = Curl_cf_recv;
|
||||
conn->send[FIRSTSOCKET] = Curl_cf_send;
|
||||
conn->recv[SECONDARYSOCKET] = Curl_cf_recv;
|
||||
conn->send[SECONDARYSOCKET] = Curl_cf_send;
|
||||
conn->bits.tcp_fastopen = data->set.tcp_fastopen;
|
||||
|
||||
/* Complete the easy's SSL configuration for connection cache matching */
|
||||
|
@ -3789,13 +3769,6 @@ static CURLcode create_conn(struct Curl_easy *data,
|
|||
|
||||
/* Continue connectdata initialization here. */
|
||||
|
||||
/*
|
||||
* Inherit the proper values from the urldata struct AFTER we have arranged
|
||||
* the persistent connection stuff
|
||||
*/
|
||||
conn->seek_func = data->set.seek_func;
|
||||
conn->seek_client = data->set.seek_client;
|
||||
|
||||
/*************************************************************
|
||||
* Resolve the address of the server or proxy
|
||||
*************************************************************/
|
||||
|
@ -3849,6 +3822,9 @@ CURLcode Curl_setup_conn(struct Curl_easy *data,
|
|||
if(!conn->bits.reuse)
|
||||
result = Curl_conn_setup(data, conn, FIRSTSOCKET, conn->dns_entry,
|
||||
CURL_CF_SSL_DEFAULT);
|
||||
if(!result)
|
||||
result = Curl_headers_init(data);
|
||||
|
||||
/* not sure we need this flag to be passed around any more */
|
||||
*protocol_done = FALSE;
|
||||
return result;
|
||||
|
@ -3863,11 +3839,8 @@ CURLcode Curl_connect(struct Curl_easy *data,
|
|||
|
||||
*asyncp = FALSE; /* assume synchronous resolves by default */
|
||||
|
||||
/* init the single-transfer specific data */
|
||||
Curl_free_request_state(data);
|
||||
memset(&data->req, 0, sizeof(struct SingleRequest));
|
||||
data->req.size = data->req.maxdownload = -1;
|
||||
data->req.no_body = data->set.opt_no_body;
|
||||
/* Set the request to virgin state based on transfer settings */
|
||||
Curl_req_hard_reset(&data->req, data);
|
||||
|
||||
/* call the stuff that needs to be called */
|
||||
result = create_conn(data, &conn, asyncp);
|
||||
|
@ -3910,8 +3883,6 @@ CURLcode Curl_connect(struct Curl_easy *data,
|
|||
|
||||
CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn)
|
||||
{
|
||||
struct SingleRequest *k = &data->req;
|
||||
|
||||
/* if this is a pushed stream, we need this: */
|
||||
CURLcode result = Curl_preconnect(data);
|
||||
if(result)
|
||||
|
@ -3927,18 +3898,15 @@ CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn)
|
|||
}
|
||||
|
||||
data->state.done = FALSE; /* *_done() is not called yet */
|
||||
data->state.expect100header = FALSE;
|
||||
|
||||
if(data->req.no_body)
|
||||
/* in HTTP lingo, no body means using the HEAD request... */
|
||||
data->state.httpreq = HTTPREQ_HEAD;
|
||||
|
||||
k->start = Curl_now(); /* start time */
|
||||
k->header = TRUE; /* assume header */
|
||||
k->bytecount = 0;
|
||||
k->ignorebody = FALSE;
|
||||
result = Curl_req_start(&data->req, data);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
Curl_client_cleanup(data);
|
||||
Curl_speedinit(data);
|
||||
Curl_pgrsSetUploadCounter(data, 0);
|
||||
Curl_pgrsSetDownloadCounter(data, 0);
|
||||
|
|
|
@ -41,7 +41,6 @@ void Curl_disconnect(struct Curl_easy *data,
|
|||
struct connectdata *, bool dead_connection);
|
||||
CURLcode Curl_setup_conn(struct Curl_easy *data,
|
||||
bool *protocol_done);
|
||||
void Curl_free_request_state(struct Curl_easy *data);
|
||||
CURLcode Curl_parse_login_details(const char *login, const size_t len,
|
||||
char **userptr, char **passwdptr,
|
||||
char **optionsptr);
|
||||
|
@ -59,9 +58,10 @@ const struct Curl_handler *Curl_getn_scheme_handler(const char *scheme,
|
|||
specified */
|
||||
|
||||
#ifdef CURL_DISABLE_VERBOSE_STRINGS
|
||||
#define Curl_verboseconnect(x,y) Curl_nop_stmt
|
||||
#define Curl_verboseconnect(x,y,z) Curl_nop_stmt
|
||||
#else
|
||||
void Curl_verboseconnect(struct Curl_easy *data, struct connectdata *conn);
|
||||
void Curl_verboseconnect(struct Curl_easy *data, struct connectdata *conn,
|
||||
int sockindex);
|
||||
#endif
|
||||
|
||||
#if defined(USE_HTTP2) || defined(USE_HTTP3)
|
||||
|
|
|
@ -531,7 +531,7 @@ UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host,
|
|||
portptr = strchr(hostname, ':');
|
||||
|
||||
if(portptr) {
|
||||
char *rest;
|
||||
char *rest = NULL;
|
||||
long port;
|
||||
size_t keep = portptr - hostname;
|
||||
|
||||
|
@ -681,7 +681,7 @@ static int ipv4_normalize(struct dynbuf *host)
|
|||
return HOST_IPV6;
|
||||
|
||||
while(!done) {
|
||||
char *endp;
|
||||
char *endp = NULL;
|
||||
unsigned long l;
|
||||
if(!ISDIGIT(*c))
|
||||
/* most importantly this doesn't allow a leading plus or minus */
|
||||
|
|
230
lib/urldata.h
230
lib/urldata.h
|
@ -53,6 +53,8 @@
|
|||
#define PORT_GOPHER 70
|
||||
#define PORT_MQTT 1883
|
||||
|
||||
struct curl_trc_featt;
|
||||
|
||||
#ifdef USE_WEBSOCKETS
|
||||
/* CURLPROTO_GOPHERS (29) is the highest publicly used protocol bit number,
|
||||
* the rest are internal information. If we use higher bits we only do this on
|
||||
|
@ -141,6 +143,7 @@ typedef unsigned int curl_prot_t;
|
|||
#include "splay.h"
|
||||
#include "dynbuf.h"
|
||||
#include "dynhds.h"
|
||||
#include "request.h"
|
||||
|
||||
/* return the count of bytes sent, or -1 on error */
|
||||
typedef ssize_t (Curl_send)(struct Curl_easy *data, /* transfer */
|
||||
|
@ -160,7 +163,6 @@ typedef ssize_t (Curl_recv)(struct Curl_easy *data, /* transfer */
|
|||
typedef CURLcode (*Curl_datastream)(struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
int *didwhat,
|
||||
bool *done,
|
||||
int select_res);
|
||||
#endif
|
||||
|
||||
|
@ -266,11 +268,17 @@ typedef enum {
|
|||
/* SSL backend-specific data; declared differently by each SSL backend */
|
||||
struct ssl_backend_data;
|
||||
|
||||
typedef enum {
|
||||
CURL_SSL_PEER_DNS,
|
||||
CURL_SSL_PEER_IPV4,
|
||||
CURL_SSL_PEER_IPV6
|
||||
} ssl_peer_type;
|
||||
|
||||
struct ssl_peer {
|
||||
char *hostname; /* hostname for verification */
|
||||
char *dispname; /* display version of hostname */
|
||||
char *sni; /* SNI version of hostname or NULL if not usable */
|
||||
BIT(is_ip_address); /* if hostname is an IPv4|6 address */
|
||||
ssl_peer_type type; /* type of the peer information */
|
||||
};
|
||||
|
||||
struct ssl_primary_config {
|
||||
|
@ -519,10 +527,6 @@ struct ConnectBits {
|
|||
the TCP layer connect */
|
||||
BIT(retry); /* this connection is about to get closed and then
|
||||
re-attempted at another connection. */
|
||||
BIT(authneg); /* TRUE when the auth phase has started, which means
|
||||
that we are creating a request with an auth header,
|
||||
but it is not the final request in the auth
|
||||
negotiation. */
|
||||
#ifndef CURL_DISABLE_FTP
|
||||
BIT(ftp_use_epsv); /* As set with CURLOPT_FTP_USE_EPSV, but if we find out
|
||||
EPSV doesn't work we disable it for the forthcoming
|
||||
|
@ -575,6 +579,14 @@ struct hostname {
|
|||
#define KEEP_RECV_PAUSE (1<<4) /* reading is paused */
|
||||
#define KEEP_SEND_PAUSE (1<<5) /* writing is paused */
|
||||
|
||||
/* KEEP_SEND_TIMED is set when the transfer should attempt sending
|
||||
* at timer (or other) events. A transfer waiting on a timer will
|
||||
* remove KEEP_SEND to suppress POLLOUTs of the connection.
|
||||
* Adding KEEP_SEND_TIMED will then attempt to send whenever the transfer
|
||||
* enters the "readwrite" loop, e.g. when a timer fires.
|
||||
* This is used in HTTP for 'Expect: 100-continue' waiting. */
|
||||
#define KEEP_SEND_TIMED (1<<6)
|
||||
|
||||
#define KEEP_RECVBITS (KEEP_RECV | KEEP_RECV_HOLD | KEEP_RECV_PAUSE)
|
||||
#define KEEP_SENDBITS (KEEP_SEND | KEEP_SEND_HOLD | KEEP_SEND_PAUSE)
|
||||
|
||||
|
@ -612,22 +624,6 @@ struct easy_pollset {
|
|||
unsigned char actions[MAX_SOCKSPEREASYHANDLE];
|
||||
};
|
||||
|
||||
enum expect100 {
|
||||
EXP100_SEND_DATA, /* enough waiting, just send the body now */
|
||||
EXP100_AWAITING_CONTINUE, /* waiting for the 100 Continue header */
|
||||
EXP100_SENDING_REQUEST, /* still sending the request but will wait for
|
||||
the 100 header once done with the request */
|
||||
EXP100_FAILED /* used on 417 Expectation Failed */
|
||||
};
|
||||
|
||||
enum upgrade101 {
|
||||
UPGR101_INIT, /* default state */
|
||||
UPGR101_WS, /* upgrade to WebSockets requested */
|
||||
UPGR101_H2, /* upgrade to HTTP/2 requested */
|
||||
UPGR101_RECEIVED, /* 101 response received */
|
||||
UPGR101_WORKING /* talking upgraded protocol */
|
||||
};
|
||||
|
||||
enum doh_slots {
|
||||
/* Explicit values for first two symbols so as to match hard-coded
|
||||
* constants in existing code
|
||||
|
@ -646,111 +642,6 @@ enum doh_slots {
|
|||
DOH_PROBE_SLOTS
|
||||
};
|
||||
|
||||
/*
|
||||
* Request specific data in the easy handle (Curl_easy). Previously,
|
||||
* these members were on the connectdata struct but since a conn struct may
|
||||
* now be shared between different Curl_easys, we store connection-specific
|
||||
* data here. This struct only keeps stuff that's interesting for *this*
|
||||
* request, as it will be cleared between multiple ones
|
||||
*/
|
||||
struct SingleRequest {
|
||||
curl_off_t size; /* -1 if unknown at this point */
|
||||
curl_off_t maxdownload; /* in bytes, the maximum amount of data to fetch,
|
||||
-1 means unlimited */
|
||||
curl_off_t bytecount; /* total number of bytes read */
|
||||
curl_off_t writebytecount; /* number of bytes written */
|
||||
|
||||
curl_off_t pendingheader; /* this many bytes left to send is actually
|
||||
header and not body */
|
||||
struct curltime start; /* transfer started at this time */
|
||||
unsigned int headerbytecount; /* received server headers (not CONNECT
|
||||
headers) */
|
||||
unsigned int allheadercount; /* all received headers (server + CONNECT) */
|
||||
unsigned int deductheadercount; /* this amount of bytes doesn't count when
|
||||
we check if anything has been transferred
|
||||
at the end of a connection. We use this
|
||||
counter to make only a 100 reply (without
|
||||
a following second response code) result
|
||||
in a CURLE_GOT_NOTHING error code */
|
||||
int headerline; /* counts header lines to better track the
|
||||
first one */
|
||||
curl_off_t offset; /* possible resume offset read from the
|
||||
Content-Range: header */
|
||||
int httpcode; /* error code from the 'HTTP/1.? XXX' or
|
||||
'RTSP/1.? XXX' line */
|
||||
int keepon;
|
||||
struct curltime start100; /* time stamp to wait for the 100 code from */
|
||||
enum expect100 exp100; /* expect 100 continue state */
|
||||
enum upgrade101 upgr101; /* 101 upgrade state */
|
||||
|
||||
/* Client Writer stack, handles trasnfer- and content-encodings, protocol
|
||||
* checks, pausing by client callbacks. */
|
||||
struct Curl_cwriter *writer_stack;
|
||||
time_t timeofdoc;
|
||||
long bodywrites;
|
||||
char *location; /* This points to an allocated version of the Location:
|
||||
header data */
|
||||
char *newurl; /* Set to the new URL to use when a redirect or a retry is
|
||||
wanted */
|
||||
|
||||
/* 'upload_present' is used to keep a byte counter of how much data there is
|
||||
still left in the buffer, aimed for upload. */
|
||||
ssize_t upload_present;
|
||||
|
||||
/* 'upload_fromhere' is used as a read-pointer when we uploaded parts of a
|
||||
buffer, so the next read should read from where this pointer points to,
|
||||
and the 'upload_present' contains the number of bytes available at this
|
||||
position */
|
||||
char *upload_fromhere;
|
||||
|
||||
/* Allocated protocol-specific data. Each protocol handler makes sure this
|
||||
points to data it needs. */
|
||||
union {
|
||||
struct FILEPROTO *file;
|
||||
struct FTP *ftp;
|
||||
struct HTTP *http;
|
||||
struct IMAP *imap;
|
||||
struct ldapreqinfo *ldap;
|
||||
struct MQTT *mqtt;
|
||||
struct POP3 *pop3;
|
||||
struct RTSP *rtsp;
|
||||
struct smb_request *smb;
|
||||
struct SMTP *smtp;
|
||||
struct SSHPROTO *ssh;
|
||||
struct TELNET *telnet;
|
||||
} p;
|
||||
#ifndef CURL_DISABLE_DOH
|
||||
struct dohdata *doh; /* DoH specific data for this request */
|
||||
#endif
|
||||
#if defined(_WIN32) && defined(USE_WINSOCK)
|
||||
struct curltime last_sndbuf_update; /* last time readwrite_upload called
|
||||
win_update_buffer_size */
|
||||
#endif
|
||||
char fread_eof[2]; /* the body read callback (index 0) returned EOF or
|
||||
the trailer read callback (index 1) returned EOF */
|
||||
#ifndef CURL_DISABLE_COOKIES
|
||||
unsigned char setcookies;
|
||||
#endif
|
||||
BIT(header); /* incoming data has HTTP header */
|
||||
BIT(content_range); /* set TRUE if Content-Range: was found */
|
||||
BIT(download_done); /* set to TRUE when download is complete */
|
||||
BIT(eos_written); /* iff EOS has been written to client */
|
||||
BIT(upload_done); /* set to TRUE when doing chunked transfer-encoding
|
||||
upload and we're uploading the last chunk */
|
||||
BIT(ignorebody); /* we read a response-body but we ignore it! */
|
||||
BIT(http_bodyless); /* HTTP response status code is between 100 and 199,
|
||||
204 or 304 */
|
||||
BIT(chunk); /* if set, this is a chunked transfer-encoding */
|
||||
BIT(ignore_cl); /* ignore content-length */
|
||||
BIT(upload_chunky); /* set TRUE if we are doing chunked transfer-encoding
|
||||
on upload */
|
||||
BIT(getheader); /* TRUE if header parsing is wanted */
|
||||
BIT(forbidchunk); /* used only to explicitly forbid chunk-upload for
|
||||
specific upload buffers. See readmoredata() in http.c
|
||||
for details. */
|
||||
BIT(no_body); /* the response has no body */
|
||||
};
|
||||
|
||||
/*
|
||||
* Specific protocol handler.
|
||||
*/
|
||||
|
@ -819,7 +710,7 @@ struct Curl_handler {
|
|||
allow the protocol to do extra handling in writing response to
|
||||
the client. */
|
||||
CURLcode (*write_resp)(struct Curl_easy *data, const char *buf, size_t blen,
|
||||
bool is_eos, bool *done);
|
||||
bool is_eos);
|
||||
|
||||
/* This function can perform various checks on the connection. See
|
||||
CONNCHECK_* for more information about the checks that can be performed,
|
||||
|
@ -875,6 +766,13 @@ struct Curl_handler {
|
|||
#define CONNRESULT_NONE 0 /* No extra information. */
|
||||
#define CONNRESULT_DEAD (1<<0) /* The connection is dead. */
|
||||
|
||||
struct ip_quadruple {
|
||||
char remote_ip[MAX_IPADR_LEN];
|
||||
char local_ip[MAX_IPADR_LEN];
|
||||
int remote_port;
|
||||
int local_port;
|
||||
};
|
||||
|
||||
struct proxy_info {
|
||||
struct hostname host;
|
||||
int port;
|
||||
|
@ -930,14 +828,13 @@ struct connectdata {
|
|||
struct proxy_info socks_proxy;
|
||||
struct proxy_info http_proxy;
|
||||
#endif
|
||||
/* 'primary_ip' and 'primary_port' get filled with peer's numerical
|
||||
ip address and port number whenever an outgoing connection is
|
||||
*attempted* from the primary socket to a remote address. When more
|
||||
than one address is tried for a connection these will hold data
|
||||
/* 'primary' and 'secondary' get filled with IP quadruple
|
||||
(local/remote numerical ip address and port) whenever a is *attempted*.
|
||||
When more than one address is tried for a connection these will hold data
|
||||
for the last attempt. When the connection is actually established
|
||||
these are updated with data which comes directly from the socket. */
|
||||
|
||||
char primary_ip[MAX_IPADR_LEN];
|
||||
struct ip_quadruple primary;
|
||||
struct ip_quadruple secondary;
|
||||
char *user; /* user name string, allocated */
|
||||
char *passwd; /* password string, allocated */
|
||||
char *options; /* options string, allocated */
|
||||
|
@ -990,14 +887,17 @@ struct connectdata {
|
|||
#endif /* however, some of them are ftp specific. */
|
||||
|
||||
struct Curl_llist easyq; /* List of easy handles using this connection */
|
||||
curl_seek_callback seek_func; /* function that seeks the input */
|
||||
void *seek_client; /* pointer to pass to the seek() above */
|
||||
|
||||
/*************** Request - specific items ************/
|
||||
#if defined(USE_WINDOWS_SSPI) && defined(SECPKG_ATTR_ENDPOINT_BINDINGS)
|
||||
CtxtHandle *sslContext;
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && defined(USE_WINSOCK)
|
||||
struct curltime last_sndbuf_update; /* last time readwrite_upload called
|
||||
win_update_buffer_size */
|
||||
#endif
|
||||
|
||||
#ifdef USE_GSASL
|
||||
struct gsasldata gsasl;
|
||||
#endif
|
||||
|
@ -1080,7 +980,6 @@ struct connectdata {
|
|||
int socks5_gssapi_enctype;
|
||||
#endif
|
||||
/* The field below gets set in connect.c:connecthost() */
|
||||
int port; /* which port to use locally - to connect to */
|
||||
int remote_port; /* the remote port, not the proxy port! */
|
||||
int conn_to_port; /* the remote port to connect to. valid only if
|
||||
bits.conn_to_port is set */
|
||||
|
@ -1135,22 +1034,16 @@ struct PureInfo {
|
|||
curl_off_t retry_after; /* info from Retry-After: header */
|
||||
unsigned int header_size; /* size of read header(s) in bytes */
|
||||
|
||||
/* PureInfo members 'conn_primary_ip', 'conn_primary_port', 'conn_local_ip'
|
||||
and, 'conn_local_port' are copied over from the connectdata struct in
|
||||
order to allow curl_easy_getinfo() to return this information even when
|
||||
the session handle is no longer associated with a connection, and also
|
||||
allow curl_easy_reset() to clear this information from the session handle
|
||||
without disturbing information which is still alive, and that might be
|
||||
reused, in the connection cache. */
|
||||
|
||||
char conn_primary_ip[MAX_IPADR_LEN];
|
||||
int conn_primary_port; /* this is the destination port to the connection,
|
||||
which might have been a proxy */
|
||||
/* PureInfo primary ip_quadruple is copied over from the connectdata
|
||||
struct in order to allow curl_easy_getinfo() to return this information
|
||||
even when the session handle is no longer associated with a connection,
|
||||
and also allow curl_easy_reset() to clear this information from the
|
||||
session handle without disturbing information which is still alive, and
|
||||
that might be reused, in the connection cache. */
|
||||
struct ip_quadruple primary;
|
||||
int conn_remote_port; /* this is the "remote port", which is the port
|
||||
number of the used URL, independent of proxy or
|
||||
not */
|
||||
char conn_local_ip[MAX_IPADR_LEN];
|
||||
int conn_local_port;
|
||||
const char *conn_scheme;
|
||||
unsigned int conn_protocol;
|
||||
struct curl_certinfo certs; /* info about the certs. Asked for with
|
||||
|
@ -1158,6 +1051,7 @@ struct PureInfo {
|
|||
CURLproxycode pxcode;
|
||||
BIT(timecond); /* set to TRUE if the time condition didn't match, which
|
||||
thus made the document NOT get fetched */
|
||||
BIT(used_proxy); /* the transfer used a proxy */
|
||||
};
|
||||
|
||||
|
||||
|
@ -1263,18 +1157,6 @@ struct Curl_data_priority {
|
|||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* This struct is for holding data that was attempted to get sent to the user's
|
||||
* callback but is held due to pausing. One instance per type (BOTH, HEADER,
|
||||
* BODY).
|
||||
*/
|
||||
struct tempbuf {
|
||||
struct dynbuf b;
|
||||
int type; /* type of the 'tempwrite' buffer as a bitmask that is used with
|
||||
Curl_client_write() */
|
||||
BIT(paused_body); /* if PAUSE happened before/during BODY write */
|
||||
};
|
||||
|
||||
/* Timers */
|
||||
typedef enum {
|
||||
EXPIRE_100_TIMEOUT,
|
||||
|
@ -1337,8 +1219,6 @@ struct UrlState {
|
|||
struct dynbuf headerb; /* buffer to store headers in */
|
||||
struct curl_slist *hstslist; /* list of HSTS files set by
|
||||
curl_easy_setopt(HSTS) calls */
|
||||
char *buffer; /* download buffer */
|
||||
char *ulbuf; /* allocated upload buffer or NULL */
|
||||
curl_off_t current_speed; /* the ProgressShow() function sets this,
|
||||
bytes / second */
|
||||
|
||||
|
@ -1353,8 +1233,6 @@ struct UrlState {
|
|||
int retrycount; /* number of retries on a new connection */
|
||||
struct Curl_ssl_session *session; /* array of 'max_ssl_sessions' size */
|
||||
long sessionage; /* number of the most recent session */
|
||||
struct tempbuf tempwrite[3]; /* BOTH, HEADER, BODY */
|
||||
unsigned int tempcount; /* number of entries in use in tempwrite, 0 - 3 */
|
||||
int os_errno; /* filled in with errno whenever an error occurs */
|
||||
char *scratch; /* huge buffer[set.buffer_size*2] for upload CRLF replacing */
|
||||
long followlocation; /* redirect counter */
|
||||
|
@ -1387,8 +1265,6 @@ struct UrlState {
|
|||
#if !defined(_WIN32) && !defined(MSDOS) && !defined(__EMX__)
|
||||
/* do FTP line-end conversions on most platforms */
|
||||
#define CURL_DO_LINEEND_CONV
|
||||
/* for FTP downloads: track CRLF sequences that span blocks */
|
||||
BIT(prev_block_had_trailing_cr);
|
||||
/* for FTP downloads: how many CRLFs did we converted to LFs? */
|
||||
curl_off_t crlf_conversions;
|
||||
#endif
|
||||
|
@ -1422,8 +1298,10 @@ struct UrlState {
|
|||
this should be dealt with in pretransfer */
|
||||
#ifndef CURL_DISABLE_HTTP
|
||||
curl_mimepart *mimepost;
|
||||
#ifndef CURL_DISABLE_FORM_API
|
||||
curl_mimepart *formp; /* storage for old API form-posting, allocated on
|
||||
demand */
|
||||
#endif
|
||||
size_t trailers_bytes_sent;
|
||||
struct dynbuf trailers_buf; /* a buffer containing the compiled trailing
|
||||
headers */
|
||||
|
@ -1442,6 +1320,10 @@ struct UrlState {
|
|||
CURLcode hresult; /* used to pass return codes back from hyper callbacks */
|
||||
#endif
|
||||
|
||||
#ifndef CURL_DISABLE_VERBOSE_STRINGS
|
||||
struct curl_trc_feat *feat; /* opt. trace feature transfer is part of */
|
||||
#endif
|
||||
|
||||
/* Dynamically allocated strings, MUST be freed before this struct is
|
||||
killed. */
|
||||
struct dynamically_allocated_data {
|
||||
|
@ -1490,7 +1372,6 @@ struct UrlState {
|
|||
BIT(authproblem); /* TRUE if there's some problem authenticating */
|
||||
/* set after initial USER failure, to prevent an authentication loop */
|
||||
BIT(wildcardmatch); /* enable wildcard matching */
|
||||
BIT(expect100header); /* TRUE if we added Expect: 100-continue */
|
||||
BIT(disableexpect); /* TRUE if Expect: is disabled due to a previous
|
||||
417 response */
|
||||
BIT(use_range);
|
||||
|
@ -1509,9 +1390,6 @@ struct UrlState {
|
|||
BIT(url_alloc); /* URL string is malloc()'ed */
|
||||
BIT(referer_alloc); /* referer string is malloc()ed */
|
||||
BIT(wildcard_resolve); /* Set to true if any resolve change is a wildcard */
|
||||
BIT(rewindbeforesend);/* TRUE when the sending couldn't be stopped even
|
||||
though it will be discarded. We must call the data
|
||||
rewind callback before trying to send again. */
|
||||
BIT(upload); /* upload request */
|
||||
BIT(internal); /* internal: true if this easy handle was created for
|
||||
internal use and the user does not have ownership of the
|
||||
|
@ -1720,7 +1598,9 @@ struct UserDefined {
|
|||
curl_off_t set_resume_from; /* continue [ftp] transfer from here */
|
||||
struct curl_slist *headers; /* linked list of extra headers */
|
||||
struct curl_httppost *httppost; /* linked list of old POST data */
|
||||
#if !defined(CURL_DISABLE_MIME) || !defined(CURL_DISABLE_FORM_API)
|
||||
curl_mimepart mimepost; /* MIME/POST data. */
|
||||
#endif
|
||||
#ifndef CURL_DISABLE_TELNET
|
||||
struct curl_slist *telnet_options; /* linked list of telnet options */
|
||||
#endif
|
||||
|
@ -1933,6 +1813,12 @@ struct UserDefined {
|
|||
#endif
|
||||
};
|
||||
|
||||
#ifndef CURL_DISABLE_MIME
|
||||
#define IS_MIME_POST(a) ((a)->set.mimepost.kind != MIMEKIND_NONE)
|
||||
#else
|
||||
#define IS_MIME_POST(a) FALSE
|
||||
#endif
|
||||
|
||||
struct Names {
|
||||
struct Curl_hash *hostcache;
|
||||
enum {
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include "curl_hmac.h"
|
||||
#include "curl_md5.h"
|
||||
#include "curl_sha256.h"
|
||||
#include "curl_sha512_256.h"
|
||||
#include "vtls/vtls.h"
|
||||
#include "warnless.h"
|
||||
#include "strtok.h"
|
||||
|
@ -150,7 +151,7 @@ static void auth_digest_md5_to_ascii(unsigned char *source, /* 16 bytes */
|
|||
msnprintf((char *) &dest[i * 2], 3, "%02x", source[i]);
|
||||
}
|
||||
|
||||
/* Convert sha256 chunk to RFC7616 -suitable ascii string */
|
||||
/* Convert sha256 or SHA-512/256 chunk to RFC7616 -suitable ascii string */
|
||||
static void auth_digest_sha256_to_ascii(unsigned char *source, /* 32 bytes */
|
||||
unsigned char *dest) /* 65 bytes */
|
||||
{
|
||||
|
@ -601,10 +602,20 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
|
|||
digest->algo = ALGO_SHA256;
|
||||
else if(strcasecompare(content, "SHA-256-SESS"))
|
||||
digest->algo = ALGO_SHA256SESS;
|
||||
else if(strcasecompare(content, "SHA-512-256"))
|
||||
else if(strcasecompare(content, "SHA-512-256")) {
|
||||
#ifdef CURL_HAVE_SHA512_256
|
||||
digest->algo = ALGO_SHA512_256;
|
||||
else if(strcasecompare(content, "SHA-512-256-SESS"))
|
||||
#else /* ! CURL_HAVE_SHA512_256 */
|
||||
return CURLE_NOT_BUILT_IN;
|
||||
#endif /* ! CURL_HAVE_SHA512_256 */
|
||||
}
|
||||
else if(strcasecompare(content, "SHA-512-256-SESS")) {
|
||||
#ifdef CURL_HAVE_SHA512_256
|
||||
digest->algo = ALGO_SHA512_256SESS;
|
||||
#else /* ! CURL_HAVE_SHA512_256 */
|
||||
return CURLE_NOT_BUILT_IN;
|
||||
#endif /* ! CURL_HAVE_SHA512_256 */
|
||||
}
|
||||
else
|
||||
return CURLE_BAD_CONTENT_ENCODING;
|
||||
}
|
||||
|
@ -717,8 +728,10 @@ static CURLcode auth_create_digest_http_message(
|
|||
if(!hashthis)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
|
||||
result = hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
|
||||
free(hashthis);
|
||||
if(result)
|
||||
return result;
|
||||
convert_to_ascii(hashbuf, (unsigned char *)userh);
|
||||
}
|
||||
|
||||
|
@ -738,8 +751,10 @@ static CURLcode auth_create_digest_http_message(
|
|||
if(!hashthis)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
|
||||
result = hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
|
||||
free(hashthis);
|
||||
if(result)
|
||||
return result;
|
||||
convert_to_ascii(hashbuf, ha1);
|
||||
|
||||
if(digest->algo & SESSION_ALGO) {
|
||||
|
@ -748,8 +763,10 @@ static CURLcode auth_create_digest_http_message(
|
|||
if(!tmp)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
hash(hashbuf, (unsigned char *) tmp, strlen(tmp));
|
||||
result = hash(hashbuf, (unsigned char *) tmp, strlen(tmp));
|
||||
free(tmp);
|
||||
if(result)
|
||||
return result;
|
||||
convert_to_ascii(hashbuf, ha1);
|
||||
}
|
||||
|
||||
|
@ -775,7 +792,11 @@ static CURLcode auth_create_digest_http_message(
|
|||
char hashed[65];
|
||||
char *hashthis2;
|
||||
|
||||
hash(hashbuf, (const unsigned char *)"", 0);
|
||||
result = hash(hashbuf, (const unsigned char *)"", 0);
|
||||
if(result) {
|
||||
free(hashthis);
|
||||
return result;
|
||||
}
|
||||
convert_to_ascii(hashbuf, (unsigned char *)hashed);
|
||||
|
||||
hashthis2 = aprintf("%s:%s", hashthis, hashed);
|
||||
|
@ -786,8 +807,10 @@ static CURLcode auth_create_digest_http_message(
|
|||
if(!hashthis)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
|
||||
result = hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
|
||||
free(hashthis);
|
||||
if(result)
|
||||
return result;
|
||||
convert_to_ascii(hashbuf, ha2);
|
||||
|
||||
if(digest->qop) {
|
||||
|
@ -801,8 +824,10 @@ static CURLcode auth_create_digest_http_message(
|
|||
if(!hashthis)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
|
||||
result = hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
|
||||
free(hashthis);
|
||||
if(result)
|
||||
return result;
|
||||
convert_to_ascii(hashbuf, request_digest);
|
||||
|
||||
/* For test case 64 (snooped from a Mozilla 1.3a request)
|
||||
|
@ -957,12 +982,24 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
|
|||
outptr, outlen,
|
||||
auth_digest_md5_to_ascii,
|
||||
Curl_md5it);
|
||||
DEBUGASSERT(digest->algo <= ALGO_SHA512_256SESS);
|
||||
return auth_create_digest_http_message(data, userp, passwdp,
|
||||
request, uripath, digest,
|
||||
outptr, outlen,
|
||||
auth_digest_sha256_to_ascii,
|
||||
Curl_sha256it);
|
||||
|
||||
if(digest->algo <= ALGO_SHA256SESS)
|
||||
return auth_create_digest_http_message(data, userp, passwdp,
|
||||
request, uripath, digest,
|
||||
outptr, outlen,
|
||||
auth_digest_sha256_to_ascii,
|
||||
Curl_sha256it);
|
||||
#ifdef CURL_HAVE_SHA512_256
|
||||
if(digest->algo <= ALGO_SHA512_256SESS)
|
||||
return auth_create_digest_http_message(data, userp, passwdp,
|
||||
request, uripath, digest,
|
||||
outptr, outlen,
|
||||
auth_digest_sha256_to_ascii,
|
||||
Curl_sha512_256it);
|
||||
#endif /* CURL_HAVE_SHA512_256 */
|
||||
|
||||
/* Should be unreachable */
|
||||
return CURLE_BAD_CONTENT_ENCODING;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -212,9 +212,15 @@ char *curl_version(void)
|
|||
|
||||
#ifdef USE_LIBPSL
|
||||
{
|
||||
#if defined(PSL_VERSION_MAJOR) && (PSL_VERSION_MAJOR > 0 || \
|
||||
PSL_VERSION_MINOR >= 11)
|
||||
int num = psl_check_version_number(0);
|
||||
msnprintf(psl_version, sizeof(psl_version), "libpsl/%d.%d.%d",
|
||||
num >> 16, (num >> 8) & 0xff, num & 0xff);
|
||||
#else
|
||||
msnprintf(psl_version, sizeof(psl_version), "libpsl/%s",
|
||||
psl_get_version());
|
||||
#endif
|
||||
src[i++] = psl_version;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -722,23 +722,6 @@ static bool cf_msh3_data_pending(struct Curl_cfilter *cf,
|
|||
return pending;
|
||||
}
|
||||
|
||||
static void cf_msh3_active(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
{
|
||||
struct cf_msh3_ctx *ctx = cf->ctx;
|
||||
|
||||
/* use this socket from now on */
|
||||
cf->conn->sock[cf->sockindex] = ctx->sock[SP_LOCAL];
|
||||
/* the first socket info gets set at conn and data */
|
||||
if(cf->sockindex == FIRSTSOCKET) {
|
||||
cf->conn->remote_addr = &ctx->addr;
|
||||
#ifdef ENABLE_IPV6
|
||||
cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6)? TRUE : FALSE;
|
||||
#endif
|
||||
Curl_persistconninfo(data, cf->conn, ctx->l_ip, ctx->l_port);
|
||||
}
|
||||
ctx->active = TRUE;
|
||||
}
|
||||
|
||||
static CURLcode h3_data_pause(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
bool pause)
|
||||
|
@ -785,10 +768,6 @@ static CURLcode cf_msh3_data_event(struct Curl_cfilter *cf,
|
|||
}
|
||||
}
|
||||
break;
|
||||
case CF_CTRL_CONN_INFO_UPDATE:
|
||||
CURL_TRC_CF(data, cf, "req: update info");
|
||||
cf_msh3_active(cf, data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -58,6 +58,7 @@
|
|||
#include "http1.h"
|
||||
#include "select.h"
|
||||
#include "inet_pton.h"
|
||||
#include "transfer.h"
|
||||
#include "vquic.h"
|
||||
#include "vquic_int.h"
|
||||
#include "vquic-tls.h"
|
||||
|
@ -145,11 +146,9 @@ struct cf_ngtcp2_ctx {
|
|||
struct h3_stream_ctx {
|
||||
int64_t id; /* HTTP/3 protocol identifier */
|
||||
struct bufq sendbuf; /* h3 request body */
|
||||
struct bufq recvbuf; /* h3 response body */
|
||||
struct h1_req_parser h1; /* h1 request parsing */
|
||||
size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */
|
||||
size_t upload_blocked_len; /* the amount written last and EGAINed */
|
||||
size_t recv_buf_nonflow; /* buffered bytes, not counting for flow control */
|
||||
uint64_t error3; /* HTTP/3 stream error code */
|
||||
curl_off_t upload_left; /* number of request bytes left to upload */
|
||||
int status_code; /* HTTP status code */
|
||||
|
@ -190,11 +189,6 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf,
|
|||
Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp,
|
||||
H3_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE);
|
||||
stream->sendbuf_len_in_flight = 0;
|
||||
/* on recv, we need a flexible buffer limit since we also write
|
||||
* headers to it that are not counted against the nghttp3 flow limits. */
|
||||
Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp,
|
||||
H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT);
|
||||
stream->recv_buf_nonflow = 0;
|
||||
Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
|
||||
|
||||
H3_STREAM_LCTX(data) = stream;
|
||||
|
@ -219,7 +213,6 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
|
|||
}
|
||||
|
||||
Curl_bufq_free(&stream->sendbuf);
|
||||
Curl_bufq_free(&stream->recvbuf);
|
||||
Curl_h1_req_parse_free(&stream->h1);
|
||||
free(stream);
|
||||
H3_STREAM_LCTX(data) = NULL;
|
||||
|
@ -387,36 +380,6 @@ static int cb_handshake_completed(ngtcp2_conn *tconn, void *user_data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void report_consumed_data(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
size_t consumed)
|
||||
{
|
||||
struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
|
||||
struct cf_ngtcp2_ctx *ctx = cf->ctx;
|
||||
|
||||
if(!stream)
|
||||
return;
|
||||
/* the HTTP/1.1 response headers are written to the buffer, but
|
||||
* consuming those does not count against flow control. */
|
||||
if(stream->recv_buf_nonflow) {
|
||||
if(consumed >= stream->recv_buf_nonflow) {
|
||||
consumed -= stream->recv_buf_nonflow;
|
||||
stream->recv_buf_nonflow = 0;
|
||||
}
|
||||
else {
|
||||
stream->recv_buf_nonflow -= consumed;
|
||||
consumed = 0;
|
||||
}
|
||||
}
|
||||
if(consumed > 0) {
|
||||
CURL_TRC_CF(data, cf, "[%" PRId64 "] ACK %zu bytes of DATA",
|
||||
stream->id, consumed);
|
||||
ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream->id,
|
||||
consumed);
|
||||
ngtcp2_conn_extend_max_offset(ctx->qconn, consumed);
|
||||
}
|
||||
}
|
||||
|
||||
static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags,
|
||||
int64_t stream_id, uint64_t offset,
|
||||
const uint8_t *buf, size_t buflen,
|
||||
|
@ -796,46 +759,18 @@ static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* write_resp_raw() copies response data in raw format to the `data`'s
|
||||
* receive buffer. If not enough space is available, it appends to the
|
||||
* `data`'s overflow buffer.
|
||||
*/
|
||||
static CURLcode write_resp_raw(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
const void *mem, size_t memlen,
|
||||
bool flow)
|
||||
static CURLcode write_resp_hds(struct Curl_easy *data,
|
||||
const char *buf, size_t blen)
|
||||
{
|
||||
struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
|
||||
CURLcode result = CURLE_OK;
|
||||
ssize_t nwritten;
|
||||
|
||||
(void)cf;
|
||||
if(!stream) {
|
||||
return CURLE_RECV_ERROR;
|
||||
}
|
||||
nwritten = Curl_bufq_write(&stream->recvbuf, mem, memlen, &result);
|
||||
if(nwritten < 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if(!flow)
|
||||
stream->recv_buf_nonflow += (size_t)nwritten;
|
||||
|
||||
if((size_t)nwritten < memlen) {
|
||||
/* This MUST not happen. Our recbuf is dimensioned to hold the
|
||||
* full max_stream_window and then some for this very reason. */
|
||||
DEBUGASSERT(0);
|
||||
return CURLE_RECV_ERROR;
|
||||
}
|
||||
return result;
|
||||
return Curl_xfer_write_resp(data, (char *)buf, blen, FALSE);
|
||||
}
|
||||
|
||||
static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id,
|
||||
const uint8_t *buf, size_t buflen,
|
||||
const uint8_t *buf, size_t blen,
|
||||
void *user_data, void *stream_user_data)
|
||||
{
|
||||
struct Curl_cfilter *cf = user_data;
|
||||
struct cf_ngtcp2_ctx *ctx = cf->ctx;
|
||||
struct Curl_easy *data = stream_user_data;
|
||||
struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
|
||||
CURLcode result;
|
||||
|
@ -846,14 +781,19 @@ static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id,
|
|||
if(!stream)
|
||||
return NGHTTP3_ERR_CALLBACK_FAILURE;
|
||||
|
||||
result = write_resp_raw(cf, data, buf, buflen, TRUE);
|
||||
result = Curl_xfer_write_resp(data, (char *)buf, blen, FALSE);
|
||||
if(result) {
|
||||
CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, ERROR receiving %d",
|
||||
stream->id, buflen, result);
|
||||
stream->id, blen, result);
|
||||
return NGHTTP3_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu", stream->id, buflen);
|
||||
h3_drain_stream(cf, data);
|
||||
if(blen) {
|
||||
CURL_TRC_CF(data, cf, "[%" PRId64 "] ACK %zu bytes of DATA",
|
||||
stream->id, blen);
|
||||
ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream->id, blen);
|
||||
ngtcp2_conn_extend_max_offset(ctx->qconn, blen);
|
||||
}
|
||||
CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu", stream->id, blen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -888,7 +828,7 @@ static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id,
|
|||
if(!stream)
|
||||
return 0;
|
||||
/* add a CRLF only if we've received some headers */
|
||||
result = write_resp_raw(cf, data, "\r\n", 2, FALSE);
|
||||
result = write_resp_hds(data, "\r\n", 2);
|
||||
if(result) {
|
||||
return -1;
|
||||
}
|
||||
|
@ -934,7 +874,7 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id,
|
|||
ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n",
|
||||
stream->status_code);
|
||||
CURL_TRC_CF(data, cf, "[%" PRId64 "] status: %s", stream_id, line);
|
||||
result = write_resp_raw(cf, data, line, ncopy, FALSE);
|
||||
result = write_resp_hds(data, line, ncopy);
|
||||
if(result) {
|
||||
return -1;
|
||||
}
|
||||
|
@ -944,19 +884,19 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id,
|
|||
CURL_TRC_CF(data, cf, "[%" PRId64 "] header: %.*s: %.*s",
|
||||
stream_id, (int)h3name.len, h3name.base,
|
||||
(int)h3val.len, h3val.base);
|
||||
result = write_resp_raw(cf, data, h3name.base, h3name.len, FALSE);
|
||||
result = write_resp_hds(data, (const char *)h3name.base, h3name.len);
|
||||
if(result) {
|
||||
return -1;
|
||||
}
|
||||
result = write_resp_raw(cf, data, ": ", 2, FALSE);
|
||||
result = write_resp_hds(data, ": ", 2);
|
||||
if(result) {
|
||||
return -1;
|
||||
}
|
||||
result = write_resp_raw(cf, data, h3val.base, h3val.len, FALSE);
|
||||
result = write_resp_hds(data, (const char *)h3val.base, h3val.len);
|
||||
if(result) {
|
||||
return -1;
|
||||
}
|
||||
result = write_resp_raw(cf, data, "\r\n", 2, FALSE);
|
||||
result = write_resp_hds(data, "\r\n", 2);
|
||||
if(result) {
|
||||
return -1;
|
||||
}
|
||||
|
@ -1092,7 +1032,7 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf,
|
|||
if(stream->reset) {
|
||||
failf(data,
|
||||
"HTTP/3 stream %" PRId64 " reset by server", stream->id);
|
||||
*err = stream->resp_hds_complete? CURLE_PARTIAL_FILE : CURLE_HTTP3;
|
||||
*err = data->req.bytecount? CURLE_PARTIAL_FILE : CURLE_HTTP3;
|
||||
goto out;
|
||||
}
|
||||
else if(!stream->resp_hds_complete) {
|
||||
|
@ -1112,7 +1052,7 @@ out:
|
|||
|
||||
/* incoming data frames on the h3 stream */
|
||||
static ssize_t cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
|
||||
char *buf, size_t len, CURLcode *err)
|
||||
char *buf, size_t blen, CURLcode *err)
|
||||
{
|
||||
struct cf_ngtcp2_ctx *ctx = cf->ctx;
|
||||
struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
|
||||
|
@ -1121,6 +1061,7 @@ static ssize_t cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
|
|||
struct pkt_io_ctx pktx;
|
||||
|
||||
(void)ctx;
|
||||
(void)buf;
|
||||
|
||||
CF_DATA_SAVE(save, cf, data);
|
||||
DEBUGASSERT(cf->connected);
|
||||
|
@ -1136,46 +1077,18 @@ static ssize_t cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
|
|||
goto out;
|
||||
}
|
||||
|
||||
if(!Curl_bufq_is_empty(&stream->recvbuf)) {
|
||||
nread = Curl_bufq_read(&stream->recvbuf,
|
||||
(unsigned char *)buf, len, err);
|
||||
if(nread < 0) {
|
||||
CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) "
|
||||
"-> %zd, %d", stream->id, len, nread, *err);
|
||||
goto out;
|
||||
}
|
||||
report_consumed_data(cf, data, nread);
|
||||
}
|
||||
|
||||
if(cf_progress_ingress(cf, data, &pktx)) {
|
||||
*err = CURLE_RECV_ERROR;
|
||||
nread = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* recvbuf had nothing before, maybe after progressing ingress? */
|
||||
if(nread < 0 && !Curl_bufq_is_empty(&stream->recvbuf)) {
|
||||
nread = Curl_bufq_read(&stream->recvbuf,
|
||||
(unsigned char *)buf, len, err);
|
||||
if(nread < 0) {
|
||||
CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) "
|
||||
"-> %zd, %d", stream->id, len, nread, *err);
|
||||
goto out;
|
||||
}
|
||||
report_consumed_data(cf, data, nread);
|
||||
}
|
||||
|
||||
if(nread > 0) {
|
||||
h3_drain_stream(cf, data);
|
||||
}
|
||||
else {
|
||||
if(stream->closed) {
|
||||
nread = recv_closed_stream(cf, data, stream, err);
|
||||
goto out;
|
||||
}
|
||||
*err = CURLE_AGAIN;
|
||||
nread = -1;
|
||||
if(stream->closed) {
|
||||
nread = recv_closed_stream(cf, data, stream, err);
|
||||
goto out;
|
||||
}
|
||||
*err = CURLE_AGAIN;
|
||||
nread = -1;
|
||||
|
||||
out:
|
||||
if(cf_progress_egress(cf, data, &pktx)) {
|
||||
|
@ -1189,8 +1102,8 @@ out:
|
|||
nread = -1;
|
||||
}
|
||||
}
|
||||
CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv(len=%zu) -> %zd, %d",
|
||||
stream? stream->id : -1, len, nread, *err);
|
||||
CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv(blen=%zu) -> %zd, %d",
|
||||
stream? stream->id : -1, blen, nread, *err);
|
||||
CF_DATA_RESTORE(cf, save);
|
||||
return nread;
|
||||
}
|
||||
|
@ -1593,7 +1506,6 @@ static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
|
|||
struct cf_ngtcp2_ctx *ctx = cf->ctx;
|
||||
struct pkt_io_ctx local_pktx;
|
||||
size_t pkts_chunk = 128, i;
|
||||
size_t pkts_max = 10 * pkts_chunk;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
if(!pktx) {
|
||||
|
@ -1608,17 +1520,13 @@ static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
|
|||
if(result)
|
||||
return result;
|
||||
|
||||
for(i = 0; i < pkts_max; i += pkts_chunk) {
|
||||
for(i = 0; i < 4; ++i) {
|
||||
if(i)
|
||||
pktx_update_time(pktx, cf);
|
||||
pktx->pkt_count = 0;
|
||||
result = vquic_recv_packets(cf, data, &ctx->q, pkts_chunk,
|
||||
recv_pkt, pktx);
|
||||
if(result) /* error */
|
||||
break;
|
||||
if(pktx->pkt_count < pkts_chunk) /* got less than we could */
|
||||
break;
|
||||
/* give egress a chance before we receive more */
|
||||
result = cf_progress_egress(cf, data, pktx);
|
||||
if(result) /* error */
|
||||
if(result || !pktx->pkt_count) /* error or got nothing */
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
|
@ -1769,7 +1677,7 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf,
|
|||
}
|
||||
|
||||
/* In UDP, there is a maximum theoretical packet paload length and
|
||||
* a minimum payload length that is "guarantueed" to work.
|
||||
* a minimum payload length that is "guaranteed" to work.
|
||||
* To detect if this minimum payload can be increased, ngtcp2 sends
|
||||
* now and then a packet payload larger than the minimum. It that
|
||||
* is ACKed by the peer, both parties know that it works and
|
||||
|
@ -1857,9 +1765,9 @@ out:
|
|||
static bool cf_ngtcp2_data_pending(struct Curl_cfilter *cf,
|
||||
const struct Curl_easy *data)
|
||||
{
|
||||
const struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
|
||||
(void)cf;
|
||||
return stream && !Curl_bufq_is_empty(&stream->recvbuf);
|
||||
(void)data;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static CURLcode h3_data_pause(struct Curl_cfilter *cf,
|
||||
|
@ -2070,8 +1978,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
|
|||
if(result)
|
||||
return result;
|
||||
|
||||
Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd,
|
||||
&sockaddr, NULL, NULL, NULL, NULL);
|
||||
Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, &sockaddr, NULL);
|
||||
if(!sockaddr)
|
||||
return CURLE_QUIC_CONNECT_ERROR;
|
||||
ctx->q.local_addrlen = sizeof(ctx->q.local_addr);
|
||||
|
@ -2186,13 +2093,11 @@ out:
|
|||
|
||||
#ifndef CURL_DISABLE_VERBOSE_STRINGS
|
||||
if(result) {
|
||||
const char *r_ip = NULL;
|
||||
int r_port = 0;
|
||||
struct ip_quadruple ip;
|
||||
|
||||
Curl_cf_socket_peek(cf->next, data, NULL, NULL,
|
||||
&r_ip, &r_port, NULL, NULL);
|
||||
Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip);
|
||||
infof(data, "QUIC connect to %s port %u failed: %s",
|
||||
r_ip, r_port, curl_easy_strerror(result));
|
||||
ip.remote_ip, ip.remote_port, curl_easy_strerror(result));
|
||||
}
|
||||
#endif
|
||||
if(!result && ctx->qconn) {
|
||||
|
|
|
@ -67,7 +67,7 @@
|
|||
* Chunk size is large enough to take a full DATA frame */
|
||||
#define H3_STREAM_WINDOW_SIZE (128 * 1024)
|
||||
#define H3_STREAM_CHUNK_SIZE (16 * 1024)
|
||||
/* The pool keeps spares around and half of a full stream windows
|
||||
/* The pool keeps spares around and half of a full stream window
|
||||
* seems good. More does not seem to improve performance.
|
||||
* The benefit of the pool is that stream buffer to not keep
|
||||
* spares. So memory consumption goes down when streams run empty,
|
||||
|
@ -100,7 +100,7 @@ typedef unsigned long sslerr_t;
|
|||
static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data);
|
||||
|
||||
static const char *SSL_ERROR_to_str(int err)
|
||||
static const char *osslq_SSL_ERROR_to_str(int err)
|
||||
{
|
||||
switch(err) {
|
||||
case SSL_ERROR_NONE:
|
||||
|
@ -139,7 +139,7 @@ static const char *SSL_ERROR_to_str(int err)
|
|||
}
|
||||
|
||||
/* Return error string for last OpenSSL error */
|
||||
static char *ossl_strerror(unsigned long error, char *buf, size_t size)
|
||||
static char *osslq_strerror(unsigned long error, char *buf, size_t size)
|
||||
{
|
||||
DEBUGASSERT(size);
|
||||
*buf = '\0';
|
||||
|
@ -381,8 +381,8 @@ static CURLcode cf_osslq_h3conn_add_stream(struct cf_osslq_h3conn *h3,
|
|||
}
|
||||
|
||||
static CURLcode cf_osslq_ssl_err(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
int detail, CURLcode def_result)
|
||||
struct Curl_easy *data,
|
||||
int detail, CURLcode def_result)
|
||||
{
|
||||
struct cf_osslq_ctx *ctx = cf->ctx;
|
||||
CURLcode result = def_result;
|
||||
|
@ -421,17 +421,17 @@ static CURLcode cf_osslq_ssl_err(struct Curl_cfilter *cf,
|
|||
/* If client certificate is required, communicate the
|
||||
error to client */
|
||||
result = CURLE_SSL_CLIENTCERT;
|
||||
ossl_strerror(errdetail, ebuf, sizeof(ebuf));
|
||||
osslq_strerror(errdetail, ebuf, sizeof(ebuf));
|
||||
}
|
||||
#endif
|
||||
else if((lib == ERR_LIB_SSL) && (reason == SSL_R_PROTOCOL_IS_SHUTDOWN)) {
|
||||
ctx->protocol_shutdown = TRUE;
|
||||
err_descr = "QUIC connectin has been shut down";
|
||||
err_descr = "QUIC connection has been shut down";
|
||||
result = def_result;
|
||||
}
|
||||
else {
|
||||
result = def_result;
|
||||
ossl_strerror(errdetail, ebuf, sizeof(ebuf));
|
||||
osslq_strerror(errdetail, ebuf, sizeof(ebuf));
|
||||
}
|
||||
|
||||
/* detail is already set to the SSL error above */
|
||||
|
@ -443,16 +443,14 @@ static CURLcode cf_osslq_ssl_err(struct Curl_cfilter *cf,
|
|||
if(CURLE_SSL_CONNECT_ERROR == result && errdetail == 0) {
|
||||
char extramsg[80]="";
|
||||
int sockerr = SOCKERRNO;
|
||||
const char *r_ip = NULL;
|
||||
int r_port = 0;
|
||||
struct ip_quadruple ip;
|
||||
|
||||
Curl_cf_socket_peek(cf->next, data, NULL, NULL,
|
||||
&r_ip, &r_port, NULL, NULL);
|
||||
Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip);
|
||||
if(sockerr && detail == SSL_ERROR_SYSCALL)
|
||||
Curl_strerror(sockerr, extramsg, sizeof(extramsg));
|
||||
failf(data, "QUIC connect: %s in connection to %s:%d (%s)",
|
||||
extramsg[0] ? extramsg : SSL_ERROR_to_str(detail),
|
||||
ctx->peer.dispname, r_port, r_ip);
|
||||
extramsg[0] ? extramsg : osslq_SSL_ERROR_to_str(detail),
|
||||
ctx->peer.dispname, ip.remote_port, ip.remote_ip);
|
||||
}
|
||||
else {
|
||||
/* Could be a CERT problem */
|
||||
|
@ -976,7 +974,7 @@ static nghttp3_callbacks ngh3_callbacks = {
|
|||
};
|
||||
|
||||
static CURLcode cf_osslq_h3conn_init(struct cf_osslq_ctx *ctx, SSL *conn,
|
||||
void *user_data)
|
||||
void *user_data)
|
||||
{
|
||||
struct cf_osslq_h3conn *h3 = &ctx->h3;
|
||||
CURLcode result;
|
||||
|
@ -1039,7 +1037,6 @@ static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf,
|
|||
CURLcode result;
|
||||
int rv;
|
||||
const struct Curl_sockaddr_ex *peer_addr = NULL;
|
||||
int peer_port;
|
||||
BIO *bio = NULL;
|
||||
BIO_ADDR *baddr = NULL;
|
||||
|
||||
|
@ -1061,8 +1058,7 @@ static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf,
|
|||
goto out;
|
||||
|
||||
result = CURLE_QUIC_CONNECT_ERROR;
|
||||
Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd,
|
||||
&peer_addr, NULL, &peer_port, NULL, NULL);
|
||||
Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, &peer_addr, NULL);
|
||||
if(!peer_addr)
|
||||
goto out;
|
||||
|
||||
|
@ -1078,7 +1074,20 @@ static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf,
|
|||
goto out;
|
||||
}
|
||||
|
||||
/* Type conversions, see #12861: OpenSSL wants an `int`, but on 64-bit
|
||||
* Win32 systems, Microsoft defines SOCKET as `unsigned long long`.
|
||||
*/
|
||||
#if defined(_WIN32) && !defined(__LWIP_OPT_H__) && !defined(LWIP_HDR_OPT_H)
|
||||
if(ctx->q.sockfd > INT_MAX) {
|
||||
failf(data, "Windows socket identifier larger than MAX_INT, "
|
||||
"unable to set in OpenSSL dgram API.");
|
||||
result = CURLE_QUIC_CONNECT_ERROR;
|
||||
goto out;
|
||||
}
|
||||
bio = BIO_new_dgram((int)ctx->q.sockfd, BIO_NOCLOSE);
|
||||
#else
|
||||
bio = BIO_new_dgram(ctx->q.sockfd, BIO_NOCLOSE);
|
||||
#endif
|
||||
if(!bio) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto out;
|
||||
|
@ -1095,6 +1104,16 @@ static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf,
|
|||
goto out;
|
||||
}
|
||||
|
||||
#ifdef SSL_VALUE_QUIC_IDLE_TIMEOUT
|
||||
/* Added in OpenSSL v3.3.x */
|
||||
if(!SSL_set_feature_request_uint(ctx->tls.ssl, SSL_VALUE_QUIC_IDLE_TIMEOUT,
|
||||
CURL_QUIC_MAX_IDLE_MS)) {
|
||||
CURL_TRC_CF(data, cf, "error setting idle timeout, ");
|
||||
result = CURLE_FAILED_INIT;
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
|
||||
SSL_set_bio(ctx->tls.ssl, bio, bio);
|
||||
bio = NULL;
|
||||
SSL_set_connect_state(ctx->tls.ssl);
|
||||
|
@ -1146,7 +1165,7 @@ static ssize_t h3_quic_recv(void *reader_ctx,
|
|||
SSL_get_stream_read_error_code(x->s->ssl, &app_error_code);
|
||||
CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] h3_quic_recv -> RESET, "
|
||||
"rv=%d, app_err=%" PRIu64,
|
||||
x->s->id, rv, app_error_code);
|
||||
x->s->id, rv, app_error_code);
|
||||
if(app_error_code != NGHTTP3_H3_NO_ERROR) {
|
||||
x->s->reset = TRUE;
|
||||
}
|
||||
|
@ -1361,7 +1380,7 @@ static CURLcode h3_send_streams(struct Curl_cfilter *cf,
|
|||
size_t written;
|
||||
int eos, ok, rv;
|
||||
size_t total_len, acked_len = 0;
|
||||
bool blocked = FALSE;
|
||||
bool blocked = FALSE, eos_written = FALSE;
|
||||
|
||||
n = nghttp3_conn_writev_stream(ctx->h3.conn, &stream_id, &eos,
|
||||
vec, ARRAYSIZE(vec));
|
||||
|
@ -1392,9 +1411,19 @@ static CURLcode h3_send_streams(struct Curl_cfilter *cf,
|
|||
for(i = 0; (i < n) && !blocked; ++i) {
|
||||
/* Without stream->s.ssl, we closed that already, so
|
||||
* pretend the write did succeed. */
|
||||
#ifdef SSL_WRITE_FLAG_CONCLUDE
|
||||
/* Since OpenSSL v3.3.x, on last chunk set EOS if needed */
|
||||
uint64_t flags = (eos && ((i + 1) == n))? SSL_WRITE_FLAG_CONCLUDE : 0;
|
||||
written = vec[i].len;
|
||||
ok = !s->ssl || SSL_write_ex2(s->ssl, vec[i].base, vec[i].len, flags,
|
||||
&written);
|
||||
if(ok && flags & SSL_WRITE_FLAG_CONCLUDE)
|
||||
eos_written = TRUE;
|
||||
#else
|
||||
written = vec[i].len;
|
||||
ok = !s->ssl || SSL_write_ex(s->ssl, vec[i].base, vec[i].len,
|
||||
&written);
|
||||
#endif
|
||||
if(ok) {
|
||||
/* As OpenSSL buffers the data, we count this as acknowledged
|
||||
* from nghttp3's point of view */
|
||||
|
@ -1409,7 +1438,7 @@ static CURLcode h3_send_streams(struct Curl_cfilter *cf,
|
|||
case SSL_ERROR_WANT_READ:
|
||||
/* QUIC blocked us from writing more */
|
||||
CURL_TRC_CF(data, cf, "[%"PRId64"] send %zu bytes to QUIC blocked",
|
||||
s->id, vec[i].len);
|
||||
s->id, vec[i].len);
|
||||
written = 0;
|
||||
nghttp3_conn_block_stream(ctx->h3.conn, s->id);
|
||||
s->send_blocked = blocked = TRUE;
|
||||
|
@ -1426,6 +1455,7 @@ static CURLcode h3_send_streams(struct Curl_cfilter *cf,
|
|||
if(acked_len > 0 || (eos && !s->send_blocked)) {
|
||||
/* Since QUIC buffers the data written internally, we can tell
|
||||
* nghttp3 that it can move forward on it */
|
||||
ctx->q.last_io = Curl_now();
|
||||
rv = nghttp3_conn_add_write_offset(ctx->h3.conn, s->id, acked_len);
|
||||
if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
|
||||
failf(data, "nghttp3_conn_add_write_offset returned error: %s\n",
|
||||
|
@ -1444,7 +1474,7 @@ static CURLcode h3_send_streams(struct Curl_cfilter *cf,
|
|||
"to QUIC, eos=%d", s->id, acked_len, total_len, eos);
|
||||
}
|
||||
|
||||
if(eos && !s->send_blocked) {
|
||||
if(eos && !s->send_blocked && !eos_written) {
|
||||
/* wrote everything and H3 indicates end of stream */
|
||||
CURL_TRC_CF(data, cf, "[%" PRId64 "] closing QUIC stream", s->id);
|
||||
SSL_stream_conclude(s->ssl, 0);
|
||||
|
@ -1569,6 +1599,7 @@ static CURLcode cf_osslq_connect(struct Curl_cfilter *cf,
|
|||
if(err == 1) {
|
||||
/* connected */
|
||||
ctx->handshake_at = now;
|
||||
ctx->q.last_io = now;
|
||||
CURL_TRC_CF(data, cf, "handshake complete after %dms",
|
||||
(int)Curl_timediff(now, ctx->started_at));
|
||||
result = cf_osslq_verify_peer(cf, data);
|
||||
|
@ -1584,15 +1615,18 @@ static CURLcode cf_osslq_connect(struct Curl_cfilter *cf,
|
|||
int detail = SSL_get_error(ctx->tls.ssl, err);
|
||||
switch(detail) {
|
||||
case SSL_ERROR_WANT_READ:
|
||||
ctx->q.last_io = now;
|
||||
CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_RECV");
|
||||
result = Curl_vquic_tls_before_recv(&ctx->tls, cf, data);
|
||||
goto out;
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
ctx->q.last_io = now;
|
||||
CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_SEND");
|
||||
result = CURLE_OK;
|
||||
goto out;
|
||||
#ifdef SSL_ERROR_WANT_ASYNC
|
||||
case SSL_ERROR_WANT_ASYNC:
|
||||
ctx->q.last_io = now;
|
||||
CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_ASYNC");
|
||||
result = CURLE_OK;
|
||||
goto out;
|
||||
|
@ -1619,13 +1653,11 @@ out:
|
|||
|
||||
#ifndef CURL_DISABLE_VERBOSE_STRINGS
|
||||
if(result) {
|
||||
const char *r_ip = NULL;
|
||||
int r_port = 0;
|
||||
struct ip_quadruple ip;
|
||||
|
||||
Curl_cf_socket_peek(cf->next, data, NULL, NULL,
|
||||
&r_ip, &r_port, NULL, NULL);
|
||||
Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip);
|
||||
infof(data, "QUIC connect to %s port %u failed: %s",
|
||||
r_ip, r_port, curl_easy_strerror(result));
|
||||
ip.remote_ip, ip.remote_port, curl_easy_strerror(result));
|
||||
}
|
||||
#endif
|
||||
if(!result)
|
||||
|
@ -1885,7 +1917,7 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf,
|
|||
if(stream->reset) {
|
||||
failf(data,
|
||||
"HTTP/3 stream %" PRId64 " reset by server", stream->s.id);
|
||||
*err = stream->resp_hds_complete? CURLE_PARTIAL_FILE : CURLE_HTTP3;
|
||||
*err = data->req.bytecount? CURLE_PARTIAL_FILE : CURLE_HTTP3;
|
||||
goto out;
|
||||
}
|
||||
else if(!stream->resp_hds_complete) {
|
||||
|
@ -2055,7 +2087,24 @@ static bool cf_osslq_conn_is_alive(struct Curl_cfilter *cf,
|
|||
if(!ctx->tls.ssl)
|
||||
goto out;
|
||||
|
||||
/* TODO: how to check negotiated connection idle time? */
|
||||
#ifdef SSL_VALUE_QUIC_IDLE_TIMEOUT
|
||||
/* Added in OpenSSL v3.3.x */
|
||||
{
|
||||
timediff_t idletime;
|
||||
uint64_t idle_ms = ctx->max_idle_ms;
|
||||
if(!SSL_get_value_uint(ctx->tls.ssl, SSL_VALUE_CLASS_FEATURE_NEGOTIATED,
|
||||
SSL_VALUE_QUIC_IDLE_TIMEOUT, &idle_ms)) {
|
||||
CURL_TRC_CF(data, cf, "error getting negotiated idle timeout, "
|
||||
"assume connection is dead.");
|
||||
goto out;
|
||||
}
|
||||
CURL_TRC_CF(data, cf, "negotiated idle timeout: %zums", (size_t)idle_ms);
|
||||
idletime = Curl_timediff(Curl_now(), ctx->q.last_io);
|
||||
if(idletime > 0 && (uint64_t)idletime > idle_ms)
|
||||
goto out;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending))
|
||||
goto out;
|
||||
|
@ -2111,15 +2160,24 @@ static CURLcode cf_osslq_query(struct Curl_cfilter *cf,
|
|||
int query, int *pres1, void *pres2)
|
||||
{
|
||||
struct cf_osslq_ctx *ctx = cf->ctx;
|
||||
struct cf_call_data save;
|
||||
|
||||
switch(query) {
|
||||
case CF_QUERY_MAX_CONCURRENT: {
|
||||
/* TODO: how to get this? */
|
||||
CF_DATA_SAVE(save, cf, data);
|
||||
#ifdef SSL_VALUE_QUIC_STREAM_BIDI_LOCAL_AVAIL
|
||||
/* Added in OpenSSL v3.3.x */
|
||||
uint64_t v;
|
||||
if(!SSL_get_value_uint(ctx->tls.ssl, SSL_VALUE_CLASS_GENERIC,
|
||||
SSL_VALUE_QUIC_STREAM_BIDI_LOCAL_AVAIL, &v)) {
|
||||
CURL_TRC_CF(data, cf, "error getting available local bidi streams");
|
||||
return CURLE_HTTP3;
|
||||
}
|
||||
/* we report avail + in_use */
|
||||
v += CONN_INUSE(cf->conn);
|
||||
*pres1 = (v > INT_MAX)? INT_MAX : (int)v;
|
||||
#else
|
||||
*pres1 = 100;
|
||||
#endif
|
||||
CURL_TRC_CF(data, cf, "query max_conncurrent -> %d", *pres1);
|
||||
CF_DATA_RESTORE(cf, save);
|
||||
return CURLE_OK;
|
||||
}
|
||||
case CF_QUERY_CONNECT_REPLY_MS:
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue