Jelajahi Sumber

curl 2024-03-27 (de7b3e89)

Code extracted from:

    https://github.com/curl/curl.git

at commit de7b3e89218467159a7af72d58cea8425946e97d (curl-8_7_1).
Curl Upstream 1 tahun lalu
induk
melakukan
e17d8d0c3b
100 mengubah file dengan 6129 tambahan dan 3669 penghapusan
  1. 0 29
      CMake/Macros.cmake
  2. 1 1
      CMake/Utilities.cmake
  3. 66 12
      CMakeLists.txt
  4. 2 1
      include/curl/curl.h
  5. 3 3
      include/curl/curlver.h
  6. 6 0
      lib/Makefile.inc
  7. 5 13
      lib/altsvc.c
  8. 9 9
      lib/asyn-ares.c
  9. 1 1
      lib/asyn-thread.c
  10. 22 1
      lib/bufq.c
  11. 7 0
      lib/bufq.h
  12. 147 173
      lib/c-hyper.c
  13. 6 2
      lib/c-hyper.h
  14. 22 17
      lib/cf-h1-proxy.c
  15. 10 1
      lib/cf-h2-proxy.c
  16. 14 9
      lib/cf-haproxy.c
  17. 40 53
      lib/cf-socket.c
  18. 3 6
      lib/cf-socket.h
  19. 67 7
      lib/cfilters.c
  20. 29 5
      lib/cfilters.h
  21. 1 7
      lib/conncache.c
  22. 18 11
      lib/connect.c
  23. 2 1
      lib/connect.h
  24. 10 23
      lib/cookie.c
  25. 3 0
      lib/curl_config.h.cmake
  26. 1 1
      lib/curl_des.c
  27. 23 32
      lib/curl_get_line.c
  28. 4 3
      lib/curl_get_line.h
  29. 1 1
      lib/curl_ntlm_wb.c
  30. 2 2
      lib/curl_rtmp.c
  31. 18 0
      lib/curl_setup.h
  32. 844 0
      lib/curl_sha512_256.c
  33. 44 0
      lib/curl_sha512_256.h
  34. 30 5
      lib/curl_trc.c
  35. 15 3
      lib/curl_trc.h
  36. 437 0
      lib/cw-out.c
  37. 53 0
      lib/cw-out.h
  38. 11 14
      lib/dict.c
  39. 22 11
      lib/doh.c
  40. 2 0
      lib/doh.h
  41. 27 43
      lib/easy.c
  42. 1 1
      lib/easygetopt.c
  43. 1 1
      lib/easyif.h
  44. 1 1
      lib/easyoptions.c
  45. 32 20
      lib/file.c
  46. 5 0
      lib/fopen.c
  47. 173 45
      lib/ftp.c
  48. 17 9
      lib/getinfo.c
  49. 5 5
      lib/gopher.c
  50. 58 3
      lib/headers.c
  51. 7 0
      lib/headers.h
  52. 1 1
      lib/hostip.c
  53. 5 12
      lib/hsts.c
  54. 302 798
      lib/http.c
  55. 14 45
      lib/http.h
  56. 137 192
      lib/http2.c
  57. 216 5
      lib/http_chunks.c
  58. 7 0
      lib/http_chunks.h
  59. 18 18
      lib/imap.c
  60. 14 11
      lib/krb5.c
  61. 8 8
      lib/ldap.c
  62. 1 0
      lib/md4.c
  63. 222 5
      lib/mime.c
  64. 7 6
      lib/mime.h
  65. 4 13
      lib/mprintf.c
  66. 7 10
      lib/mqtt.c
  67. 153 111
      lib/multi.c
  68. 9 0
      lib/multihandle.h
  69. 49 0
      lib/multiif.h
  70. 7 3
      lib/netrc.c
  71. 1 1
      lib/openldap.c
  72. 24 15
      lib/pingpong.c
  73. 1 1
      lib/pingpong.h
  74. 2 3
      lib/pop3.c
  75. 409 0
      lib/request.c
  76. 227 0
      lib/request.h
  77. 64 57
      lib/rtsp.c
  78. 958 437
      lib/sendf.c
  79. 227 23
      lib/sendf.h
  80. 10 14
      lib/setopt.c
  81. 26 25
      lib/smb.c
  82. 1 0
      lib/smb.h
  83. 185 165
      lib/smtp.c
  84. 0 13
      lib/smtp.h
  85. 2 2
      lib/socks.c
  86. 1 1
      lib/socks_gssapi.c
  87. 8 13
      lib/strtoofft.c
  88. 13 9
      lib/telnet.c
  89. 8 7
      lib/tftp.c
  90. 124 589
      lib/transfer.c
  91. 29 10
      lib/transfer.h
  92. 46 78
      lib/url.c
  93. 3 3
      lib/url.h
  94. 2 2
      lib/urlapi.c
  95. 58 172
      lib/urldata.h
  96. 52 15
      lib/vauth/digest.c
  97. 6 0
      lib/version.c
  98. 0 21
      lib/vquic/curl_msh3.c
  99. 42 137
      lib/vquic/curl_ngtcp2.c
  100. 91 33
      lib/vquic/curl_osslq.c

+ 0 - 29
CMake/Macros.cmake

@@ -68,35 +68,6 @@ macro(curl_internal_test CURL_TEST)
   endif()
   endif()
 endmacro()
 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)
 macro(optional_dependency DEPENDENCY)
   set(CURL_${DEPENDENCY} AUTO CACHE STRING "Build curl with ${DEPENDENCY} support (AUTO, ON or OFF)")
   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)
   set_property(CACHE CURL_${DEPENDENCY} PROPERTY STRINGS AUTO ON OFF)

+ 1 - 1
CMake/Utilities.cmake

@@ -23,7 +23,7 @@
 ###########################################################################
 ###########################################################################
 # File containing various utilities
 # 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)
 function(count_true output_count_var)
   set(lst_len 0)
   set(lst_len 0)
   foreach(option_var IN LISTS ARGN)
   foreach(option_var IN LISTS ARGN)

+ 66 - 12
CMakeLists.txt

@@ -307,18 +307,14 @@ endif()
 find_package(Perl)
 find_package(Perl)
 
 
 option(BUILD_LIBCURL_DOCS "to build libcurl man pages" ON)
 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(ENABLE_CURL_MANUAL OR BUILD_LIBCURL_DOCS)
   if(PERL_FOUND)
   if(PERL_FOUND)
-    curl_nroff_check()
-    if(NROFF_USEFUL)
-      set(HAVE_MANUAL_TOOLS ON)
-    endif()
+    set(HAVE_MANUAL_TOOLS ON)
   endif()
   endif()
   if(NOT HAVE_MANUAL_TOOLS)
   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()
 endif()
 endif()
 
 
@@ -396,8 +392,7 @@ if(APPLE)
 endif()
 endif()
 if(WIN32)
 if(WIN32)
   cmake_dependent_option(CURL_USE_SCHANNEL "Enable Windows native SSL/TLS" OFF CURL_ENABLE_SSL OFF)
   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()
 endif()
 cmake_dependent_option(CURL_USE_MBEDTLS "Enable mbedTLS for SSL/TLS" OFF CURL_ENABLE_SSL OFF)
 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)
 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})
   list(APPEND CURL_LIBS ${MSH3_LIBRARIES})
 endif()
 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.")
   message(FATAL_ERROR "MultiSSL cannot be enabled with HTTP/3 and vice versa.")
 endif()
 endif()
 
 
@@ -1542,7 +1556,7 @@ if(NOT CURL_DISABLE_INSTALL)
                           NOT CURL_DISABLE_HTTP AND NTLM_WB_ENABLED)
                           NOT CURL_DISABLE_HTTP AND NTLM_WB_ENABLED)
   _add_if("TLS-SRP"       USE_TLS_SRP)
   _add_if("TLS-SRP"       USE_TLS_SRP)
   _add_if("HTTP2"         USE_NGHTTP2)
   _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)
   _add_if("MultiSSL"      CURL_WITH_MULTI_SSL)
   # TODO wolfSSL only support this from v5.0.0 onwards
   # TODO wolfSSL only support this from v5.0.0 onwards
   _add_if("HTTPS-proxy"   SSL_ENABLED AND (USE_OPENSSL OR USE_GNUTLS
   _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(LDFLAGS                 "${CMAKE_SHARED_LINKER_FLAGS}")
   set(LIBCURL_LIBS            "")
   set(LIBCURL_LIBS            "")
   set(libdir                  "${CMAKE_INSTALL_PREFIX}/lib")
   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})
   foreach(_lib ${CMAKE_C_IMPLICIT_LINK_LIBRARIES} ${CURL_LIBS})
     if(TARGET "${_lib}")
     if(TARGET "${_lib}")
       set(_libname "${_lib}")
       set(_libname "${_lib}")
@@ -1642,8 +1680,24 @@ if(NOT CURL_DISABLE_INSTALL)
         continue()
         continue()
       endif()
       endif()
     endif()
     endif()
-    if(_lib MATCHES ".*/.*" OR _lib MATCHES "^-")
+    if(_lib MATCHES "^-")
       set(LIBCURL_LIBS          "${LIBCURL_LIBS} ${_lib}")
       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()
     else()
       set(LIBCURL_LIBS          "${LIBCURL_LIBS} -l${_lib}")
       set(LIBCURL_LIBS          "${LIBCURL_LIBS} -l${_lib}")
     endif()
     endif()

+ 2 - 1
include/curl/curl.h

@@ -2938,7 +2938,8 @@ typedef enum {
   CURLINFO_XFER_ID          = CURLINFO_OFF_T + 63,
   CURLINFO_XFER_ID          = CURLINFO_OFF_T + 63,
   CURLINFO_CONN_ID          = CURLINFO_OFF_T + 64,
   CURLINFO_CONN_ID          = CURLINFO_OFF_T + 64,
   CURLINFO_QUEUE_TIME_T     = CURLINFO_OFF_T + 65,
   CURLINFO_QUEUE_TIME_T     = CURLINFO_OFF_T + 65,
-  CURLINFO_LASTONE          = 65
+  CURLINFO_USED_PROXY       = CURLINFO_LONG + 66,
+  CURLINFO_LASTONE          = 66
 } CURLINFO;
 } CURLINFO;
 
 
 /* CURLINFO_RESPONSE_CODE is the new name for the option previously known as
 /* CURLINFO_RESPONSE_CODE is the new name for the option previously known as

+ 3 - 3
include/curl/curlver.h

@@ -32,12 +32,12 @@
 
 
 /* This is the version number of the libcurl package from which this header
 /* This is the version number of the libcurl package from which this header
    file origins: */
    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
 /* The numeric version number is also available "in parts" by using these
    defines: */
    defines: */
 #define LIBCURL_VERSION_MAJOR 8
 #define LIBCURL_VERSION_MAJOR 8
-#define LIBCURL_VERSION_MINOR 6
+#define LIBCURL_VERSION_MINOR 7
 #define LIBCURL_VERSION_PATCH 0
 #define LIBCURL_VERSION_PATCH 0
 
 
 /* This is the numeric version of the libcurl version number, meant for easier
 /* 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
    CURL_VERSION_BITS() macro since curl's own configure script greps for it
    and needs it to contain the full number.
    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
  * This is the date and time when the full source package was created. The

+ 6 - 0
lib/Makefile.inc

@@ -134,9 +134,11 @@ LIB_CFILES =         \
   curl_range.c       \
   curl_range.c       \
   curl_rtmp.c        \
   curl_rtmp.c        \
   curl_sasl.c        \
   curl_sasl.c        \
+  curl_sha512_256.c  \
   curl_sspi.c        \
   curl_sspi.c        \
   curl_threads.c     \
   curl_threads.c     \
   curl_trc.c         \
   curl_trc.c         \
+  cw-out.c           \
   dict.c             \
   dict.c             \
   doh.c              \
   doh.c              \
   dynbuf.c           \
   dynbuf.c           \
@@ -199,6 +201,7 @@ LIB_CFILES =         \
   psl.c              \
   psl.c              \
   rand.c             \
   rand.c             \
   rename.c           \
   rename.c           \
+  request.c          \
   rtsp.c             \
   rtsp.c             \
   select.c           \
   select.c           \
   sendf.c            \
   sendf.c            \
@@ -277,10 +280,12 @@ LIB_HFILES =         \
   curl_setup.h       \
   curl_setup.h       \
   curl_setup_once.h  \
   curl_setup_once.h  \
   curl_sha256.h      \
   curl_sha256.h      \
+  curl_sha512_256.h  \
   curl_sspi.h        \
   curl_sspi.h        \
   curl_threads.h     \
   curl_threads.h     \
   curl_trc.h         \
   curl_trc.h         \
   curlx.h            \
   curlx.h            \
+  cw-out.h           \
   dict.h             \
   dict.h             \
   doh.h              \
   doh.h              \
   dynbuf.h           \
   dynbuf.h           \
@@ -333,6 +338,7 @@ LIB_HFILES =         \
   psl.h              \
   psl.h              \
   rand.h             \
   rand.h             \
   rename.h           \
   rename.h           \
+  request.h          \
   rtsp.h             \
   rtsp.h             \
   select.h           \
   select.h           \
   sendf.h            \
   sendf.h            \

+ 5 - 13
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)
 static CURLcode altsvc_load(struct altsvcinfo *asi, const char *file)
 {
 {
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
-  char *line = NULL;
   FILE *fp;
   FILE *fp;
 
 
   /* we need a private copy of the file name so that the altsvc cache file
   /* 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);
   fp = fopen(file, FOPEN_READTEXT);
   if(fp) {
   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))
       while(*lineptr && ISBLANK(*lineptr))
         lineptr++;
         lineptr++;
       if(*lineptr == '#')
       if(*lineptr == '#')
@@ -234,16 +232,10 @@ static CURLcode altsvc_load(struct altsvcinfo *asi, const char *file)
 
 
       altsvc_add(asi, lineptr);
       altsvc_add(asi, lineptr);
     }
     }
-    free(line); /* free the line buffer */
+    Curl_dyn_free(&buf); /* free the line buffer */
     fclose(fp);
     fclose(fp);
   }
   }
   return result;
   return result;
-
-fail:
-  Curl_safefree(asi->filename);
-  free(line);
-  fclose(fp);
-  return CURLE_OUT_OF_MEMORY;
 }
 }
 
 
 /*
 /*

+ 9 - 9
lib/asyn-ares.c

@@ -122,6 +122,8 @@ struct thread_data {
 
 
 #define CARES_TIMEOUT_PER_ATTEMPT 2000
 #define CARES_TIMEOUT_PER_ATTEMPT 2000
 
 
+static int ares_ver = 0;
+
 /*
 /*
  * Curl_resolver_global_init() - the generic low-level asynchronous name
  * Curl_resolver_global_init() - the generic low-level asynchronous name
  * resolve API.  Called from curl_global_init() to initialize global resolver
  * 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;
     return CURLE_FAILED_INIT;
   }
   }
 #endif
 #endif
+  ares_version(&ares_ver);
   return CURLE_OK;
   return CURLE_OK;
 }
 }
 
 
@@ -173,16 +176,8 @@ CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver)
   int status;
   int status;
   struct ares_options options;
   struct ares_options options;
   int optmask = ARES_OPT_SOCK_STATE_CB;
   int optmask = ARES_OPT_SOCK_STATE_CB;
-  static int ares_ver = 0;
   options.sock_state_cb = sock_state_cb;
   options.sock_state_cb = sock_state_cb;
   options.sock_state_cb_data = easy;
   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)
      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
      if c-ares >= 1.24.0, user can set the timeout via /etc/resolv.conf to
      overwrite c-ares' timeout.
      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);
   status = ares_init_options((ares_channel*)resolver, &options, optmask);
   if(status != ARES_SUCCESS) {
   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
   /* If server is NULL or empty, this would purge all DNS servers
    * from ares library, which will cause any and all queries to fail.
    * 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
    * 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).
    * it gets from the OS (for instance from /etc/resolv.conf on Linux).
    */
    */
   if(!(servers && servers[0]))
   if(!(servers && servers[0]))

+ 1 - 1
lib/asyn-thread.c

@@ -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
      * before the FD is invalidated to avoid EBADF on EPOLL_CTL_DEL
      */
      */
     Curl_multi_closed(data, sock_rd);
     Curl_multi_closed(data, sock_rd);
-    sclose(sock_rd);
+    wakeup_close(sock_rd);
 #endif
 #endif
   }
   }
   async->tdata = NULL;
   async->tdata = NULL;

+ 22 - 1
lib/bufq.c

@@ -396,7 +396,7 @@ ssize_t Curl_bufq_write(struct bufq *q,
   while(len) {
   while(len) {
     tail = get_non_full_tail(q);
     tail = get_non_full_tail(q);
     if(!tail) {
     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;
         *err = CURLE_OUT_OF_MEMORY;
         return -1;
         return -1;
       }
       }
@@ -417,6 +417,17 @@ ssize_t Curl_bufq_write(struct bufq *q,
   return nwritten;
   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,
 ssize_t Curl_bufq_read(struct bufq *q, unsigned char *buf, size_t len,
                        CURLcode *err)
                        CURLcode *err)
 {
 {
@@ -440,6 +451,16 @@ ssize_t Curl_bufq_read(struct bufq *q, unsigned char *buf, size_t len,
   return nread;
   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,
 bool Curl_bufq_peek(struct bufq *q,
                     const unsigned char **pbuf, size_t *plen)
                     const unsigned char **pbuf, size_t *plen)
 {
 {

+ 7 - 0
lib/bufq.h

@@ -178,6 +178,10 @@ ssize_t Curl_bufq_write(struct bufq *q,
                         const unsigned char *buf, size_t len,
                         const unsigned char *buf, size_t len,
                         CURLcode *err);
                         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
  * Read buf from the start of the buffer queue. The buf is copied
  * and the amount of copied bytes is returned.
  * 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,
 ssize_t Curl_bufq_read(struct bufq *q, unsigned char *buf, size_t len,
                         CURLcode *err);
                         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
  * 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
  * the chunk buf (at the current offset) and its length. Does not

+ 147 - 173
lib/c-hyper.c

@@ -53,7 +53,9 @@
 
 
 #include <hyper.h>
 #include <hyper.h>
 #include "urldata.h"
 #include "urldata.h"
+#include "cfilters.h"
 #include "sendf.h"
 #include "sendf.h"
+#include "headers.h"
 #include "transfer.h"
 #include "transfer.h"
 #include "multiif.h"
 #include "multiif.h"
 #include "progress.h"
 #include "progress.h"
@@ -65,6 +67,9 @@
 #include "curl_memory.h"
 #include "curl_memory.h"
 #include "memdebug.h"
 #include "memdebug.h"
 
 
+
+static CURLcode cr_hyper_add(struct Curl_easy *data);
+
 typedef enum {
 typedef enum {
     USERDATA_NOT_SET = 0, /* for tasks with no userdata set; must be zero */
     USERDATA_NOT_SET = 0, /* for tasks with no userdata set; must be zero */
     USERDATA_RESP_BODY
     USERDATA_RESP_BODY
@@ -73,7 +78,8 @@ typedef enum {
 size_t Curl_hyper_recv(void *userp, hyper_context *ctx,
 size_t Curl_hyper_recv(void *userp, hyper_context *ctx,
                        uint8_t *buf, size_t buflen)
                        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;
   struct connectdata *conn = data->conn;
   CURLcode result;
   CURLcode result;
   ssize_t nread;
   ssize_t nread;
@@ -81,7 +87,8 @@ size_t Curl_hyper_recv(void *userp, hyper_context *ctx,
   (void)ctx;
   (void)ctx;
 
 
   DEBUGF(infof(data, "Curl_hyper_recv(%zu)", buflen));
   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) {
   if(result == CURLE_AGAIN) {
     /* would block, register interest */
     /* would block, register interest */
     DEBUGF(infof(data, "Curl_hyper_recv(%zu) -> EAGAIN", buflen));
     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,
 size_t Curl_hyper_send(void *userp, hyper_context *ctx,
                        const uint8_t *buf, size_t buflen)
                        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;
   CURLcode result;
-  ssize_t nwrote;
+  size_t nwrote;
 
 
   DEBUGF(infof(data, "Curl_hyper_send(%zu)", buflen));
   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) {
   if(result == CURLE_AGAIN) {
     DEBUGF(infof(data, "Curl_hyper_send(%zu) -> EAGAIN", buflen));
     DEBUGF(infof(data, "Curl_hyper_send(%zu) -> EAGAIN", buflen));
     /* would block, register interest */
     /* would block, register interest */
@@ -152,9 +158,6 @@ static int hyper_each_header(void *userdata,
     return HYPER_ITER_BREAK;
     return HYPER_ITER_BREAK;
   }
   }
 
 
-  if(!data->req.bytecount)
-    Curl_pgrsTime(data, TIMER_STARTTRANSFER);
-
   Curl_dyn_reset(&data->state.headerb);
   Curl_dyn_reset(&data->state.headerb);
   if(name_len) {
   if(name_len) {
     if(Curl_dyn_addf(&data->state.headerb, "%.*s: %.*s\r\n",
     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);
   len = Curl_dyn_len(&data->state.headerb);
   headp = Curl_dyn_ptr(&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) {
   if(result) {
     data->state.hresult = result;
     data->state.hresult = result;
     return HYPER_ITER_BREAK;
     return HYPER_ITER_BREAK;
@@ -204,7 +207,6 @@ static int hyper_body_chunk(void *userdata, const hyper_buf *chunk)
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
 
 
   if(0 == k->bodywrites) {
   if(0 == k->bodywrites) {
-    bool done = FALSE;
 #if defined(USE_NTLM)
 #if defined(USE_NTLM)
     struct connectdata *conn = data->conn;
     struct connectdata *conn = data->conn;
     if(conn->bits.close &&
     if(conn->bits.close &&
@@ -217,27 +219,26 @@ static int hyper_body_chunk(void *userdata, const hyper_buf *chunk)
       Curl_safefree(data->req.newurl);
       Curl_safefree(data->req.newurl);
     }
     }
 #endif
 #endif
-    if(data->state.expect100header) {
-      Curl_expire_done(data, EXPIRE_100_TIMEOUT);
+    if(Curl_http_exp100_is_selected(data)) {
       if(data->req.httpcode < 400) {
       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 */
       else { /* >= 4xx */
-        k->exp100 = EXP100_FAILED;
+        Curl_req_abort_sending(data);
       }
       }
     }
     }
     if(data->state.hconnect && (data->req.httpcode/100 != 2) &&
     if(data->state.hconnect && (data->req.httpcode/100 != 2) &&
        data->state.authproxy.done) {
        data->state.authproxy.done) {
-      done = TRUE;
+      data->req.done = TRUE;
       result = CURLE_OK;
       result = CURLE_OK;
     }
     }
     else
     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");
       infof(data, "Return early from hyper_body_chunk");
       data->state.hresult = result;
       data->state.hresult = result;
       return HYPER_ITER_BREAK;
       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
   /* We need to set 'httpcodeq' for functions that check the response code in
      a single place. */
      a single place. */
   data->req.httpcode = http_status;
   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)
   if(data->state.hconnect)
     /* CONNECT */
     /* CONNECT */
     data->info.httpproxycode = http_status;
     data->info.httpproxycode = http_status;
   else {
   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)
     if(http_version == HYPER_HTTP_VERSION_1_0)
       data->state.httpwant = CURL_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,
 CURLcode Curl_hyper_stream(struct Curl_easy *data,
                            struct connectdata *conn,
                            struct connectdata *conn,
                            int *didwhat,
                            int *didwhat,
-                           bool *done,
                            int select_res)
                            int select_res)
 {
 {
   hyper_response *resp = NULL;
   hyper_response *resp = NULL;
@@ -352,20 +351,9 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data,
   struct SingleRequest *k = &data->req;
   struct SingleRequest *k = &data->req;
   (void)conn;
   (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) {
   if(select_res & CURL_CSELECT_IN) {
@@ -379,7 +367,6 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data,
     h->write_waker = NULL;
     h->write_waker = NULL;
   }
   }
 
 
-  *done = FALSE;
   do {
   do {
     hyper_task_return_type t;
     hyper_task_return_type t;
     task = hyper_executor_poll(h->exec);
     task = hyper_executor_poll(h->exec);
@@ -422,7 +409,7 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data,
           break;
           break;
         }
         }
       }
       }
-      *done = TRUE;
+      data->req.done = TRUE;
       hyper_error_free(hypererr);
       hyper_error_free(hypererr);
       break;
       break;
     }
     }
@@ -431,12 +418,11 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data,
       hyper_task_free(task);
       hyper_task_free(task);
       if((userdata_t)userdata == USERDATA_RESP_BODY) {
       if((userdata_t)userdata == USERDATA_RESP_BODY) {
         /* end of transfer */
         /* end of transfer */
-        *done = TRUE;
+        data->req.done = TRUE;
         infof(data, "hyperstream is done");
         infof(data, "hyperstream is done");
         if(!k->bodywrites) {
         if(!k->bodywrites) {
           /* hyper doesn't always call the body write callback */
           /* 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;
         break;
       }
       }
@@ -462,11 +448,11 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data,
     reasonp = hyper_response_reason_phrase(resp);
     reasonp = hyper_response_reason_phrase(resp);
     reason_len = hyper_response_reason_phrase_len(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");
       infof(data, "Got 417 while waiting for a 100");
       data->state.disableexpect = TRUE;
       data->state.disableexpect = TRUE;
       data->req.newurl = strdup(data->state.url);
       data->req.newurl = strdup(data->state.url);
-      Curl_done_sending(data, k);
+      Curl_req_abort_sending(data);
     }
     }
 
 
     result = status_line(data, conn,
     result = status_line(data, conn,
@@ -654,115 +640,66 @@ static CURLcode request_target(struct Curl_easy *data,
   return result;
   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,
 static int uploadstreamed(void *userdata, hyper_context *ctx,
                           hyper_buf **chunk)
                           hyper_buf **chunk)
 {
 {
   size_t fillcount;
   size_t fillcount;
   struct Curl_easy *data = (struct Curl_easy *)userdata;
   struct Curl_easy *data = (struct Curl_easy *)userdata;
-  struct connectdata *conn = (struct connectdata *)data->conn;
   CURLcode result;
   CURLcode result;
+  char *xfer_ulbuf;
+  size_t xfer_ulblen;
+  bool eos;
+  int rc = HYPER_POLL_ERROR;
   (void)ctx;
   (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)
     if(copy)
       *chunk = copy;
       *chunk = copy;
     else {
     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
     /* increasing the writebytecount here is a little premature but we
        don't know exactly when the body is sent */
        don't know exactly when the body is sent */
     data->req.writebytecount += fillcount;
     data->req.writebytecount += fillcount;
     Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
     Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
+    rc = HYPER_POLL_READY;
+  }
+  else if(eos) {
+    *chunk = NULL;
+    rc = HYPER_POLL_READY;
   }
   }
-  return 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;
   CURLcode result = CURLE_OK;
   struct dynbuf req;
   struct dynbuf req;
   if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD))
   if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD))
@@ -770,34 +707,31 @@ static CURLcode bodysend(struct Curl_easy *data,
   else {
   else {
     hyper_body *body;
     hyper_body *body;
     Curl_dyn_init(&req, DYN_HTTP_REQUEST);
     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));
       result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&req));
+      if(result)
+        return result;
+    }
 
 
     Curl_dyn_free(&req);
     Curl_dyn_free(&req);
 
 
     body = hyper_body_new();
     body = hyper_body_new();
     hyper_body_set_userdata(body, data);
     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)) {
     if(HYPERE_OK != hyper_request_set_body(hyperreq, body)) {
       /* fail */
       /* fail */
       result = CURLE_OUT_OF_MEMORY;
       result = CURLE_OUT_OF_MEMORY;
     }
     }
   }
   }
-  http->sending = HTTPSEND_BODY;
-  return result;
+
+  return cr_hyper_add(data);
 }
 }
 
 
 static CURLcode cookies(struct Curl_easy *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
      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. */
      the rest of the request in the PERFORM phase. */
   *done = TRUE;
   *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");
   infof(data, "Time for the Hyper dance");
   memset(h, 0, sizeof(struct hyptransfer));
   memset(h, 0, sizeof(struct hyptransfer));
@@ -913,9 +856,9 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
       return result;
       return result;
   }
   }
 
 
-  result = Curl_http_resume(data, conn, httpreq);
+  result = Curl_http_req_set_reader(data, httpreq, &te);
   if(result)
   if(result)
-    return result;
+    goto error;
 
 
   result = Curl_http_range(data, httpreq);
   result = Curl_http_range(data, httpreq);
   if(result)
   if(result)
@@ -932,7 +875,9 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
     goto error;
     goto error;
   }
   }
   /* tell Hyper how to read/write network data */
   /* 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_read(io, Curl_hyper_recv);
   hyper_io_set_write(io, Curl_hyper_send);
   hyper_io_set_write(io, Curl_hyper_send);
 
 
@@ -1005,11 +950,6 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
       goto error;
       goto error;
     }
     }
   }
   }
-  else {
-    if(!data->state.disableexpect) {
-      data->state.expect100header = TRUE;
-    }
-  }
 
 
   if(hyper_request_set_method(req, (uint8_t *)method, strlen(method))) {
   if(hyper_request_set_method(req, (uint8_t *)method, strlen(method))) {
     failf(data, "error setting method");
     failf(data, "error setting method");
@@ -1034,10 +974,6 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
     goto error;
     goto error;
   }
   }
 
 
-  result = Curl_http_body(data, conn, httpreq, &te);
-  if(result)
-    goto error;
-
   if(data->state.aptr.host) {
   if(data->state.aptr.host) {
     result = Curl_hyper_header(data, headers, data->state.aptr.host);
     result = Curl_hyper_header(data, headers, data->state.aptr.host);
     if(result)
     if(result)
@@ -1160,13 +1096,13 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
   if(result)
   if(result)
     goto error;
     goto error;
 
 
-  result = bodysend(data, conn, headers, req, httpreq);
+  result = finalize_request(data, headers, req, httpreq);
   if(result)
   if(result)
     goto error;
     goto error;
 
 
   Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"\r\n", 2);
   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;
     data->req.upload_chunky = TRUE;
   }
   }
   else {
   else {
@@ -1193,13 +1129,10 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
   if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) {
   if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) {
     /* HTTP GET/HEAD download */
     /* HTTP GET/HEAD download */
     Curl_pgrsSetUploadSize(data, 0); /* nothing */
     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;
   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
   /* clear userpwd and proxyuserpwd to avoid reusing old credentials
    * from reused connections */
    * from reused connections */
@@ -1241,10 +1174,51 @@ void Curl_hyper_done(struct Curl_easy *data)
     hyper_waker_free(h->write_waker);
     hyper_waker_free(h->write_waker);
     h->write_waker = NULL;
     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) */
 #endif /* !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER) */

+ 6 - 2
lib/c-hyper.h

@@ -29,13 +29,18 @@
 
 
 #include <hyper.h>
 #include <hyper.h>
 
 
+struct hyp_io_ctx {
+  struct Curl_easy *data;
+  int sockindex;
+};
+
 /* per-transfer data for the Hyper backend */
 /* per-transfer data for the Hyper backend */
 struct hyptransfer {
 struct hyptransfer {
   hyper_waker *write_waker;
   hyper_waker *write_waker;
   hyper_waker *read_waker;
   hyper_waker *read_waker;
   const hyper_executor *exec;
   const hyper_executor *exec;
-  hyper_waker *exp100_waker;
   hyper_waker *send_body_waker;
   hyper_waker *send_body_waker;
+  struct hyp_io_ctx io_ctx;
 };
 };
 
 
 size_t Curl_hyper_recv(void *userp, hyper_context *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,
 CURLcode Curl_hyper_stream(struct Curl_easy *data,
                            struct connectdata *conn,
                            struct connectdata *conn,
                            int *didwhat,
                            int *didwhat,
-                           bool *done,
                            int select_res);
                            int select_res);
 
 
 CURLcode Curl_hyper_header(struct Curl_easy *data, hyper_headers *headers,
 CURLcode Curl_hyper_header(struct Curl_easy *data, hyper_headers *headers,

+ 22 - 17
lib/cf-h1-proxy.c

@@ -114,18 +114,12 @@ static CURLcode tunnel_init(struct Curl_cfilter *cf,
                             struct h1_tunnel_state **pts)
                             struct h1_tunnel_state **pts)
 {
 {
   struct h1_tunnel_state *ts;
   struct h1_tunnel_state *ts;
-  CURLcode result;
 
 
   if(cf->conn->handler->flags & PROTOPT_NOTCPPROXY) {
   if(cf->conn->handler->flags & PROTOPT_NOTCPPROXY) {
     failf(data, "%s cannot be done over CONNECT", cf->conn->handler->scheme);
     failf(data, "%s cannot be done over CONNECT", cf->conn->handler->scheme);
     return CURLE_UNSUPPORTED_PROTOCOL;
     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));
   ts = calloc(1, sizeof(*ts));
   if(!ts)
   if(!ts)
     return CURLE_OUT_OF_MEMORY;
     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
 #ifndef USE_HYPER
 static CURLcode start_CONNECT(struct Curl_cfilter *cf,
 static CURLcode start_CONNECT(struct Curl_cfilter *cf,
                               struct Curl_easy *data,
                               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;
   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);
   result = Curl_h1_req_write_head(req, http_minor, &ts->request_data);
+  if(!result)
+    result = Curl_creader_set_null(data);
 
 
 out:
 out:
   if(result)
   if(result)
@@ -366,7 +367,6 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
 {
 {
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
   struct SingleRequest *k = &data->req;
   struct SingleRequest *k = &data->req;
-  curl_socket_t tunnelsocket = Curl_conn_cf_get_socket(cf, data);
   char *linep;
   char *linep;
   size_t line_len;
   size_t line_len;
   int error, writetype;
   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
     /* Read one byte at a time to avoid a race condition. Wait at most one
        second before looping to ensure continuous pgrsUpdates. */
        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)
     if(result == CURLE_AGAIN)
       /* socket buffer drained, return */
       /* socket buffer drained, return */
       return CURLE_OK;
       return CURLE_OK;
@@ -593,7 +593,9 @@ static CURLcode start_CONNECT(struct Curl_cfilter *cf,
     goto error;
     goto error;
   }
   }
   /* tell Hyper how to read/write network data */
   /* 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_read(io, Curl_hyper_recv);
   hyper_io_set_write(io, Curl_hyper_send);
   hyper_io_set_write(io, Curl_hyper_send);
   conn->sockfd = tunnelsocket;
   conn->sockfd = tunnelsocket;
@@ -749,6 +751,10 @@ static CURLcode start_CONNECT(struct Curl_cfilter *cf,
   if(result)
   if(result)
     goto error;
     goto error;
 
 
+  result = Curl_creader_set_null(data);
+  if(result)
+    goto error;
+
   sendtask = hyper_clientconn_send(client, req);
   sendtask = hyper_clientconn_send(client, req);
   if(!sendtask) {
   if(!sendtask) {
     failf(data, "hyper_clientconn_send");
     failf(data, "hyper_clientconn_send");
@@ -832,9 +838,9 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
   int didwhat;
   int didwhat;
 
 
   (void)ts;
   (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);
                              CURL_CSELECT_IN | CURL_CSELECT_OUT);
+  *done = data->req.done;
   if(result || !*done)
   if(result || !*done)
     return result;
     return result;
   if(h->exec) {
   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
          * If the other side indicated a connection close, or if someone
          * else told us to close this connection, do so now.
          * else told us to close this connection, do so now.
          */
          */
+        Curl_req_soft_reset(&data->req, data);
         if(ts->close_connection || conn->bits.close) {
         if(ts->close_connection || conn->bits.close) {
           /* Close this filter and the sub-chain, re-connect the
           /* Close this filter and the sub-chain, re-connect the
            * sub-chain and continue. Closing this filter will
            * sub-chain and continue. Closing this filter will
@@ -1003,11 +1010,9 @@ out:
   *done = (result == CURLE_OK) && tunnel_is_established(cf->ctx);
   *done = (result == CURLE_OK) && tunnel_is_established(cf->ctx);
   if(*done) {
   if(*done) {
     cf->connected = TRUE;
     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_pgrsSetUploadCounter(data, 0);
     Curl_pgrsSetDownloadCounter(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
          wait for the socket to become readable to be able to get the
          response headers or if we're still sending the request, wait
          response headers or if we're still sending the request, wait
          for write. */
          for write. */
-      if(ts->CONNECT.sending == HTTPSEND_REQUEST)
+      if(tunnel_want_send(ts))
         Curl_pollset_set_out_only(data, ps, sock);
         Curl_pollset_set_out_only(data, ps, sock);
       else
       else
         Curl_pollset_set_in_only(data, ps, sock);
         Curl_pollset_set_in_only(data, ps, sock);

+ 10 - 1
lib/cf-h2-proxy.c

@@ -38,6 +38,7 @@
 #include "http2.h"
 #include "http2.h"
 #include "http_proxy.h"
 #include "http_proxy.h"
 #include "multiif.h"
 #include "multiif.h"
+#include "sendf.h"
 #include "cf-h2-proxy.h"
 #include "cf-h2-proxy.h"
 
 
 /* The last 3 #include files should be in this order */
 /* 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;
   struct httpreq *req = NULL;
 
 
   result = Curl_http_proxy_create_CONNECT(&req, cf, data, 2);
   result = Curl_http_proxy_create_CONNECT(&req, cf, data, 2);
+  if(result)
+    goto out;
+  result = Curl_creader_set_null(data);
   if(result)
   if(result)
     goto out;
     goto out;
 
 
@@ -1125,7 +1129,12 @@ static CURLcode cf_h2_proxy_connect(struct Curl_cfilter *cf,
 
 
 out:
 out:
   *done = (result == CURLE_OK) && (ts->state == H2_TUNNEL_ESTABLISHED);
   *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);
   CF_DATA_RESTORE(cf, save);
   return result;
   return result;
 }
 }

+ 14 - 9
lib/cf-haproxy.c

@@ -86,14 +86,14 @@ static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter*cf,
   if(data->set.str[STRING_HAPROXY_CLIENT_IP])
   if(data->set.str[STRING_HAPROXY_CLIENT_IP])
     client_ip = data->set.str[STRING_HAPROXY_CLIENT_IP];
     client_ip = data->set.str[STRING_HAPROXY_CLIENT_IP];
   else
   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",
   result = Curl_dyn_addf(&ctx->data_out, "PROXY %s %s %s %i %i\r\n",
                          tcp_version,
                          tcp_version,
                          client_ip,
                          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
 #ifdef USE_UNIX_SOCKETS
   }
   }
@@ -129,12 +129,17 @@ static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf,
   case HAPROXY_SEND:
   case HAPROXY_SEND:
     len = Curl_dyn_len(&ctx->data_out);
     len = Curl_dyn_len(&ctx->data_out);
     if(len > 0) {
     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;
         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) {
       if(Curl_dyn_len(&ctx->data_out) > 0) {
         result = CURLE_OK;
         result = CURLE_OK;
         goto out;
         goto out;

+ 40 - 53
lib/cf-socket.c

@@ -776,10 +776,7 @@ struct cf_socket_ctx {
   struct Curl_sockaddr_ex addr;      /* address to connect to */
   struct Curl_sockaddr_ex addr;      /* address to connect to */
   curl_socket_t sock;                /* current attempt socket */
   curl_socket_t sock;                /* current attempt socket */
   struct bufq recvbuf;               /* used when `buffer_recv` is set */
   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 started_at;        /* when socket was created */
   struct curltime connected_at;      /* when socket connected/got first byte */
   struct curltime connected_at;      /* when socket connected/got first byte */
   struct curltime first_byte_at;     /* when first byte was recvd */
   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;
       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;
   return nread;
 }
 }
 
 
@@ -940,7 +938,7 @@ static CURLcode set_local_ip(struct Curl_cfilter *cf,
       return CURLE_FAILED_INIT;
       return CURLE_FAILED_INIT;
     }
     }
     if(!Curl_addr2string((struct sockaddr*)&ssloc, slen,
     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",
       failf(data, "ssloc inet_ntop() failed with errno %d: %s",
             errno, Curl_strerror(errno, buffer, sizeof(buffer)));
             errno, Curl_strerror(errno, buffer, sizeof(buffer)));
       return CURLE_FAILED_INIT;
       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 */
   /* store remote address and port used in this connection attempt */
   if(!Curl_addr2string(&ctx->addr.sa_addr, ctx->addr.addrlen,
   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];
     char buffer[STRERROR_LEN];
 
 
     ctx->error = errno;
     ctx->error = errno;
@@ -996,11 +994,11 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf,
 #ifdef ENABLE_IPV6
 #ifdef ENABLE_IPV6
   if(ctx->addr.family == AF_INET6) {
   if(ctx->addr.family == AF_INET6) {
     set_ipv6_v6only(ctx->sock, 0);
     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
   else
 #endif
 #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
 #ifdef ENABLE_IPV6
   is_tcp = (ctx->addr.family == AF_INET
   is_tcp = (ctx->addr.family == AF_INET
@@ -1166,9 +1164,9 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf,
     error = SOCKERRNO;
     error = SOCKERRNO;
     set_local_ip(cf, data);
     set_local_ip(cf, data);
     CURL_TRC_CF(data, cf, "local address %s port %d...",
     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) {
     if(-1 == rc) {
-      result = socket_connect_result(data, ctx->r_ip, error);
+      result = socket_connect_result(data, ctx->ip.remote_ip, error);
       goto out;
       goto out;
     }
     }
   }
   }
@@ -1213,7 +1211,8 @@ out:
       {
       {
         char buffer[STRERROR_LEN];
         char buffer[STRERROR_LEN];
         infof(data, "connect to %s port %u from %s port %d failed: %s",
         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)));
               Curl_strerror(ctx->error, buffer, sizeof(buffer)));
       }
       }
 #endif
 #endif
@@ -1233,10 +1232,11 @@ static void cf_socket_get_host(struct Curl_cfilter *cf,
                                const char **pdisplay_host,
                                const char **pdisplay_host,
                                int *pport)
                                int *pport)
 {
 {
+  struct cf_socket_ctx *ctx = cf->ctx;
   (void)data;
   (void)data;
   *phost = cf->conn->host.name;
   *phost = cf->conn->host.name;
   *pdisplay_host = cf->conn->host.dispname;
   *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,
 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(ctx->sock != CURL_SOCKET_BAD) {
     if(!cf->connected) {
     if(!cf->connected) {
       Curl_pollset_set_out_only(data, ps, ctx->sock);
       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) {
     else if(!ctx->active) {
       Curl_pollset_add_in(data, ps, ctx->sock);
       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;
   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)
 static void cf_socket_active(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
 {
   struct cf_socket_ctx *ctx = cf->ctx;
   struct cf_socket_ctx *ctx = cf->ctx;
 
 
   /* use this socket from now on */
   /* use this socket from now on */
   cf->conn->sock[cf->sockindex] = ctx->sock;
   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) {
   if(cf->sockindex == FIRSTSOCKET) {
     cf->conn->remote_addr = &ctx->addr;
     cf->conn->remote_addr = &ctx->addr;
   #ifdef ENABLE_IPV6
   #ifdef ENABLE_IPV6
     cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6)? TRUE : FALSE;
     cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6)? TRUE : FALSE;
   #endif
   #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
     /* buffering is currently disabled by default because we have stalls
      * in parallel transfers where not all buffered data is consumed and no
      * in parallel transfers where not all buffered data is consumed and no
      * socket events happen.
      * socket events happen.
@@ -1480,7 +1475,7 @@ static CURLcode cf_socket_cntrl(struct Curl_cfilter *cf,
     cf_socket_active(cf, data);
     cf_socket_active(cf, data);
     break;
     break;
   case CF_CTRL_DATA_SETUP:
   case CF_CTRL_DATA_SETUP:
-    Curl_persistconninfo(data, cf->conn, ctx->l_ip, ctx->l_port);
+    Curl_persistconninfo(data, cf->conn, &ctx->ip);
     break;
     break;
   case CF_CTRL_FORGET_SOCKET:
   case CF_CTRL_FORGET_SOCKET:
     ctx->sock = CURL_SOCKET_BAD;
     ctx->sock = CURL_SOCKET_BAD;
@@ -1637,7 +1632,7 @@ static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf,
 #else
 #else
   rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
   rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
   if(-1 == rc) {
   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;
   ctx->sock_connected = TRUE;
 #endif
 #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
   CURL_TRC_CF(data, cf, "%s socket %" CURL_FORMAT_SOCKET_T
               " connected: [%s:%d] -> [%s:%d]",
               " connected: [%s:%d] -> [%s:%d]",
               (ctx->transport == TRNSPRT_QUIC)? "QUIC" : "UDP",
               (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);
   (void)curlx_nonblock(ctx->sock, TRUE);
   switch(ctx->addr.family) {
   switch(ctx->addr.family) {
@@ -1695,7 +1691,7 @@ static CURLcode cf_udp_connect(struct Curl_cfilter *cf,
         goto out;
         goto out;
       CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%"
       CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%"
                   CURL_FORMAT_SOCKET_T " (%s:%d)",
                   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 {
     else {
       CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%"
       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;
   struct Curl_sockaddr_storage ssrem;
   curl_socklen_t plen;
   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);
   plen = sizeof(ssrem);
   memset(&ssrem, 0, plen);
   memset(&ssrem, 0, plen);
   if(getpeername(ctx->sock, (struct sockaddr*) &ssrem, &plen)) {
   if(getpeername(ctx->sock, (struct sockaddr*) &ssrem, &plen)) {
@@ -1902,14 +1898,14 @@ static void set_accepted_remote_ip(struct Curl_cfilter *cf,
     return;
     return;
   }
   }
   if(!Curl_addr2string((struct sockaddr*)&ssrem, plen,
   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",
     failf(data, "ssrem inet_ntop() failed with errno %d: %s",
           errno, Curl_strerror(errno, buffer, sizeof(buffer)));
           errno, Curl_strerror(errno, buffer, sizeof(buffer)));
     return;
     return;
   }
   }
 #else
 #else
-  ctx->r_ip[0] = 0;
-  ctx->r_port = 0;
+  ctx->ip.remote_ip[0] = 0;
+  ctx->ip.remote_port = 0;
   (void)data;
   (void)data;
 #endif
 #endif
 }
 }
@@ -1938,7 +1934,7 @@ CURLcode Curl_conn_tcp_accepted_set(struct Curl_easy *data,
   cf->connected = TRUE;
   cf->connected = TRUE;
   CURL_TRC_CF(data, cf, "accepted_set(sock=%" CURL_FORMAT_SOCKET_T
   CURL_TRC_CF(data, cf, "accepted_set(sock=%" CURL_FORMAT_SOCKET_T
               ", remote=%s port=%d)",
               ", 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;
   return CURLE_OK;
 }
 }
@@ -1958,9 +1954,9 @@ CURLcode Curl_cf_socket_peek(struct Curl_cfilter *cf,
                              struct Curl_easy *data,
                              struct Curl_easy *data,
                              curl_socket_t *psock,
                              curl_socket_t *psock,
                              const struct Curl_sockaddr_ex **paddr,
                              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) {
   if(cf_is_socket(cf) && cf->ctx) {
     struct cf_socket_ctx *ctx = 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;
       *psock = ctx->sock;
     if(paddr)
     if(paddr)
       *paddr = &ctx->addr;
       *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_OK;
   }
   }
   return CURLE_FAILED_INIT;
   return CURLE_FAILED_INIT;

+ 3 - 6
lib/cf-socket.h

@@ -33,6 +33,7 @@ struct Curl_cfilter;
 struct Curl_easy;
 struct Curl_easy;
 struct connectdata;
 struct connectdata;
 struct Curl_sockaddr_ex;
 struct Curl_sockaddr_ex;
+struct ip_quadruple;
 
 
 /*
 /*
  * The Curl_sockaddr_ex structure is basically libcurl's external API
  * 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.
  * The filter owns all returned values.
  * @param psock             pointer to hold socket descriptor or NULL
  * @param psock             pointer to hold socket descriptor or NULL
  * @param paddr             pointer to hold addr reference 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.
  * Returns error if the filter is of invalid type.
  */
  */
 CURLcode Curl_cf_socket_peek(struct Curl_cfilter *cf,
 CURLcode Curl_cf_socket_peek(struct Curl_cfilter *cf,
                              struct Curl_easy *data,
                              struct Curl_easy *data,
                              curl_socket_t *psock,
                              curl_socket_t *psock,
                              const struct Curl_sockaddr_ex **paddr,
                              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_tcp;
 extern struct Curl_cftype Curl_cft_udp;
 extern struct Curl_cftype Curl_cft_udp;

+ 67 - 7
lib/cfilters.c

@@ -67,7 +67,7 @@ void Curl_cf_def_get_host(struct Curl_cfilter *cf, struct Curl_easy *data,
   else {
   else {
     *phost = cf->conn->host.name;
     *phost = cf->conn->host.name;
     *pdisplay_host = cf->conn->host.dispname;
     *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;
   struct Curl_cfilter *cf;
 
 
   DEBUGASSERT(data);
   DEBUGASSERT(data);
   DEBUGASSERT(data->conn);
   DEBUGASSERT(data->conn);
+  *code = CURLE_OK;
   cf = data->conn->cfilter[num];
   cf = data->conn->cfilter[num];
   while(cf && !cf->connected) {
   while(cf && !cf->connected) {
     cf = cf->next;
     cf = cf->next;
   }
   }
   if(cf) {
   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");
   failf(data, "recv: no filter connected");
   *code = CURLE_FAILED_INIT;
   *code = CURLE_FAILED_INIT;
   return -1;
   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;
   struct Curl_cfilter *cf;
 
 
   DEBUGASSERT(data);
   DEBUGASSERT(data);
   DEBUGASSERT(data->conn);
   DEBUGASSERT(data->conn);
+  *code = CURLE_OK;
   cf = data->conn->cfilter[num];
   cf = data->conn->cfilter[num];
   while(cf && !cf->connected) {
   while(cf && !cf->connected) {
     cf = cf->next;
     cf = cf->next;
   }
   }
   if(cf) {
   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");
   failf(data, "send: no filter connected");
   DEBUGASSERT(0);
   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;
   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,
 void Curl_pollset_reset(struct Curl_easy *data,
                         struct easy_pollset *ps)
                         struct easy_pollset *ps)

+ 29 - 5
lib/cfilters.h

@@ -402,11 +402,11 @@ void Curl_conn_adjust_pollset(struct Curl_easy *data,
 /**
 /**
  * Receive data through the filter chain at `sockindex` for connection
  * Receive data through the filter chain at `sockindex` for connection
  * `data->conn`. Copy at most `len` bytes into `buf`. Return the
  * `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`.
  * 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`
  * 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.
  * or a negative value on error.
  * The error code is placed into `*code`.
  * 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
  * 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);
                                     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,
 void Curl_pollset_reset(struct Curl_easy *data,
                         struct easy_pollset *ps);
                         struct easy_pollset *ps);
 
 

+ 1 - 7
lib/conncache.c

@@ -131,7 +131,7 @@ static void hashkey(struct connectdata *conn, char *buf, size_t len)
 #ifndef CURL_DISABLE_PROXY
 #ifndef CURL_DISABLE_PROXY
   if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
   if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
     hostname = conn->http_proxy.host.name;
     hostname = conn->http_proxy.host.name;
-    port = conn->port;
+    port = conn->primary.remote_port;
   }
   }
   else
   else
 #endif
 #endif
@@ -395,8 +395,6 @@ bool Curl_conncache_return_conn(struct Curl_easy *data,
          important that details from this (unrelated) disconnect does not
          important that details from this (unrelated) disconnect does not
          taint meta-data in the data handle. */
          taint meta-data in the data handle. */
       struct conncache *connc = data->state.conn_cache;
       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,
       Curl_disconnect(connc->closure_handle, conn_candidate,
                       /* dead_connection */ FALSE);
                       /* dead_connection */ FALSE);
     }
     }
@@ -522,12 +520,9 @@ Curl_conncache_extract_oldest(struct Curl_easy *data)
 void Curl_conncache_close_all_connections(struct conncache *connc)
 void Curl_conncache_close_all_connections(struct conncache *connc)
 {
 {
   struct connectdata *conn;
   struct connectdata *conn;
-  char buffer[READBUFFER_MIN + 1];
   SIGPIPE_VARIABLE(pipe_st);
   SIGPIPE_VARIABLE(pipe_st);
   if(!connc->closure_handle)
   if(!connc->closure_handle)
     return;
     return;
-  connc->closure_handle->state.buffer = buffer;
-  connc->closure_handle->set.buffer_size = READBUFFER_MIN;
 
 
   conn = conncache_find_first_connection(connc);
   conn = conncache_find_first_connection(connc);
   while(conn) {
   while(conn) {
@@ -541,7 +536,6 @@ void Curl_conncache_close_all_connections(struct conncache *connc)
     conn = conncache_find_first_connection(connc);
     conn = conncache_find_first_connection(connc);
   }
   }
 
 
-  connc->closure_handle->state.buffer = NULL;
   sigpipe_ignore(connc->closure_handle, &pipe_st);
   sigpipe_ignore(connc->closure_handle, &pipe_st);
 
 
   Curl_hostcache_clean(connc->closure_handle,
   Curl_hostcache_clean(connc->closure_handle,

+ 18 - 11
lib/connect.c

@@ -94,7 +94,7 @@
  * infinite time left). If the value is negative, the timeout time has already
  * infinite time left). If the value is negative, the timeout time has already
  * elapsed.
  * elapsed.
  * @param data the transfer to check on
  * @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.
  * @param duringconnect TRUE iff connect timeout is also taken into account.
  * @unittest: 1303
  * @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
 /* Copies connection info into the transfer handle to make it available when
    the transfer handle is no longer associated with the connection. */
    the transfer handle is no longer associated with the connection. */
 void Curl_persistconninfo(struct Curl_easy *data, struct connectdata *conn,
 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;
   data->info.conn_scheme = conn->handler->scheme;
   /* conn_protocol can only provide "old" protocols */
   /* conn_protocol can only provide "old" protocols */
   data->info.conn_protocol = (conn->handler->protocol) & CURLPROTO_MASK;
   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_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 *
 static const struct Curl_addrinfo *
@@ -721,7 +728,7 @@ evaluate:
 
 
   failf(data, "Failed to connect to %s port %u after "
   failf(data, "Failed to connect to %s port %u after "
         "%" CURL_FORMAT_TIMEDIFF_T " ms: %s",
         "%" CURL_FORMAT_TIMEDIFF_T " ms: %s",
-        hostname, conn->port,
+        hostname, conn->primary.remote_port,
         Curl_timediff(now, data->progress.t_startsingle),
         Curl_timediff(now, data->progress.t_startsingle),
         curl_easy_strerror(result));
         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)
         if(cf->conn->handler->protocol & PROTO_FAMILY_SSH)
           Curl_pgrsTime(data, TIMER_APPCONNECT); /* we're connected already */
           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 */
         data->info.numconnects++; /* to track the # of connections made */
       }
       }
       break;
       break;

+ 2 - 1
lib/connect.h

@@ -30,6 +30,7 @@
 #include "timeval.h"
 #include "timeval.h"
 
 
 struct Curl_dns_entry;
 struct Curl_dns_entry;
+struct ip_quadruple;
 
 
 /* generic function that returns how much time there's left to run, according
 /* generic function that returns how much time there's left to run, according
    to the timeouts set */
    to the timeouts set */
@@ -52,7 +53,7 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
                       char *addr, int *port);
                       char *addr, int *port);
 
 
 void Curl_persistconninfo(struct Curl_easy *data, struct connectdata *conn,
 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'
  * Curl_conncontrol() marks the end of a connection/stream. The 'closeit'

+ 10 - 23
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. */
 /* Make sure domain contains a dot or is localhost. */
 static bool bad_domain(const char *domain, size_t len)
 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;
   return TRUE;
 }
 }
+#endif
 
 
 /*
 /*
   RFC 6265 section 4.1.1 says a server should accept this range:
   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);
         Curl_psl_release(data);
       }
       }
       else
       else
-        acceptable = !bad_domain(domain, strlen(domain));
+        infof(data, "libpsl problem, rejecting cookie for satety");
     }
     }
 
 
     if(!acceptable) {
     if(!acceptable) {
@@ -1205,7 +1207,6 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
                                     bool newsession)
                                     bool newsession)
 {
 {
   struct CookieInfo *c;
   struct CookieInfo *c;
-  char *line = NULL;
   FILE *handle = NULL;
   FILE *handle = NULL;
 
 
   if(!inc) {
   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 */
     c->running = FALSE; /* this is not running, this is init */
     if(fp) {
     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;
         bool headerline = FALSE;
-        if(checkprefix("Set-Cookie:", line)) {
+        if(checkprefix("Set-Cookie:", lineptr)) {
           /* This is a cookie line, get it! */
           /* This is a cookie line, get it! */
-          lineptr = &line[11];
+          lineptr += 11;
           headerline = TRUE;
           headerline = TRUE;
           while(*lineptr && ISBLANK(*lineptr))
           while(*lineptr && ISBLANK(*lineptr))
             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);
         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
        * 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 */
   c->running = TRUE;          /* now, we're running */
 
 
   return c;
   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 */
 }
 }
 
 
 /*
 /*

+ 3 - 0
lib/curl_config.h.cmake

@@ -720,6 +720,9 @@ ${SIZEOF_TIME_T_CODE}
 /* to enable quiche */
 /* to enable quiche */
 #cmakedefine USE_QUICHE 1
 #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. */
 /* Define to 1 if you have the quiche_conn_set_qlog_fd function. */
 #cmakedefine HAVE_QUICHE_CONN_SET_QLOG_FD 1
 #cmakedefine HAVE_QUICHE_CONN_SET_QLOG_FD 1
 
 

+ 1 - 1
lib/curl_des.c

@@ -36,7 +36,7 @@
  * Curl_des_set_odd_parity()
  * Curl_des_set_odd_parity()
  *
  *
  * This is used to apply odd parity to the given byte array. It is typically
  * 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:
  * The function is a port of the Java based oddParity() function over at:
  *
  *

+ 23 - 32
lib/curl_get_line.c

@@ -33,14 +33,16 @@
 #include "memdebug.h"
 #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) {
   while(1) {
-    char *b = fgets(buf, len, input);
+    char *b = fgets(buffer, sizeof(buffer), input);
 
 
     if(b) {
     if(b) {
       size_t rlen = strlen(b);
       size_t rlen = strlen(b);
@@ -48,39 +50,28 @@ char *Curl_get_line(char *buf, int len, FILE *input)
       if(!rlen)
       if(!rlen)
         break;
         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
     else
       break;
       break;
   }
   }
-  return NULL;
+  return 0;
 }
 }
 
 
 #endif /* if not disabled */
 #endif /* if not disabled */

+ 4 - 3
lib/curl_get_line.h

@@ -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 */
 #endif /* HEADER_CURL_GET_LINE_H */

+ 1 - 1
lib/curl_ntlm_wb.c

@@ -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;
   size_t len_in = strlen(input), len_out = 0;
   struct dynbuf b;
   struct dynbuf b;
   char *ptr = NULL;
   char *ptr = NULL;
-  usigned char buf[1024]
+  unsigned char buf[1024];
   Curl_dyn_init(&b, MAX_NTLM_WB_RESPONSE);
   Curl_dyn_init(&b, MAX_NTLM_WB_RESPONSE);
 
 
   while(len_in > 0) {
   while(len_in > 0) {

+ 2 - 2
lib/curl_rtmp.c

@@ -265,10 +265,10 @@ static CURLcode rtmp_do(struct Curl_easy *data, bool *done)
 
 
   if(data->state.upload) {
   if(data->state.upload) {
     Curl_pgrsSetUploadSize(data, data->state.infilesize);
     Curl_pgrsSetUploadSize(data, data->state.infilesize);
-    Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
+    Curl_xfer_setup(data, -1, -1, FALSE, FIRSTSOCKET);
   }
   }
   else
   else
-    Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
+    Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1);
   *done = TRUE;
   *done = TRUE;
   return CURLE_OK;
   return CURLE_OK;
 }
 }

+ 18 - 0
lib/curl_setup.h

@@ -266,6 +266,13 @@
 
 
 #include <curl/system.h>
 #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
 /* curl uses its own printf() function internally. It understands the GNU
  * format. Use this format, so that is matches the GNU format attribute we
  * 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.
  * use with the mingw compiler, allowing it to verify them at compile-time.
@@ -495,6 +502,17 @@
 #endif
 #endif
 #define CURL_OFF_T_MIN (-CURL_OFF_T_MAX - CURL_OFF_T_C(1))
 #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)
 #if (SIZEOF_TIME_T == 4)
 #  ifdef HAVE_TIME_T_UNSIGNED
 #  ifdef HAVE_TIME_T_UNSIGNED
 #  define TIME_T_MAX UINT_MAX
 #  define TIME_T_MAX UINT_MAX

+ 844 - 0
lib/curl_sha512_256.c

@@ -0,0 +1,844 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Evgeny Grin (Karlson2k), <[email protected]>.
+ *
+ * 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 */

+ 44 - 0
lib/curl_sha512_256.h

@@ -0,0 +1,44 @@
+#ifndef HEADER_CURL_SHA512_256_H
+#define HEADER_CURL_SHA512_256_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Evgeny Grin (Karlson2k), <[email protected]>.
+ *
+ * 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 */

+ 30 - 5
lib/curl_trc.c

@@ -36,6 +36,7 @@
 
 
 #include "cf-socket.h"
 #include "cf-socket.h"
 #include "connect.h"
 #include "connect.h"
+#include "doh.h"
 #include "http2.h"
 #include "http2.h"
 #include "http_proxy.h"
 #include "http_proxy.h"
 #include "cf-h1-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, ...)
 void Curl_infof(struct Curl_easy *data, const char *fmt, ...)
 {
 {
   DEBUGASSERT(!strchr(fmt, '\n'));
   DEBUGASSERT(!strchr(fmt, '\n'));
-  if(data && data->set.verbose) {
+  if(Curl_trc_is_verbose(data)) {
     va_list ap;
     va_list ap;
-    int len;
+    int len = 0;
     char buffer[MAXINFO + 2];
     char buffer[MAXINFO + 2];
+    if(data->state.feat)
+      len = msnprintf(buffer, MAXINFO, "[%s] ", data->state.feat->name);
     va_start(ap, fmt);
     va_start(ap, fmt);
-    len = mvsnprintf(buffer, MAXINFO, fmt, ap);
+    len += mvsnprintf(buffer + len, MAXINFO - len, fmt, ap);
     va_end(ap);
     va_end(ap);
     buffer[len++] = '\n';
     buffer[len++] = '\n';
     buffer[len] = '\0';
     buffer[len] = '\0';
@@ -132,9 +135,16 @@ void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf,
   DEBUGASSERT(cf);
   DEBUGASSERT(cf);
   if(Curl_trc_cf_is_verbose(cf, data)) {
   if(Curl_trc_cf_is_verbose(cf, data)) {
     va_list ap;
     va_list ap;
-    int len;
+    int len = 0;
     char buffer[MAXINFO + 2];
     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);
     va_start(ap, fmt);
     len += mvsnprintf(buffer + len, MAXINFO - len, fmt, ap);
     len += mvsnprintf(buffer + len, MAXINFO - len, fmt, ap);
     va_end(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[] = {
 static struct Curl_cftype *cf_types[] = {
   &Curl_cft_tcp,
   &Curl_cft_tcp,
@@ -215,6 +231,15 @@ CURLcode Curl_trc_opt(const char *config)
         break;
         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);
     token = strtok_r(NULL, ", ", &tok_buf);
   }
   }
   free(tmp);
   free(tmp);

+ 15 - 3
lib/curl_trc.h

@@ -86,10 +86,21 @@ void Curl_failf(struct Curl_easy *data,
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
 /* informational messages enabled */
 /* 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) \
 #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.
  * 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_is_verbose(d)        ((void)(d), FALSE)
 #define Curl_trc_cf_is_verbose(x,y)   ((void)(x), (void)(y), 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, ...)
 static void Curl_infof(struct Curl_easy *data, const char *fmt, ...)
 {
 {

+ 437 - 0
lib/cw-out.c

@@ -0,0 +1,437 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <[email protected]>, 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);
+}

+ 53 - 0
lib/cw-out.h

@@ -0,0 +1,53 @@
+#ifndef HEADER_CURL_CW_OUT_H
+#define HEADER_CURL_CW_OUT_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <[email protected]>, 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 */

+ 11 - 14
lib/dict.c

@@ -122,13 +122,12 @@ static char *unescape_word(const char *input)
 }
 }
 
 
 /* sendf() sends formatted data to the server */
 /* 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;
   size_t write_len;
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
   char *s;
   char *s;
@@ -146,7 +145,7 @@ static CURLcode sendf(curl_socket_t sockfd, struct Curl_easy *data,
 
 
   for(;;) {
   for(;;) {
     /* Write the buffer to the socket */
     /* 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)
     if(result)
       break;
       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
   char *nthdef = NULL; /* This is not part of the protocol, but required
                           by RFC 2229 */
                           by RFC 2229 */
   CURLcode result;
   CURLcode result;
-  struct connectdata *conn = data->conn;
-  curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
 
 
   char *path;
   char *path;
 
 
@@ -228,7 +225,7 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
       goto error;
       goto error;
     }
     }
 
 
-    result = sendf(sockfd, data,
+    result = sendf(data,
                    "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n"
                    "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n"
                    "MATCH "
                    "MATCH "
                    "%s "    /* database */
                    "%s "    /* database */
@@ -243,7 +240,7 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
       failf(data, "Failed sending DICT request");
       failf(data, "Failed sending DICT request");
       goto error;
       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) ||
   else if(strncasecompare(path, DICT_DEFINE, sizeof(DICT_DEFINE)-1) ||
           strncasecompare(path, DICT_DEFINE2, sizeof(DICT_DEFINE2)-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;
       goto error;
     }
     }
 
 
-    result = sendf(sockfd, data,
+    result = sendf(data,
                    "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n"
                    "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n"
                    "DEFINE "
                    "DEFINE "
                    "%s "     /* database */
                    "%s "     /* database */
@@ -289,7 +286,7 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
       failf(data, "Failed sending DICT request");
       failf(data, "Failed sending DICT request");
       goto error;
       goto error;
     }
     }
-    Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
+    Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1);
   }
   }
   else {
   else {
 
 
@@ -302,7 +299,7 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
         if(ppath[i] == ':')
         if(ppath[i] == ':')
           ppath[i] = ' ';
           ppath[i] = ' ';
       }
       }
-      result = sendf(sockfd, data,
+      result = sendf(data,
                      "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n"
                      "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n"
                      "%s\r\n"
                      "%s\r\n"
                      "QUIT\r\n", ppath);
                      "QUIT\r\n", ppath);
@@ -311,7 +308,7 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
         goto error;
         goto error;
       }
       }
 
 
-      Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
+      Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1);
     }
     }
   }
   }
 
 

+ 22 - 11
lib/doh.c

@@ -69,7 +69,12 @@ static const char *doh_strerror(DOHcode code)
     return errors[code];
     return errors[code];
   return "bad error 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
 /* @unittest 1655
  */
  */
@@ -189,9 +194,9 @@ static int doh_done(struct Curl_easy *doh, CURLcode result)
   struct dohdata *dohp = data->req.doh;
   struct dohdata *dohp = data->req.doh;
   /* so one of the DoH request done for the 'data' transfer is now complete! */
   /* so one of the DoH request done for the 'data' transfer is now complete! */
   dohp->pending--;
   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)
   if(result)
-    infof(data, "DoH request %s", curl_easy_strerror(result));
+    infof(doh, "DoH request %s", curl_easy_strerror(result));
 
 
   if(!dohp->pending) {
   if(!dohp->pending) {
     /* DoH completed */
     /* DoH completed */
@@ -242,6 +247,9 @@ static CURLcode dohprobe(struct Curl_easy *data,
        the gcc typecheck helpers */
        the gcc typecheck helpers */
     struct dynbuf *resp = &p->serverdoh;
     struct dynbuf *resp = &p->serverdoh;
     doh->state.internal = true;
     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_URL, url);
     ERROR_CHECK_SETOPT(CURLOPT_DEFAULT_PROTOCOL, "https");
     ERROR_CHECK_SETOPT(CURLOPT_DEFAULT_PROTOCOL, "https");
     ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb);
     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);
     ERROR_CHECK_SETOPT(CURLOPT_SHARE, data->share);
     if(data->set.err && data->set.err != stderr)
     if(data->set.err && data->set.err != stderr)
       ERROR_CHECK_SETOPT(CURLOPT_STDERR, data->set.err);
       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);
       ERROR_CHECK_SETOPT(CURLOPT_VERBOSE, 1L);
     if(data->set.no_signal)
     if(data->set.no_signal)
       ERROR_CHECK_SETOPT(CURLOPT_NOSIGNAL, 1L);
       ERROR_CHECK_SETOPT(CURLOPT_NOSIGNAL, 1L);
@@ -741,11 +749,11 @@ static void showdoh(struct Curl_easy *data,
                     const struct dohentry *d)
                     const struct dohentry *d)
 {
 {
   int i;
   int i;
-  infof(data, "TTL: %u seconds", d->ttl);
+  infof(data, "[DoH] TTL: %u seconds", d->ttl);
   for(i = 0; i < d->numaddr; i++) {
   for(i = 0; i < d->numaddr; i++) {
     const struct dohaddr *a = &d->addr[i];
     const struct dohaddr *a = &d->addr[i];
     if(a->type == DNS_TYPE_A) {
     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[0], a->ip.v4[1],
             a->ip.v4[2], a->ip.v4[3]);
             a->ip.v4[2], a->ip.v4[3]);
     }
     }
@@ -754,9 +762,9 @@ static void showdoh(struct Curl_easy *data,
       char buffer[128];
       char buffer[128];
       char *ptr;
       char *ptr;
       size_t len;
       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) {
       for(j = 0; j < 16; j += 2) {
         size_t l;
         size_t l;
         msnprintf(ptr, len, "%s%02x%02x", j?":":"", d->addr[i].ip.v6[j],
         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_dns_entry *dns;
       struct Curl_addrinfo *ai;
       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);
       result = doh2ai(&de, dohp->host, dohp->port, &ai);
       if(result) {
       if(result) {

+ 2 - 0
lib/doh.h

@@ -120,6 +120,8 @@ void de_init(struct dohentry *d);
 void de_cleanup(struct dohentry *d);
 void de_cleanup(struct dohentry *d);
 #endif
 #endif
 
 
+extern struct curl_trc_feat Curl_doh_trc;
+
 #else /* if DoH is disabled */
 #else /* if DoH is disabled */
 #define Curl_doh(a,b,c,d) NULL
 #define Curl_doh(a,b,c,d) NULL
 #define Curl_doh_is_resolved(x,y) CURLE_COULDNT_RESOLVE_HOST
 #define Curl_doh_is_resolved(x,y) CURLE_COULDNT_RESOLVE_HOST

+ 27 - 43
lib/easy.c

@@ -58,6 +58,7 @@
 #include "multiif.h"
 #include "multiif.h"
 #include "select.h"
 #include "select.h"
 #include "cfilters.h"
 #include "cfilters.h"
+#include "cw-out.h"
 #include "sendf.h" /* for failf function prototype */
 #include "sendf.h" /* for failf function prototype */
 #include "connect.h" /* for Curl_getconnectinfo */
 #include "connect.h" /* for Curl_getconnectinfo */
 #include "slist.h"
 #include "slist.h"
@@ -741,7 +742,6 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events)
     multi = Curl_multi_handle(1, 3, 7);
     multi = Curl_multi_handle(1, 3, 7);
     if(!multi)
     if(!multi)
       return CURLE_OUT_OF_MEMORY;
       return CURLE_OUT_OF_MEMORY;
-    data->multi_easy = multi;
   }
   }
 
 
   if(multi->in_callback)
   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 */
   /* Copy the MAXCONNECTS option to the multi handle */
   curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, (long)data->set.maxconnects);
   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);
   mcode = curl_multi_add_handle(multi, data);
   if(mcode) {
   if(mcode) {
     curl_multi_cleanup(multi);
     curl_multi_cleanup(multi);
-    data->multi_easy = NULL;
     if(mcode == CURLM_OUT_OF_MEMORY)
     if(mcode == CURLM_OUT_OF_MEMORY)
       return CURLE_OUT_OF_MEMORY;
       return CURLE_OUT_OF_MEMORY;
     return CURLE_FAILED_INIT;
     return CURLE_FAILED_INIT;
   }
   }
 
 
+  /* assign this after curl_multi_add_handle() */
+  data->multi_easy = multi;
+
   sigpipe_ignore(data, &pipe_st);
   sigpipe_ignore(data, &pipe_st);
 
 
   /* run the transfer */
   /* run the transfer */
@@ -1021,7 +1024,6 @@ fail:
 #ifndef CURL_DISABLE_COOKIES
 #ifndef CURL_DISABLE_COOKIES
     free(outcurl->cookies);
     free(outcurl->cookies);
 #endif
 #endif
-    free(outcurl->state.buffer);
     Curl_dyn_free(&outcurl->state.headerb);
     Curl_dyn_free(&outcurl->state.headerb);
     Curl_altsvc_cleanup(&outcurl->asi);
     Curl_altsvc_cleanup(&outcurl->asi);
     Curl_hsts_cleanup(&outcurl->hsts);
     Curl_hsts_cleanup(&outcurl->hsts);
@@ -1038,7 +1040,7 @@ fail:
  */
  */
 void curl_easy_reset(struct Curl_easy *data)
 void curl_easy_reset(struct Curl_easy *data)
 {
 {
-  Curl_free_request_state(data);
+  Curl_req_hard_reset(&data->req, data);
 
 
   /* zero out UserDefined data: */
   /* zero out UserDefined data: */
   Curl_freeset(data);
   Curl_freeset(data);
@@ -1108,9 +1110,10 @@ CURLcode curl_easy_pause(struct Curl_easy *data, int action)
   /* Unpause parts in active mime tree. */
   /* Unpause parts in active mime tree. */
   if((k->keepon & ~newstate & KEEP_SEND_PAUSE) &&
   if((k->keepon & ~newstate & KEEP_SEND_PAUSE) &&
      (data->mstate == MSTATE_PERFORMING ||
      (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 */
   /* 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)) {
   if(!(newstate & KEEP_RECV_PAUSE)) {
     Curl_conn_ev_data_pause(data, FALSE);
     Curl_conn_ev_data_pause(data, FALSE);
-    result = Curl_client_unpause(data);
+    result = Curl_cw_out_flush(data);
     if(result)
     if(result)
       return 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
   /* if there's no error and we're not pausing both directions, we want
      to have this handle checked soon */
      to have this handle checked soon */
   if((newstate & (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) !=
   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 */
     /* reset the too-slow time keeper */
     data->state.keeps_speed.tv_sec = 0;
     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
       /* if not pausing again, force a recv/send check of this connection as
          the data might've been read off the socket already */
          the data might've been read off the socket already */
       data->state.select_bits = CURL_CSELECT_IN | CURL_CSELECT_OUT;
       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)
                                 struct connectdata **connp)
 {
 {
+  curl_socket_t sfd;
+
   if(!data)
   if(!data)
     return CURLE_BAD_FUNCTION_ARGUMENT;
     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;
     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");
     failf(data, "Failed to get recent socket");
     return CURLE_UNSUPPORTED_PROTOCOL;
     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,
 CURLcode curl_easy_recv(struct Curl_easy *data, void *buffer, size_t buflen,
                         size_t *n)
                         size_t *n)
 {
 {
-  curl_socket_t sfd;
   CURLcode result;
   CURLcode result;
   ssize_t n1;
   ssize_t n1;
   struct connectdata *c;
   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))
   if(Curl_is_in_callback(data))
     return CURLE_RECURSIVE_API_CALL;
     return CURLE_RECURSIVE_API_CALL;
 
 
-  result = easy_connection(data, &sfd, &c);
+  result = easy_connection(data, &c);
   if(result)
   if(result)
     return 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);
     Curl_attach_connection(data, c);
 
 
   *n = 0;
   *n = 0;
-  result = Curl_read(data, sfd, buffer, buflen, &n1);
+  result = Curl_conn_recv(data, FIRSTSOCKET, buffer, buflen, &n1);
 
 
   if(result)
   if(result)
     return result;
     return result;
@@ -1226,11 +1220,10 @@ CURLcode curl_easy_recv(struct Curl_easy *data, void *buffer, size_t buflen,
 #ifdef USE_WEBSOCKETS
 #ifdef USE_WEBSOCKETS
 CURLcode Curl_connect_only_attach(struct Curl_easy *data)
 CURLcode Curl_connect_only_attach(struct Curl_easy *data)
 {
 {
-  curl_socket_t sfd;
   CURLcode result;
   CURLcode result;
   struct connectdata *c = NULL;
   struct connectdata *c = NULL;
 
 
-  result = easy_connection(data, &sfd, &c);
+  result = easy_connection(data, &c);
   if(result)
   if(result)
     return 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()
  * This is the private internal version of curl_easy_send()
  */
  */
 CURLcode Curl_senddata(struct Curl_easy *data, const void *buffer,
 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;
   CURLcode result;
-  ssize_t n1;
   struct connectdata *c = NULL;
   struct connectdata *c = NULL;
   SIGPIPE_VARIABLE(pipe_st);
   SIGPIPE_VARIABLE(pipe_st);
 
 
-  result = easy_connection(data, &sfd, &c);
+  *n = 0;
+  result = easy_connection(data, &c);
   if(result)
   if(result)
     return result;
     return result;
 
 
@@ -1266,20 +1258,12 @@ CURLcode Curl_senddata(struct Curl_easy *data, const void *buffer,
        needs to be reattached */
        needs to be reattached */
     Curl_attach_connection(data, c);
     Curl_attach_connection(data, c);
 
 
-  *n = 0;
   sigpipe_ignore(data, &pipe_st);
   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);
   sigpipe_restore(&pipe_st);
 
 
-  if(n1 == -1)
+  if(result && result != CURLE_AGAIN)
     return CURLE_SEND_ERROR;
     return CURLE_SEND_ERROR;
-
-  /* detect EAGAIN */
-  if(!result && !n1)
-    return CURLE_AGAIN;
-
-  *n = n1;
-
   return result;
   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,
 CURLcode curl_easy_send(struct Curl_easy *data, const void *buffer,
                         size_t buflen, size_t *n)
                         size_t buflen, size_t *n)
 {
 {
-  ssize_t written = 0;
+  size_t written = 0;
   CURLcode result;
   CURLcode result;
   if(Curl_is_in_callback(data))
   if(Curl_is_in_callback(data))
     return CURLE_RECURSIVE_API_CALL;
     return CURLE_RECURSIVE_API_CALL;
 
 
   result = Curl_senddata(data, buffer, buflen, &written);
   result = Curl_senddata(data, buffer, buflen, &written);
-  *n = (size_t)written;
+  *n = written;
   return result;
   return result;
 }
 }
 
 

+ 1 - 1
lib/easygetopt.c

@@ -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
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
  * you should have received as part of this distribution. The terms

+ 1 - 1
lib/easyif.h

@@ -28,7 +28,7 @@
  * Prototypes for library-wide functions provided by easy.c
  * Prototypes for library-wide functions provided by easy.c
  */
  */
 CURLcode Curl_senddata(struct Curl_easy *data, const void *buffer,
 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
 #ifdef USE_WEBSOCKETS
 CURLcode Curl_connect_only_attach(struct Curl_easy *data);
 CURLcode Curl_connect_only_attach(struct Curl_easy *data);

+ 1 - 1
lib/easyoptions.c

@@ -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
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
  * you should have received as part of this distribution. The terms

+ 32 - 20
lib/file.c

@@ -59,6 +59,7 @@
 #include "file.h"
 #include "file.h"
 #include "speedcheck.h"
 #include "speedcheck.h"
 #include "getinfo.h"
 #include "getinfo.h"
+#include "multiif.h"
 #include "transfer.h"
 #include "transfer.h"
 #include "url.h"
 #include "url.h"
 #include "parsedate.h" /* for the week day and month names */
 #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 fd;
   int mode;
   int mode;
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
-  char buffer[8*1024], *uphere_save;
+  char *xfer_ulbuf;
+  size_t xfer_ulblen;
   curl_off_t bytecount = 0;
   curl_off_t bytecount = 0;
   struct_stat file_stat;
   struct_stat file_stat;
   const char *sendbuf;
   const char *sendbuf;
+  bool eos = FALSE;
 
 
   /*
   /*
    * Since FILE: doesn't do the full init, we need to provide some extra
    * 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;
     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;
     size_t nread;
     ssize_t nwrite;
     ssize_t nwrite;
     size_t readcount;
     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)
     if(result)
       break;
       break;
 
 
@@ -359,16 +363,16 @@ static CURLcode file_upload(struct Curl_easy *data)
       if((curl_off_t)nread <= data->state.resume_from) {
       if((curl_off_t)nread <= data->state.resume_from) {
         data->state.resume_from -= nread;
         data->state.resume_from -= nread;
         nread = 0;
         nread = 0;
-        sendbuf = buffer;
+        sendbuf = xfer_ulbuf;
       }
       }
       else {
       else {
-        sendbuf = buffer + data->state.resume_from;
+        sendbuf = xfer_ulbuf + data->state.resume_from;
         nread -= (size_t)data->state.resume_from;
         nread -= (size_t)data->state.resume_from;
         data->state.resume_from = 0;
         data->state.resume_from = 0;
       }
       }
     }
     }
     else
     else
-      sendbuf = buffer;
+      sendbuf = xfer_ulbuf;
 
 
     /* write the data to the target */
     /* write the data to the target */
     nwrite = write(fd, sendbuf, nread);
     nwrite = write(fd, sendbuf, nread);
@@ -389,8 +393,9 @@ static CURLcode file_upload(struct Curl_easy *data)
   if(!result && Curl_pgrsUpdate(data))
   if(!result && Curl_pgrsUpdate(data))
     result = CURLE_ABORTED_BY_CALLBACK;
     result = CURLE_ABORTED_BY_CALLBACK;
 
 
+out:
   close(fd);
   close(fd);
-  data->req.upload_fromhere = uphere_save;
+  Curl_multi_xfer_ulbuf_release(data, xfer_ulbuf);
 
 
   return result;
   return result;
 }
 }
@@ -419,6 +424,8 @@ static CURLcode file_do(struct Curl_easy *data, bool *done)
   bool fstated = FALSE;
   bool fstated = FALSE;
   int fd;
   int fd;
   struct FILEPROTO *file;
   struct FILEPROTO *file;
+  char *xfer_buf;
+  size_t xfer_blen;
 
 
   *done = TRUE; /* unconditionally */
   *done = TRUE; /* unconditionally */
 
 
@@ -541,25 +548,26 @@ static CURLcode file_do(struct Curl_easy *data, bool *done)
       return CURLE_BAD_DOWNLOAD_RESUME;
       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) {
   while(!result) {
-    char tmpbuf[8*1024];
     ssize_t nread;
     ssize_t nread;
     /* Don't fill a whole buffer if we want less than all data */
     /* Don't fill a whole buffer if we want less than all data */
     size_t bytestoread;
     size_t bytestoread;
 
 
     if(size_known) {
     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
     else
-      bytestoread = sizeof(tmpbuf)-1;
+      bytestoread = xfer_blen-1;
 
 
-    nread = read(fd, tmpbuf, bytestoread);
+    nread = read(fd, xfer_buf, bytestoread);
 
 
     if(nread > 0)
     if(nread > 0)
-      tmpbuf[nread] = 0;
+      xfer_buf[nread] = 0;
 
 
     if(nread <= 0 || (size_known && (expected_size == 0)))
     if(nread <= 0 || (size_known && (expected_size == 0)))
       break;
       break;
@@ -567,18 +575,22 @@ static CURLcode file_do(struct Curl_easy *data, bool *done)
     if(size_known)
     if(size_known)
       expected_size -= nread;
       expected_size -= nread;
 
 
-    result = Curl_client_write(data, CLIENTWRITE_BODY, tmpbuf, nread);
+    result = Curl_client_write(data, CLIENTWRITE_BODY, xfer_buf, nread);
     if(result)
     if(result)
-      return result;
+      goto out;
 
 
     if(Curl_pgrsUpdate(data))
     if(Curl_pgrsUpdate(data))
       result = CURLE_ABORTED_BY_CALLBACK;
       result = CURLE_ABORTED_BY_CALLBACK;
     else
     else
       result = Curl_speedcheck(data, Curl_now());
       result = Curl_speedcheck(data, Curl_now());
+    if(result)
+      goto out;
   }
   }
   if(Curl_pgrsUpdate(data))
   if(Curl_pgrsUpdate(data))
     result = CURLE_ABORTED_BY_CALLBACK;
     result = CURLE_ABORTED_BY_CALLBACK;
 
 
+out:
+  Curl_multi_xfer_buf_release(data, xfer_buf);
   return result;
   return result;
 }
 }
 
 

+ 5 - 0
lib/fopen.c

@@ -129,7 +129,12 @@ CURLcode Curl_fopen(struct Curl_easy *data, const char *filename,
   }
   }
 
 
   result = CURLE_WRITE_ERROR;
   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);
   fd = open(tempstore, O_WRONLY | O_CREAT | O_EXCL, 0600|sb.st_mode);
+#endif
   if(fd == -1)
   if(fd == -1)
     goto fail;
     goto fail;
 
 

+ 173 - 45
lib/ftp.c

@@ -85,6 +85,14 @@
 #define INET_ADDRSTRLEN 16
 #define INET_ADDRSTRLEN 16
 #endif
 #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
 #ifdef CURL_DISABLE_VERBOSE_STRINGS
 #define ftp_pasv_verbose(a,b,c,d)  Curl_nop_stmt
 #define ftp_pasv_verbose(a,b,c,d)  Curl_nop_stmt
 #endif
 #endif
@@ -143,7 +151,7 @@ static CURLcode wc_statemach(struct Curl_easy *data);
 static void wc_data_dtor(void *ptr);
 static void wc_data_dtor(void *ptr);
 static CURLcode ftp_state_retr(struct Curl_easy *data, curl_off_t filesize);
 static CURLcode ftp_state_retr(struct Curl_easy *data, curl_off_t filesize);
 static CURLcode ftp_readresp(struct Curl_easy *data,
 static CURLcode ftp_readresp(struct Curl_easy *data,
-                             curl_socket_t sockfd,
+                             int sockindex,
                              struct pingpong *pp,
                              struct pingpong *pp,
                              int *ftpcode,
                              int *ftpcode,
                              size_t *size);
                              size_t *size);
@@ -247,6 +255,98 @@ static void freedirs(struct ftp_conn *ftpc)
   Curl_safefree(ftpc->newhost);
   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()
  * AcceptServerConnect()
@@ -412,8 +512,32 @@ static CURLcode ReceivedServerConnect(struct Curl_easy *data, bool *received)
   }
   }
   if(response) {
   if(response) {
     infof(data, "Ctrl conn has data while waiting for data conn");
     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);
     (void)Curl_GetFTPResponse(data, &nread, &ftpcode);
 
 
+    infof(data, "FTP code: %03d", ftpcode);
+
     if(ftpcode/100 > 3)
     if(ftpcode/100 > 3)
       return CURLE_FTP_ACCEPT_FAILED;
       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 */
     /* set the SO_SNDBUF for the secondary socket for those who need it */
     Curl_sndbufset(conn->sock[SECONDARYSOCKET]);
     Curl_sndbufset(conn->sock[SECONDARYSOCKET]);
 
 
-    Curl_setup_transfer(data, -1, -1, FALSE, SECONDARYSOCKET);
+    Curl_xfer_setup(data, -1, -1, FALSE, SECONDARYSOCKET);
   }
   }
   else {
   else {
     /* FTP download: */
     /* 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 */
   conn->proto.ftpc.pp.pending_resp = TRUE; /* expect server response */
@@ -525,14 +649,6 @@ out:
   return result;
   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,
 static bool ftp_endofresp(struct Curl_easy *data, struct connectdata *conn,
                           char *line, size_t len, int *code)
                           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,
 static CURLcode ftp_readresp(struct Curl_easy *data,
-                             curl_socket_t sockfd,
+                             int sockindex,
                              struct pingpong *pp,
                              struct pingpong *pp,
                              int *ftpcode, /* return the ftp-code if done */
                              int *ftpcode, /* return the ftp-code if done */
                              size_t *size) /* size of the response */
                              size_t *size) /* size of the response */
 {
 {
   int code;
   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
 #ifdef HAVE_GSSAPI
   {
   {
@@ -689,7 +805,7 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data,
         break;
         break;
       }
       }
     }
     }
-    result = ftp_readresp(data, sockfd, pp, ftpcode, &nread);
+    result = ftp_readresp(data, FIRSTSOCKET, pp, ftpcode, &nread);
     if(result)
     if(result)
       break;
       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
    * remote site, or we could wait for that site to connect to us. Or just
    * handle ordinary commands.
    * handle ordinary commands.
    */
    */
-
   DEBUGF(infof(data, "ftp_domore_getsock()"));
   DEBUGF(infof(data, "ftp_domore_getsock()"));
-  if(conn->cfilter[SECONDARYSOCKET]
-     && !Curl_conn_is_connected(conn, SECONDARYSOCKET))
-    return 0;
 
 
   if(FTP_STOP == ftpc->state) {
   if(FTP_STOP == ftpc->state) {
-    int bits = GETSOCK_READSOCK(0);
-
     /* if stopped and still in this state, then we're also waiting for a
     /* if stopped and still in this state, then we're also waiting for a
        connect on the secondary connection */
        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];
     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);
   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;
     conn->bits.ftp_use_eprt = TRUE;
 #endif
 #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++) {
   for(; fcmd != DONE; fcmd++) {
 
 
     if(!conn->bits.ftp_use_eprt && (EPRT == 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 */
   /* store which command was sent */
   ftpc->count1 = fcmd;
   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);
   ftp_state(data, FTP_PORT);
 
 
 out:
 out:
@@ -1572,10 +1683,10 @@ static CURLcode ftp_state_ul_setup(struct Curl_easy *data,
     append = TRUE;
     append = TRUE;
 
 
     /* Let's read off the proper amount of bytes from the input. */
     /* 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);
       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);
       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");
         infof(data, "File already completely uploaded");
 
 
         /* no data to transfer */
         /* 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
         /* Set ->transfer so that we won't get any error in
          * ftp_done() because we didn't transfer anything! */
          * 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)
   if(conn->bits.tunnel_proxy || conn->bits.socksproxy)
     return conn->host.name;
     return conn->host.name;
 #endif
 #endif
-  return conn->primary_ip;
+  return conn->primary.remote_ip;
 }
 }
 
 
 static bool match_pasv_6nums(const char *p,
 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 ?
     const char * const host_name = conn->bits.socksproxy ?
       conn->socks_proxy.host.name : conn->http_proxy.host.name;
       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)
     if(rc == CURLRESOLV_PENDING)
       /* BLOCKING, ignores the return code but 'addr' will be NULL in
       /* BLOCKING, ignores the return code but 'addr' will be NULL in
          case of failure */
          case of failure */
       (void)Curl_resolver_wait_resolv(data, &addr);
       (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) {
     if(!addr) {
       failf(data, "Can't resolve proxy host %s:%hu", host_name, connectport);
       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) {
     if(ftp->downloadsize == 0) {
       /* no data to transfer */
       /* 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");
       infof(data, "File already completely downloaded");
 
 
       /* Set ->transfer so that we won't get any error in ftp_done()
       /* 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)
                                  struct connectdata *conn)
 {
 {
   CURLcode result;
   CURLcode result;
-  curl_socket_t sock = conn->sock[FIRSTSOCKET];
   int ftpcode;
   int ftpcode;
   struct ftp_conn *ftpc = &conn->proto.ftpc;
   struct ftp_conn *ftpc = &conn->proto.ftpc;
   struct pingpong *pp = &ftpc->pp;
   struct pingpong *pp = &ftpc->pp;
@@ -2702,7 +2812,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
   if(pp->sendleft)
   if(pp->sendleft)
     return Curl_pp_flushsend(data, pp);
     return Curl_pp_flushsend(data, pp);
 
 
-  result = ftp_readresp(data, sock, pp, &ftpcode, &nread);
+  result = ftp_readresp(data, FIRSTSOCKET, pp, &ftpcode, &nread);
   if(result)
   if(result)
     return result;
     return result;
 
 
@@ -3702,7 +3812,7 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
   }
   }
 
 
   /* no data to transfer */
   /* 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) {
   if(!ftpc->wait_data_conn) {
     /* no waiting for the data connection so this is now complete */
     /* 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 */
   *done = FALSE; /* default to false */
   ftpc->wait_data_conn = FALSE; /* default to no such wait */
   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) {
   if(data->state.wildcardmatch) {
     result = wc_statemach(data);
     result = wc_statemach(data);
     if(data->wildcard->state == CURLWC_SKIP ||
     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)
   if(ftp->transfer != PPTRANSFER_BODY)
     /* no data to transfer */
     /* no data to transfer */
-    Curl_setup_transfer(data, -1, -1, FALSE, -1);
+    Curl_xfer_setup(data, -1, -1, FALSE, -1);
   else if(!connected)
   else if(!connected)
     /* since we didn't connect now, we want do_more to get called */
     /* since we didn't connect now, we want do_more to get called */
     conn->bits.do_more = TRUE;
     conn->bits.do_more = TRUE;

+ 17 - 9
lib/getinfo.c

@@ -76,10 +76,10 @@ CURLcode Curl_initinfo(struct Curl_easy *data)
   free(info->wouldredirect);
   free(info->wouldredirect);
   info->wouldredirect = NULL;
   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->retry_after = 0;
 
 
   info->conn_scheme = 0;
   info->conn_scheme = 0;
@@ -153,12 +153,12 @@ static CURLcode getinfo_char(struct Curl_easy *data, CURLINFO info,
     break;
     break;
   case CURLINFO_PRIMARY_IP:
   case CURLINFO_PRIMARY_IP:
     /* Return the ip address of the most recent (primary) connection */
     /* 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;
     break;
   case CURLINFO_LOCAL_IP:
   case CURLINFO_LOCAL_IP:
     /* Return the source/local ip address of the most recent (primary)
     /* Return the source/local ip address of the most recent (primary)
        connection */
        connection */
-    *param_charp = data->info.conn_local_ip;
+    *param_charp = data->info.primary.local_ip;
     break;
     break;
   case CURLINFO_RTSP_SESSION_ID:
   case CURLINFO_RTSP_SESSION_ID:
     *param_charp = data->set.str[STRING_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;
     *param_charp = NULL;
 #endif
 #endif
     break;
     break;
-
   default:
   default:
     return CURLE_UNKNOWN_OPTION;
     return CURLE_UNKNOWN_OPTION;
   }
   }
@@ -285,11 +284,11 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info,
     break;
     break;
   case CURLINFO_PRIMARY_PORT:
   case CURLINFO_PRIMARY_PORT:
     /* Return the (remote) port of the most recent (primary) connection */
     /* 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;
     break;
   case CURLINFO_LOCAL_PORT:
   case CURLINFO_LOCAL_PORT:
     /* Return the local port of the most recent (primary) connection */
     /* 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;
     break;
   case CURLINFO_PROXY_ERROR:
   case CURLINFO_PROXY_ERROR:
     *param_longp = (long)data->info.pxcode;
     *param_longp = (long)data->info.pxcode;
@@ -334,6 +333,15 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info,
   case CURLINFO_PROTOCOL:
   case CURLINFO_PROTOCOL:
     *param_longp = data->info.conn_protocol;
     *param_longp = data->info.conn_protocol;
     break;
     break;
+  case CURLINFO_USED_PROXY:
+    *param_longp =
+#ifdef CURL_DISABLE_PROXY
+      0
+#else
+      data->info.used_proxy
+#endif
+      ;
+    break;
   default:
   default:
     return CURLE_UNKNOWN_OPTION;
     return CURLE_UNKNOWN_OPTION;
   }
   }

+ 5 - 5
lib/gopher.c

@@ -139,8 +139,8 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done)
   char *sel = NULL;
   char *sel = NULL;
   char *sel_org = NULL;
   char *sel_org = NULL;
   timediff_t timeout_ms;
   timediff_t timeout_ms;
-  ssize_t amount, k;
-  size_t len;
+  ssize_t k;
+  size_t amount, len;
   int what;
   int what;
 
 
   *done = TRUE; /* unconditionally */
   *done = TRUE; /* unconditionally */
@@ -185,7 +185,7 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done)
     if(strlen(sel) < 1)
     if(strlen(sel) < 1)
       break;
       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! */
     if(!result) { /* Which may not have written it all! */
       result = Curl_client_write(data, CLIENTWRITE_HEADER, sel, amount);
       result = Curl_client_write(data, CLIENTWRITE_HEADER, sel, amount);
       if(result)
       if(result)
@@ -227,7 +227,7 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done)
   free(sel_org);
   free(sel_org);
 
 
   if(!result)
   if(!result)
-    result = Curl_nwrite(data, FIRSTSOCKET, "\r\n", 2, &amount);
+    result = Curl_xfer_send(data, "\r\n", 2, &amount);
   if(result) {
   if(result) {
     failf(data, "Failed sending Gopher request");
     failf(data, "Failed sending Gopher request");
     return result;
     return result;
@@ -236,7 +236,7 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done)
   if(result)
   if(result)
     return result;
     return result;
 
 
-  Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
+  Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1);
   return CURLE_OK;
   return CURLE_OK;
 }
 }
 #endif /* CURL_DISABLE_GOPHER */
 #endif /* CURL_DISABLE_GOPHER */

+ 58 - 3
lib/headers.c

@@ -27,6 +27,7 @@
 #include "urldata.h"
 #include "urldata.h"
 #include "strdup.h"
 #include "strdup.h"
 #include "strcase.h"
 #include "strcase.h"
+#include "sendf.h"
 #include "headers.h"
 #include "headers.h"
 
 
 /* The last 3 #include files should be in this order */
 /* 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);
   Curl_llist_init(&data->state.httphdrs, NULL);
   data->state.prevhead = 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.
  * 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;
     n = e->next;
     free(hs);
     free(hs);
   }
   }
-  headers_init(data);
+  headers_reset(data);
   return CURLE_OK;
   return CURLE_OK;
 }
 }
 
 

+ 7 - 0
lib/headers.h

@@ -36,6 +36,12 @@ struct Curl_header_store {
   char buffer[1]; /* this is the raw header blob */
   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.
  * 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);
 CURLcode Curl_headers_cleanup(struct Curl_easy *data);
 
 
 #else
 #else
+#define Curl_headers_init(x) CURLE_OK
 #define Curl_headers_push(x,y,z) CURLE_OK
 #define Curl_headers_push(x,y,z) CURLE_OK
 #define Curl_headers_cleanup(x) Curl_nop_stmt
 #define Curl_headers_cleanup(x) Curl_nop_stmt
 #endif
 #endif

+ 1 - 1
lib/hostip.c

@@ -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,
   size_t entry_len = create_hostcache_id(hostname, 0, port,
                                          entry_id, sizeof(entry_id));
                                          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);
   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 */
   /* No entry found in cache, check if we might have a wildcard entry */

+ 5 - 12
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)
 static CURLcode hsts_load(struct hsts *h, const char *file)
 {
 {
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
-  char *line = NULL;
   FILE *fp;
   FILE *fp;
 
 
   /* we need a private copy of the file name so that the hsts cache file
   /* 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);
   fp = fopen(file, FOPEN_READTEXT);
   if(fp) {
   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))
       while(*lineptr && ISBLANK(*lineptr))
         lineptr++;
         lineptr++;
       if(*lineptr == '#')
       if(*lineptr == '#')
@@ -536,15 +534,10 @@ static CURLcode hsts_load(struct hsts *h, const char *file)
 
 
       hsts_add(h, lineptr);
       hsts_add(h, lineptr);
     }
     }
-    free(line); /* free the line buffer */
+    Curl_dyn_free(&buf); /* free the line buffer */
     fclose(fp);
     fclose(fp);
   }
   }
   return result;
   return result;
-
-fail:
-  Curl_safefree(h->filename);
-  fclose(fp);
-  return CURLE_OUT_OF_MEMORY;
 }
 }
 
 
 /*
 /*

File diff ditekan karena terlalu besar
+ 302 - 798
lib/http.c


+ 14 - 45
lib/http.h

@@ -74,12 +74,6 @@ char *Curl_checkProxyheaders(struct Curl_easy *data,
                              const char *thisheader,
                              const char *thisheader,
                              const size_t thislen);
                              const size_t thislen);
 struct HTTP; /* see below */
 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,
 CURLcode Curl_add_timecondition(struct Curl_easy *data,
 #ifndef USE_HYPER
 #ifndef USE_HYPER
@@ -100,10 +94,6 @@ CURLcode Curl_dynhds_add_custom(struct Curl_easy *data,
                                 bool is_connect,
                                 bool is_connect,
                                 struct dynhds *hds);
                                 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,
 void Curl_http_method(struct Curl_easy *data, struct connectdata *conn,
                       const char **method, Curl_HttpReq *);
                       const char **method, Curl_HttpReq *);
 CURLcode Curl_http_useragent(struct Curl_easy *data);
 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,
 CURLcode Curl_http_statusline(struct Curl_easy *data,
                               struct connectdata *conn);
                               struct connectdata *conn);
 CURLcode Curl_http_header(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_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,
 bool Curl_use_http_1_1plus(const struct Curl_easy *data,
                            const struct connectdata *conn);
                            const struct connectdata *conn);
 #ifndef CURL_DISABLE_COOKIES
 #ifndef CURL_DISABLE_COOKIES
@@ -129,14 +119,9 @@ CURLcode Curl_http_cookies(struct Curl_easy *data,
 #else
 #else
 #define Curl_http_cookies(a,b,c) CURLE_OK
 #define Curl_http_cookies(a,b,c) CURLE_OK
 #endif
 #endif
-CURLcode Curl_http_resume(struct Curl_easy *data,
-                          struct connectdata *conn,
-                          Curl_HttpReq httpreq);
 CURLcode Curl_http_range(struct Curl_easy *data,
 CURLcode Curl_http_range(struct Curl_easy *data,
                          Curl_HttpReq httpreq);
                          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 */
 /* protocol-specific functions set up to be called by the main engine */
 CURLcode Curl_http_setup_conn(struct Curl_easy *data,
 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);
                          curl_socket_t *socks);
 CURLcode Curl_http_write_resp(struct Curl_easy *data,
 CURLcode Curl_http_write_resp(struct Curl_easy *data,
                               const char *buf, size_t blen,
                               const char *buf, size_t blen,
-                              bool is_eos,
-                              bool *done);
+                              bool is_eos);
 
 
 /* These functions are in http.c */
 /* These functions are in http.c */
 CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
 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. */
    version. This count includes CONNECT response headers. */
 #define MAX_HTTP_RESP_HEADER_SIZE (300*1024)
 #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 */
 #endif /* CURL_DISABLE_HTTP */
 
 
 /****************************************************************************
 /****************************************************************************
  * HTTP unique setup
  * HTTP unique setup
  ***************************************************************************/
  ***************************************************************************/
 struct HTTP {
 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
 #ifndef CURL_DISABLE_HTTP
   void *h2_ctx;              /* HTTP/2 implementation context */
   void *h2_ctx;              /* HTTP/2 implementation context */
   void *h3_ctx;              /* HTTP/3 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
 #endif
 };
 };
 
 
@@ -227,8 +197,7 @@ CURLcode Curl_http_size(struct Curl_easy *data);
 
 
 CURLcode Curl_http_write_resp_hds(struct Curl_easy *data,
 CURLcode Curl_http_write_resp_hds(struct Curl_easy *data,
                                   const char *buf, size_t blen,
                                   const char *buf, size_t blen,
-                                  size_t *pconsumed,
-                                  bool *done);
+                                  size_t *pconsumed);
 
 
 /**
 /**
  * Curl_http_output_auth() setups the authentication headers for the
  * Curl_http_output_auth() setups the authentication headers for the

+ 137 - 192
lib/http2.c

@@ -121,7 +121,6 @@ static ssize_t populate_binsettings(uint8_t *binsettings,
 
 
 struct cf_h2_ctx {
 struct cf_h2_ctx {
   nghttp2_session *h2;
   nghttp2_session *h2;
-  uint32_t max_concurrent_streams;
   /* The easy handle used in the current filter call, cleared at return */
   /* The easy handle used in the current filter call, cleared at return */
   struct cf_call_data call_data;
   struct cf_call_data call_data;
 
 
@@ -130,6 +129,7 @@ struct cf_h2_ctx {
   struct bufc_pool stream_bufcp; /* spares for stream buffers */
   struct bufc_pool stream_bufcp; /* spares for stream buffers */
 
 
   size_t drain_total; /* sum of all stream's UrlState drain */
   size_t drain_total; /* sum of all stream's UrlState drain */
+  uint32_t max_concurrent_streams;
   int32_t goaway_error;
   int32_t goaway_error;
   int32_t last_stream_id;
   int32_t last_stream_id;
   BIT(conn_closed);
   BIT(conn_closed);
@@ -169,11 +169,9 @@ static CURLcode h2_progress_egress(struct Curl_cfilter *cf,
                                   struct Curl_easy *data);
                                   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 recvbuf; /* response buffer */
   struct bufq sendbuf; /* request buffer */
   struct bufq sendbuf; /* request buffer */
   struct h1_req_parser h1; /* parsing the request */
   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 resp_hds_len; /* amount of response header bytes in recvbuf */
   size_t upload_blocked_len;
   size_t upload_blocked_len;
   curl_off_t upload_left; /* number of request bytes left to upload */
   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 */
   char **push_headers;       /* allocated array */
   size_t push_headers_used;  /* number of entries filled in */
   size_t push_headers_used;  /* number of entries filled in */
@@ -189,16 +188,18 @@ struct stream_ctx {
   int status_code; /* HTTP response status code */
   int status_code; /* HTTP response status code */
   uint32_t error; /* stream error code */
   uint32_t error; /* stream error code */
   uint32_t local_window_size; /* the local recv window size */
   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 \
                              ((struct HTTP *)(d)->req.p.http)->h2_ctx \
                                : NULL))
                                : NULL))
 #define H2_STREAM_LCTX(d)   ((struct HTTP *)(d)->req.p.http)->h2_ctx
 #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,
 static void drain_stream(struct Curl_cfilter *cf,
                          struct Curl_easy *data,
                          struct Curl_easy *data,
-                         struct stream_ctx *stream)
+                         struct h2_stream_ctx *stream)
 {
 {
   unsigned char bits;
   unsigned char bits;
 
 
@@ -229,10 +230,10 @@ static void drain_stream(struct Curl_cfilter *cf,
 
 
 static CURLcode http2_data_setup(struct Curl_cfilter *cf,
 static CURLcode http2_data_setup(struct Curl_cfilter *cf,
                                  struct Curl_easy *data,
                                  struct Curl_easy *data,
-                                 struct stream_ctx **pstream)
+                                 struct h2_stream_ctx **pstream)
 {
 {
   struct cf_h2_ctx *ctx = cf->ctx;
   struct cf_h2_ctx *ctx = cf->ctx;
-  struct stream_ctx *stream;
+  struct h2_stream_ctx *stream;
 
 
   (void)cf;
   (void)cf;
   DEBUGASSERT(data);
   DEBUGASSERT(data);
@@ -253,8 +254,6 @@ static CURLcode http2_data_setup(struct Curl_cfilter *cf,
   stream->id = -1;
   stream->id = -1;
   Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp,
   Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp,
                   H2_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE);
                   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_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
   Curl_dynhds_init(&stream->resp_trailers, 0, DYN_HTTP_REQUEST);
   Curl_dynhds_init(&stream->resp_trailers, 0, DYN_HTTP_REQUEST);
   stream->resp_hds_len = 0;
   stream->resp_hds_len = 0;
@@ -265,20 +264,28 @@ static CURLcode http2_data_setup(struct Curl_cfilter *cf,
   stream->error = NGHTTP2_NO_ERROR;
   stream->error = NGHTTP2_NO_ERROR;
   stream->local_window_size = H2_STREAM_WINDOW_SIZE;
   stream->local_window_size = H2_STREAM_WINDOW_SIZE;
   stream->upload_left = 0;
   stream->upload_left = 0;
+  stream->nrcvd_data = 0;
 
 
   H2_STREAM_LCTX(data) = stream;
   H2_STREAM_LCTX(data) = stream;
   *pstream = stream;
   *pstream = stream;
   return CURLE_OK;
   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 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);
   DEBUGASSERT(ctx);
-  (void)premature;
   if(!stream)
   if(!stream)
     return;
     return;
 
 
@@ -298,34 +305,15 @@ static void http2_data_done(struct Curl_cfilter *cf,
                                 stream->id, NGHTTP2_STREAM_CLOSED);
                                 stream->id, NGHTTP2_STREAM_CLOSED);
       flush_egress = TRUE;
       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)
     if(flush_egress)
       nghttp2_session_send(ctx->h2);
       nghttp2_session_send(ctx->h2);
   }
   }
 
 
   Curl_bufq_free(&stream->sendbuf);
   Curl_bufq_free(&stream->sendbuf);
-  Curl_bufq_free(&stream->recvbuf);
   Curl_h1_req_parse_free(&stream->h1);
   Curl_h1_req_parse_free(&stream->h1);
   Curl_dynhds_free(&stream->resp_trailers);
   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);
   free(stream);
   H2_STREAM_LCTX(data) = NULL;
   H2_STREAM_LCTX(data) = NULL;
 }
 }
@@ -411,7 +399,7 @@ static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf,
                                bool via_h1_upgrade)
                                bool via_h1_upgrade)
 {
 {
   struct cf_h2_ctx *ctx = cf->ctx;
   struct cf_h2_ctx *ctx = cf->ctx;
-  struct stream_ctx *stream;
+  struct h2_stream_ctx *stream;
   CURLcode result = CURLE_OUT_OF_MEMORY;
   CURLcode result = CURLE_OUT_OF_MEMORY;
   int rc;
   int rc;
   nghttp2_session_callbacks *cbs = NULL;
   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))
   if(!h || !GOOD_EASY_HANDLE(h->data))
     return NULL;
     return NULL;
   else {
   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)
     if(stream && num < stream->push_headers_used)
       return stream->push_headers[num];
       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)
 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 len;
   size_t i;
   size_t i;
   /* Verify that we got a good easy handle in the push header struct,
   /* 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);
       (void)Curl_close(&second);
     }
     }
     else {
     else {
-      struct stream_ctx *second_stream;
+      struct h2_stream_ctx *second_stream;
 
 
       second->req.p.http = http;
       second->req.p.http = http;
       http2_data_setup(cf, second, &second_stream);
       http2_data_setup(cf, second, &second_stream);
@@ -850,9 +838,8 @@ fail:
 static void discard_newhandle(struct Curl_cfilter *cf,
 static void discard_newhandle(struct Curl_cfilter *cf,
                               struct Curl_easy *newhandle)
                               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);
   (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",
   CURL_TRC_CF(data, cf, "[%d] PUSH_PROMISE received",
               frame->promised_stream_id);
               frame->promised_stream_id);
   if(data->multi->push_cb) {
   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;
     struct curl_pushheaders heads;
     CURLMcode rc;
     CURLMcode rc;
     CURLcode result;
     CURLcode result;
-    size_t i;
     /* clone the parent */
     /* clone the parent */
     struct Curl_easy *newhandle = h2_duphandle(cf, data);
     struct Curl_easy *newhandle = h2_duphandle(cf, data);
     if(!newhandle) {
     if(!newhandle) {
@@ -917,11 +903,7 @@ static int push_promise(struct Curl_cfilter *cf,
     Curl_set_in_callback(data, false);
     Curl_set_in_callback(data, false);
 
 
     /* free the headers again */
     /* 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) {
     if(rv) {
       DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT));
       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,
                                   struct Curl_easy *data,
                                   const char *buf, size_t blen)
                                   const char *buf, size_t blen)
 {
 {
-  struct stream_ctx *stream = H2_STREAM_CTX(data);
-  ssize_t nwritten;
-  CURLcode result;
-
   (void)cf;
   (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,
 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)
                                 const nghttp2_frame *frame)
 {
 {
   struct cf_h2_ctx *ctx = cf->ctx;
   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;
   int32_t stream_id = frame->hd.stream_id;
   CURLcode result;
   CURLcode result;
-  size_t rbuflen;
   int rv;
   int rv;
 
 
   if(!stream) {
   if(!stream) {
@@ -999,9 +970,8 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf,
 
 
   switch(frame->hd.type) {
   switch(frame->hd.type) {
   case NGHTTP2_DATA:
   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(
                 nghttp2_session_get_stream_effective_recv_data_length(
                   ctx->h2, stream->id),
                   ctx->h2, stream->id),
                 nghttp2_session_get_stream_effective_local_window_size(
                 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) {
     if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
       drain_stream(cf, data, 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;
     break;
   case NGHTTP2_HEADERS:
   case NGHTTP2_HEADERS:
     if(stream->bodystarted) {
     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.
          * 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. */
          * To be safe, we UNHOLD a stream in order not to stall. */
         if(CURL_WANT_SEND(data)) {
         if(CURL_WANT_SEND(data)) {
-          struct stream_ctx *stream = H2_STREAM_CTX(data);
+          struct h2_stream_ctx *stream = H2_STREAM_CTX(data);
           if(stream)
           if(stream)
             drain_stream(cf, data, 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)
                               const uint8_t *mem, size_t len, void *userp)
 {
 {
   struct Curl_cfilter *cf = 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;
   struct Curl_easy *data_s;
-  ssize_t nwritten;
   CURLcode result;
   CURLcode result;
   (void)flags;
   (void)flags;
 
 
@@ -1296,18 +1252,15 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
   if(!stream)
   if(!stream)
     return NGHTTP2_ERR_CALLBACK_FAILURE;
     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 */
   /* if we receive data for another handle, wake that up */
   drain_stream(cf, data_s, stream);
   drain_stream(cf, data_s, stream);
-
-  DEBUGASSERT((size_t)nwritten == len);
   return 0;
   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_cfilter *cf = userp;
   struct Curl_easy *data_s, *call_data = CF_DATA_CURRENT(cf);
   struct Curl_easy *data_s, *call_data = CF_DATA_CURRENT(cf);
-  struct stream_ctx *stream;
+  struct h2_stream_ctx *stream;
   int rv;
   int rv;
   (void)session;
   (void)session;
 
 
@@ -1374,7 +1327,7 @@ static int on_begin_headers(nghttp2_session *session,
                             const nghttp2_frame *frame, void *userp)
                             const nghttp2_frame *frame, void *userp)
 {
 {
   struct Curl_cfilter *cf = userp;
   struct Curl_cfilter *cf = userp;
-  struct stream_ctx *stream;
+  struct h2_stream_ctx *stream;
   struct Curl_easy *data_s = NULL;
   struct Curl_easy *data_s = NULL;
 
 
   (void)cf;
   (void)cf;
@@ -1403,7 +1356,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
                      void *userp)
                      void *userp)
 {
 {
   struct Curl_cfilter *cf = userp;
   struct Curl_cfilter *cf = userp;
-  struct stream_ctx *stream;
+  struct h2_stream_ctx *stream;
   struct Curl_easy *data_s;
   struct Curl_easy *data_s;
   int32_t stream_id = frame->hd.stream_id;
   int32_t stream_id = frame->hd.stream_id;
   CURLcode result;
   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 *
       stream->push_headers = malloc(stream->push_headers_alloc *
                                     sizeof(char *));
                                     sizeof(char *));
       if(!stream->push_headers)
       if(!stream->push_headers)
-        return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
+        return NGHTTP2_ERR_CALLBACK_FAILURE;
       stream->push_headers_used = 0;
       stream->push_headers_used = 0;
     }
     }
     else if(stream->push_headers_used ==
     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) {
       if(stream->push_headers_alloc > 1000) {
         /* this is beyond crazy many headers, bail out */
         /* this is beyond crazy many headers, bail out */
         failf(data_s, "Too many PUSH_PROMISE headers");
         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;
       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) {
       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;
       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_cfilter *cf = userp;
   struct Curl_easy *data_s;
   struct Curl_easy *data_s;
-  struct stream_ctx *stream = NULL;
+  struct h2_stream_ctx *stream = NULL;
   CURLcode result;
   CURLcode result;
   ssize_t nread;
   ssize_t nread;
   (void)source;
   (void)source;
@@ -1667,7 +1620,7 @@ static CURLcode http2_data_done_send(struct Curl_cfilter *cf,
 {
 {
   struct cf_h2_ctx *ctx = cf->ctx;
   struct cf_h2_ctx *ctx = cf->ctx;
   CURLcode result = CURLE_OK;
   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)
   if(!ctx || !ctx->h2 || !stream)
     goto out;
     goto out;
@@ -1691,7 +1644,7 @@ out:
 
 
 static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf,
 static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf,
                                          struct Curl_easy *data,
                                          struct Curl_easy *data,
-                                         struct stream_ctx *stream,
+                                         struct h2_stream_ctx *stream,
                                          CURLcode *err)
                                          CURLcode *err)
 {
 {
   ssize_t rv = 0;
   ssize_t rv = 0;
@@ -1713,7 +1666,7 @@ static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf,
   }
   }
   else if(stream->reset) {
   else if(stream->reset) {
     failf(data, "HTTP/2 stream %u was reset", stream->id);
     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;
     return -1;
   }
   }
 
 
@@ -1787,7 +1740,7 @@ static void h2_pri_spec(struct Curl_easy *data,
                         nghttp2_priority_spec *pri_spec)
                         nghttp2_priority_spec *pri_spec)
 {
 {
   struct Curl_data_priority *prio = &data->set.priority;
   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;
   int32_t depstream_id = depstream? depstream->id:0;
   nghttp2_priority_spec_init(pri_spec, depstream_id,
   nghttp2_priority_spec_init(pri_spec, depstream_id,
                              sweight_wanted(data),
                              sweight_wanted(data),
@@ -1805,7 +1758,7 @@ static CURLcode h2_progress_egress(struct Curl_cfilter *cf,
                                   struct Curl_easy *data)
                                   struct Curl_easy *data)
 {
 {
   struct cf_h2_ctx *ctx = cf->ctx;
   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;
   int rv = 0;
 
 
   if(stream && stream->id > 0 &&
   if(stream && stream->id > 0 &&
@@ -1838,40 +1791,26 @@ out:
 }
 }
 
 
 static ssize_t stream_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
 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)
                            char *buf, size_t len, CURLcode *err)
 {
 {
   struct cf_h2_ctx *ctx = cf->ctx;
   struct cf_h2_ctx *ctx = cf->ctx;
   ssize_t nread = -1;
   ssize_t nread = -1;
 
 
+  (void)buf;
   *err = CURLE_AGAIN;
   *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(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;
+  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 = data->req.bytecount? CURLE_PARTIAL_FILE : CURLE_HTTP2;
     nread = -1;
     nread = -1;
   }
   }
 
 
-out:
   if(nread < 0 && *err != CURLE_AGAIN)
   if(nread < 0 && *err != CURLE_AGAIN)
     CURL_TRC_CF(data, cf, "[%d] stream_recv(len=%zu) -> %zd, %d",
     CURL_TRC_CF(data, cf, "[%d] stream_recv(len=%zu) -> %zd, %d",
                 stream->id, len, nread, *err);
                 stream->id, len, nread, *err);
@@ -1879,10 +1818,11 @@ out:
 }
 }
 
 
 static CURLcode h2_progress_ingress(struct Curl_cfilter *cf,
 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 cf_h2_ctx *ctx = cf->ctx;
-  struct stream_ctx *stream;
+  struct h2_stream_ctx *stream;
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
   ssize_t nread;
   ssize_t nread;
 
 
@@ -1899,16 +1839,17 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf,
    * all network input */
    * all network input */
   while(!ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) {
   while(!ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) {
     stream = H2_STREAM_CTX(data);
     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
       /* We would like to abort here and stop processing, so that
        * the transfer loop can handle the data/close here. However,
        * the transfer loop can handle the data/close here. However,
        * this may leave data in underlying buffers that will not
        * this may leave data in underlying buffers that will not
        * be consumed. */
        * be consumed. */
       if(!cf->next || !cf->next->cft->has_data_pending(cf->next, data))
       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(nread < 0) {
       if(result != CURLE_AGAIN) {
       if(result != CURLE_AGAIN) {
         failf(data, "Failed receiving HTTP2 data: %d(%s)", result,
         failf(data, "Failed receiving HTTP2 data: %d(%s)", result,
@@ -1923,8 +1864,9 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf,
       break;
       break;
     }
     }
     else {
     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))
     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)
                           char *buf, size_t len, CURLcode *err)
 {
 {
   struct cf_h2_ctx *ctx = cf->ctx;
   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;
   ssize_t nread = -1;
   CURLcode result;
   CURLcode result;
   struct cf_call_data save;
   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;
     goto out;
 
 
   if(nread < 0) {
   if(nread < 0) {
-    *err = h2_progress_ingress(cf, data);
+    *err = h2_progress_ingress(cf, data, len);
     if(*err)
     if(*err)
       goto out;
       goto out;
 
 
@@ -2011,9 +1953,8 @@ out:
     nread = -1;
     nread = -1;
   }
   }
   CURL_TRC_CF(data, cf, "[%d] cf_recv(len=%zu) -> %zd %d, "
   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,
               stream->id, len, nread, *err,
-              Curl_bufq_len(&stream->recvbuf),
               nghttp2_session_get_stream_effective_recv_data_length(
               nghttp2_session_get_stream_effective_recv_data_length(
                 ctx->h2, stream->id),
                 ctx->h2, stream->id),
               nghttp2_session_get_stream_effective_local_window_size(
               nghttp2_session_get_stream_effective_local_window_size(
@@ -2025,12 +1966,13 @@ out:
   return nread;
   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,
                          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 cf_h2_ctx *ctx = cf->ctx;
-  struct stream_ctx *stream = NULL;
+  struct h2_stream_ctx *stream = NULL;
   struct dynhds h2_headers;
   struct dynhds h2_headers;
   nghttp2_nv *nva = NULL;
   nghttp2_nv *nva = NULL;
   const void *body = NULL;
   const void *body = NULL;
@@ -2040,6 +1982,7 @@ static ssize_t h2_submit(struct stream_ctx **pstream,
   nghttp2_priority_spec pri_spec;
   nghttp2_priority_spec pri_spec;
   ssize_t nwritten;
   ssize_t nwritten;
 
 
+  *phdslen = 0;
   Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST);
   Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST);
 
 
   *err = http2_data_setup(cf, data, &stream);
   *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);
   nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err);
   if(nwritten < 0)
   if(nwritten < 0)
     goto out;
     goto out;
+  *phdslen = (size_t)nwritten;
   if(!stream->h1.done) {
   if(!stream->h1.done) {
     /* need more data */
     /* need more data */
     goto out;
     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)
                           const void *buf, size_t len, CURLcode *err)
 {
 {
   struct cf_h2_ctx *ctx = cf->ctx;
   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;
   struct cf_call_data save;
   int rv;
   int rv;
   ssize_t nwritten;
   ssize_t nwritten;
+  size_t hdslen = 0;
   CURLcode result;
   CURLcode result;
   int blocked = 0, was_blocked = 0;
   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 {
   else {
-    nwritten = h2_submit(&stream, cf, data, buf, len, err);
+    nwritten = h2_submit(&stream, cf, data, buf, len, &hdslen, err);
     if(nwritten < 0) {
     if(nwritten < 0) {
       goto out;
       goto out;
     }
     }
     DEBUGASSERT(stream);
     DEBUGASSERT(stream);
+    DEBUGASSERT(hdslen <= (size_t)nwritten);
   }
   }
 
 
   /* Call the nghttp2 send loop and flush to write ALL buffered data,
   /* 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. */
      * frame buffer or our network out buffer. */
     size_t rwin = nghttp2_session_get_stream_remote_window_size(ctx->h2,
     size_t rwin = nghttp2_session_get_stream_remote_window_size(ctx->h2,
                                                                 stream->id);
                                                                 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 "
     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,
                 stream->id, len,
                 nghttp2_session_get_remote_window_size(ctx->h2), rwin,
                 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)) {
   else if(should_close_session(ctx)) {
     /* nghttp2 thinks this session is done. If the stream has not been
     /* 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);
   sock = Curl_conn_cf_get_socket(cf, data);
   Curl_pollset_check(data, ps, sock, &want_recv, &want_send);
   Curl_pollset_check(data, ps, sock, &want_recv, &want_send);
   if(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;
     struct cf_call_data save;
     bool c_exhaust, s_exhaust;
     bool c_exhaust, s_exhaust;
 
 
@@ -2387,7 +2341,7 @@ static CURLcode cf_h2_connect(struct Curl_cfilter *cf,
       goto out;
       goto out;
   }
   }
 
 
-  result = h2_progress_ingress(cf, data);
+  result = h2_progress_ingress(cf, data, H2_CHUNK_SIZE);
   if(result)
   if(result)
     goto out;
     goto out;
 
 
@@ -2441,7 +2395,7 @@ static CURLcode http2_data_pause(struct Curl_cfilter *cf,
 {
 {
 #ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE
 #ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE
   struct cf_h2_ctx *ctx = cf->ctx;
   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);
   DEBUGASSERT(data);
   if(ctx && ctx->h2 && stream) {
   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);
     result = http2_data_done_send(cf, data);
     break;
     break;
   case CF_CTRL_DATA_DETACH:
   case CF_CTRL_DATA_DETACH:
-    http2_data_done(cf, data, TRUE);
+    http2_data_done(cf, data);
     break;
     break;
   case CF_CTRL_DATA_DONE:
   case CF_CTRL_DATA_DONE:
-    http2_data_done(cf, data, arg1 != 0);
+    http2_data_done(cf, data);
     break;
     break;
   default:
   default:
     break;
     break;
@@ -2526,11 +2480,10 @@ static bool cf_h2_data_pending(struct Curl_cfilter *cf,
                                const struct Curl_easy *data)
                                const struct Curl_easy *data)
 {
 {
   struct cf_h2_ctx *ctx = cf->ctx;
   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)
   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 TRUE;
   return cf->next? cf->next->cft->has_data_pending(cf->next, data) : FALSE;
   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,
 static CURLcode http2_cfilter_add(struct Curl_cfilter **pcf,
                                   struct Curl_easy *data,
                                   struct Curl_easy *data,
                                   struct connectdata *conn,
                                   struct connectdata *conn,
-                                  int sockindex)
+                                  int sockindex,
+                                  bool via_h1_upgrade)
 {
 {
   struct Curl_cfilter *cf = NULL;
   struct Curl_cfilter *cf = NULL;
   struct cf_h2_ctx *ctx;
   struct cf_h2_ctx *ctx;
@@ -2630,8 +2584,9 @@ static CURLcode http2_cfilter_add(struct Curl_cfilter **pcf,
   if(result)
   if(result)
     goto out;
     goto out;
 
 
+  ctx = NULL;
   Curl_conn_cf_add(data, conn, sockindex, cf);
   Curl_conn_cf_add(data, conn, sockindex, cf);
-  result = CURLE_OK;
+  result = cf_h2_ctx_init(cf, data, via_h1_upgrade);
 
 
 out:
 out:
   if(result)
   if(result)
@@ -2641,7 +2596,8 @@ out:
 }
 }
 
 
 static CURLcode http2_cfilter_insert_after(struct Curl_cfilter *cf,
 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 Curl_cfilter *cf_h2 = NULL;
   struct cf_h2_ctx *ctx;
   struct cf_h2_ctx *ctx;
@@ -2656,8 +2612,9 @@ static CURLcode http2_cfilter_insert_after(struct Curl_cfilter *cf,
   if(result)
   if(result)
     goto out;
     goto out;
 
 
+  ctx = NULL;
   Curl_conn_cf_insert_after(cf, cf_h2);
   Curl_conn_cf_insert_after(cf, cf_h2);
-  result = CURLE_OK;
+  result = cf_h2_ctx_init(cf_h2, data, via_h1_upgrade);
 
 
 out:
 out:
   if(result)
   if(result)
@@ -2714,11 +2671,7 @@ CURLcode Curl_http2_switch(struct Curl_easy *data,
   DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex));
   DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex));
   DEBUGF(infof(data, "switching to HTTP/2"));
   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)
   if(result)
     return 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));
   DEBUGASSERT(!Curl_cf_is_http2(cf, data));
 
 
-  result = http2_cfilter_insert_after(cf, data);
+  result = http2_cfilter_insert_after(cf, data, FALSE);
   if(result)
   if(result)
     return result;
     return result;
 
 
   cf_h2 = cf->next;
   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->httpversion = 20; /* we know we're on HTTP/2 now */
   cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
   cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
   cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
   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"));
   DEBUGF(infof(data, "upgrading to HTTP/2"));
   DEBUGASSERT(data->req.upgr101 == UPGR101_RECEIVED);
   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)
   if(result)
     return result;
     return result;
 
 
   DEBUGASSERT(cf->cft == &Curl_cft_nghttp2);
   DEBUGASSERT(cf->cft == &Curl_cft_nghttp2);
   ctx = cf->ctx;
   ctx = cf->ctx;
 
 
-  result = cf_h2_ctx_init(cf, data, TRUE);
-  if(result)
-    return result;
-
   if(nread > 0) {
   if(nread > 0) {
     /* Remaining data from the protocol switch reply is already using
     /* Remaining data from the protocol switch reply is already using
      * the switched protocol, ie. HTTP/2. We add that to the network
      * 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! */
    CURLE_HTTP2_STREAM error! */
 bool Curl_h2_http_1_1_error(struct Curl_easy *data)
 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);
   return (stream && stream->error == NGHTTP2_HTTP_1_1_REQUIRED);
 }
 }
 
 

+ 216 - 5
lib/http_chunks.c

@@ -27,10 +27,12 @@
 #ifndef CURL_DISABLE_HTTP
 #ifndef CURL_DISABLE_HTTP
 
 
 #include "urldata.h" /* it includes http_chunks.h */
 #include "urldata.h" /* it includes http_chunks.h */
+#include "curl_printf.h"
 #include "sendf.h"   /* for the client write stuff */
 #include "sendf.h"   /* for the client write stuff */
 #include "dynbuf.h"
 #include "dynbuf.h"
 #include "content_encoding.h"
 #include "content_encoding.h"
 #include "http.h"
 #include "http.h"
+#include "multiif.h"
 #include "strtoofft.h"
 #include "strtoofft.h"
 #include "warnless.h"
 #include "warnless.h"
 
 
@@ -152,9 +154,9 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data,
         ch->hexbuffer[ch->hexindex++] = *buf;
         ch->hexbuffer[ch->hexindex++] = *buf;
         buf++;
         buf++;
         blen--;
         blen--;
+        (*pconsumed)++;
       }
       }
       else {
       else {
-        char *endptr;
         if(0 == ch->hexindex) {
         if(0 == ch->hexindex) {
           /* This is illegal data, we received junk where we expected
           /* This is illegal data, we received junk where we expected
              a hexadecimal digit. */
              a hexadecimal digit. */
@@ -166,7 +168,7 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data,
 
 
         /* blen and buf are unmodified */
         /* blen and buf are unmodified */
         ch->hexbuffer[ch->hexindex] = 0;
         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);
           failf(data, "chunk hex-length not valid: '%s'", ch->hexbuffer);
           ch->state = CHUNK_FAILED;
           ch->state = CHUNK_FAILED;
           ch->last_code = CHUNKE_ILLEGAL_HEX;
           ch->last_code = CHUNKE_ILLEGAL_HEX;
@@ -189,6 +191,7 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data,
 
 
       buf++;
       buf++;
       blen--;
       blen--;
+      (*pconsumed)++;
       break;
       break;
 
 
     case CHUNK_DATA:
     case CHUNK_DATA:
@@ -236,6 +239,7 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data,
       }
       }
       buf++;
       buf++;
       blen--;
       blen--;
+      (*pconsumed)++;
       break;
       break;
 
 
     case CHUNK_TRAILER:
     case CHUNK_TRAILER:
@@ -293,6 +297,7 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data,
       }
       }
       buf++;
       buf++;
       blen--;
       blen--;
+      (*pconsumed)++;
       break;
       break;
 
 
     case CHUNK_TRAILER_CR:
     case CHUNK_TRAILER_CR:
@@ -300,6 +305,7 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data,
         ch->state = CHUNK_TRAILER_POSTCR;
         ch->state = CHUNK_TRAILER_POSTCR;
         buf++;
         buf++;
         blen--;
         blen--;
+        (*pconsumed)++;
       }
       }
       else {
       else {
         ch->state = CHUNK_FAILED;
         ch->state = CHUNK_FAILED;
@@ -320,6 +326,7 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data,
         /* skip if CR */
         /* skip if CR */
         buf++;
         buf++;
         blen--;
         blen--;
+        (*pconsumed)++;
       }
       }
       /* now wait for the final LF */
       /* now wait for the final LF */
       ch->state = CHUNK_STOP;
       ch->state = CHUNK_STOP;
@@ -328,6 +335,7 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data,
     case CHUNK_STOP:
     case CHUNK_STOP:
       if(*buf == 0x0a) {
       if(*buf == 0x0a) {
         blen--;
         blen--;
+        (*pconsumed)++;
         /* Record the length of any data left in the end of the buffer
         /* Record the length of any data left in the end of the buffer
            even if there's no more chunks to read */
            even if there's no more chunks to read */
         ch->datasize = blen;
         ch->datasize = blen;
@@ -386,7 +394,7 @@ struct chunked_writer {
 static CURLcode cw_chunked_init(struct Curl_easy *data,
 static CURLcode cw_chunked_init(struct Curl_easy *data,
                                 struct Curl_cwriter *writer)
                                 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. */
   data->req.chunk = TRUE;      /* chunks coming our way. */
   Curl_httpchunk_init(data, &ctx->ch, FALSE);
   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,
 static void cw_chunked_close(struct Curl_easy *data,
                              struct Curl_cwriter *writer)
                              struct Curl_cwriter *writer)
 {
 {
-  struct chunked_writer *ctx = (struct chunked_writer *)writer;
+  struct chunked_writer *ctx = writer->ctx;
   Curl_httpchunk_free(data, &ctx->ch);
   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,
                                  struct Curl_cwriter *writer, int type,
                                  const char *buf, size_t blen)
                                  const char *buf, size_t blen)
 {
 {
-  struct chunked_writer *ctx = (struct chunked_writer *)writer;
+  struct chunked_writer *ctx = writer->ctx;
   CURLcode result;
   CURLcode result;
   size_t consumed;
   size_t consumed;
 
 
@@ -452,4 +460,207 @@ const struct Curl_cwtype Curl_httpchunk_unencoder = {
   sizeof(struct chunked_writer)
   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 */
 #endif /* CURL_DISABLE_HTTP */

+ 7 - 0
lib/http_chunks.h

@@ -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_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 /* !CURL_DISABLE_HTTP */
 
 
 #endif /* HEADER_CURL_HTTP_CHUNKS_H */
 #endif /* HEADER_CURL_HTTP_CHUNKS_H */

+ 18 - 18
lib/imap.c

@@ -770,6 +770,7 @@ static CURLcode imap_perform_append(struct Curl_easy *data)
     return CURLE_URL_MALFORMAT;
     return CURLE_URL_MALFORMAT;
   }
   }
 
 
+#ifndef CURL_DISABLE_MIME
   /* Prepare the mime data if some. */
   /* Prepare the mime data if some. */
   if(data->set.mimepost.kind != MIMEKIND_NONE) {
   if(data->set.mimepost.kind != MIMEKIND_NONE) {
     /* Use the whole structure as data. */
     /* 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,
         result = Curl_mime_add_header(&data->set.mimepost.curlheaders,
                                       "Mime-Version: 1.0");
                                       "Mime-Version: 1.0");
 
 
-    /* Make sure we will read the entire mime structure. */
     if(!result)
     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)
     if(result)
       return 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 */
   /* 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)
     if(data->req.bytecount == size)
       /* The entire data is already transferred! */
       /* The entire data is already transferred! */
-      Curl_setup_transfer(data, -1, -1, FALSE, -1);
+      Curl_xfer_setup(data, -1, -1, FALSE, -1);
     else {
     else {
       /* IMAP download */
       /* IMAP download */
       data->req.maxdownload = size;
       data->req.maxdownload = size;
       /* force a recv/send check of this connection, as the data might've been
       /* force a recv/send check of this connection, as the data might've been
        read off the socket already */
        read off the socket already */
       data->state.select_bits = CURL_CSELECT_IN;
       data->state.select_bits = CURL_CSELECT_IN;
-      Curl_setup_transfer(data, FIRSTSOCKET, size, FALSE, -1);
+      Curl_xfer_setup(data, FIRSTSOCKET, size, FALSE, -1);
     }
     }
   }
   }
   else {
   else {
@@ -1266,7 +1267,7 @@ static CURLcode imap_state_append_resp(struct Curl_easy *data, int imapcode,
     Curl_pgrsSetUploadSize(data, data->state.infilesize);
     Curl_pgrsSetUploadSize(data, data->state.infilesize);
 
 
     /* IMAP upload */
     /* IMAP upload */
-    Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
+    Curl_xfer_setup(data, -1, -1, FALSE, FIRSTSOCKET);
 
 
     /* End of DO phase */
     /* End of DO phase */
     imap_state(data, IMAP_STOP);
     imap_state(data, IMAP_STOP);
@@ -1297,7 +1298,6 @@ static CURLcode imap_statemachine(struct Curl_easy *data,
                                   struct connectdata *conn)
                                   struct connectdata *conn)
 {
 {
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
-  curl_socket_t sock = conn->sock[FIRSTSOCKET];
   int imapcode;
   int imapcode;
   struct imap_conn *imapc = &conn->proto.imapc;
   struct imap_conn *imapc = &conn->proto.imapc;
   struct pingpong *pp = &imapc->pp;
   struct pingpong *pp = &imapc->pp;
@@ -1314,7 +1314,7 @@ static CURLcode imap_statemachine(struct Curl_easy *data,
 
 
   do {
   do {
     /* Read the response from the server */
     /* 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)
     if(result)
       return 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 &&
   else if(!data->set.connect_only && !imap->custom &&
           (imap->uid || imap->mindex || data->state.upload ||
           (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 */
     /* 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);
       imap_state(data, IMAP_FETCH_FINAL);
     else {
     else {
       /* End the APPEND command first by sending an empty line */
       /* 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;
     selected = TRUE;
 
 
   /* Start the first command in the DO phase */
   /* 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 */
     /* APPEND can be executed directly */
     result = imap_perform_append(data);
     result = imap_perform_append(data);
   else if(imap->custom && (selected || !imap->mailbox))
   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)
   if(imap->transfer != PPTRANSFER_BODY)
     /* no data to transfer */
     /* no data to transfer */
-    Curl_setup_transfer(data, -1, -1, FALSE, -1);
+    Curl_xfer_setup(data, -1, -1, FALSE, -1);
 
 
   return CURLE_OK;
   return CURLE_OK;
 }
 }

+ 14 - 11
lib/krb5.c

@@ -52,6 +52,7 @@
 #include "ftp.h"
 #include "ftp.h"
 #include "curl_gssapi.h"
 #include "curl_gssapi.h"
 #include "sendf.h"
 #include "sendf.h"
+#include "transfer.h"
 #include "curl_krb5.h"
 #include "curl_krb5.h"
 #include "warnless.h"
 #include "warnless.h"
 #include "strcase.h"
 #include "strcase.h"
@@ -65,7 +66,7 @@
 static CURLcode ftpsend(struct Curl_easy *data, struct connectdata *conn,
 static CURLcode ftpsend(struct Curl_easy *data, struct connectdata *conn,
                         const char *cmd)
                         const char *cmd)
 {
 {
-  ssize_t bytes_written;
+  size_t bytes_written;
 #define SBUF_SIZE 1024
 #define SBUF_SIZE 1024
   char s[SBUF_SIZE];
   char s[SBUF_SIZE];
   size_t write_len;
   size_t write_len;
@@ -90,8 +91,7 @@ static CURLcode ftpsend(struct Curl_easy *data, struct connectdata *conn,
 #ifdef HAVE_GSSAPI
 #ifdef HAVE_GSSAPI
     conn->data_prot = PROT_CMD;
     conn->data_prot = PROT_CMD;
 #endif
 #endif
-    result = Curl_nwrite(data, FIRSTSOCKET, sptr, write_len,
-                        &bytes_written);
+    result = Curl_xfer_send(data, sptr, write_len, &bytes_written);
 #ifdef HAVE_GSSAPI
 #ifdef HAVE_GSSAPI
     DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST);
     DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST);
     conn->data_prot = data_sec;
     conn->data_prot = data_sec;
@@ -100,9 +100,9 @@ static CURLcode ftpsend(struct Curl_easy *data, struct connectdata *conn,
     if(result)
     if(result)
       break;
       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;
       write_len -= bytes_written;
       sptr += 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;
   ssize_t nread = 0;
 
 
   while(len > 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) {
     if(nread > 0) {
       len -= nread;
       len -= nread;
       to_p += nread;
       to_p += nread;
@@ -494,11 +494,11 @@ socket_write(struct Curl_easy *data, int sockindex, const void *to,
 {
 {
   const char *to_p = to;
   const char *to_p = to;
   CURLcode result;
   CURLcode result;
-  ssize_t written;
+  size_t written;
 
 
   while(len > 0) {
   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;
       len -= written;
       to_p += written;
       to_p += written;
     }
     }
@@ -567,8 +567,11 @@ static ssize_t sec_recv(struct Curl_easy *data, int sockindex,
   *err = CURLE_OK;
   *err = CURLE_OK;
 
 
   /* Handle clear text response. */
   /* 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) {
   if(conn->in_buffer.eof_flag) {
     conn->in_buffer.eof_flag = 0;
     conn->in_buffer.eof_flag = 0;

+ 8 - 8
lib/ldap.c

@@ -371,7 +371,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
 #ifdef HAVE_LDAP_SSL
 #ifdef HAVE_LDAP_SSL
 #ifdef USE_WIN32_LDAP
 #ifdef USE_WIN32_LDAP
     /* Win32 LDAP SDK doesn't support insecure mode without CA! */
     /* 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);
     ldap_set_option(server, LDAP_OPT_SSL, LDAP_OPT_ON);
 #else
 #else
     int ldap_option;
     int ldap_option;
@@ -417,10 +417,10 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
       result = CURLE_SSL_CERTPROBLEM;
       result = CURLE_SSL_CERTPROBLEM;
       goto quit;
       goto quit;
     }
     }
-    server = ldapssl_init(host, conn->port, 1);
+    server = ldapssl_init(host, conn->primary.remote_port, 1);
     if(!server) {
     if(!server) {
       failf(data, "LDAP local: Cannot connect to %s:%u",
       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;
       result = CURLE_COULDNT_CONNECT;
       goto quit;
       goto quit;
     }
     }
@@ -458,10 +458,10 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
       result = CURLE_SSL_CERTPROBLEM;
       result = CURLE_SSL_CERTPROBLEM;
       goto quit;
       goto quit;
     }
     }
-    server = ldap_init(host, conn->port);
+    server = ldap_init(host, conn->primary.remote_port);
     if(!server) {
     if(!server) {
       failf(data, "LDAP local: Cannot connect to %s:%u",
       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;
       result = CURLE_COULDNT_CONNECT;
       goto quit;
       goto quit;
     }
     }
@@ -499,10 +499,10 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
     goto quit;
     goto quit;
   }
   }
   else {
   else {
-    server = ldap_init(host, conn->port);
+    server = ldap_init(host, conn->primary.remote_port);
     if(!server) {
     if(!server) {
       failf(data, "LDAP local: Cannot connect to %s:%u",
       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;
       result = CURLE_COULDNT_CONNECT;
       goto quit;
       goto quit;
     }
     }
@@ -749,7 +749,7 @@ quit:
   FREE_ON_WINLDAP(host);
   FREE_ON_WINLDAP(host);
 
 
   /* no data to transfer */
   /* 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");
   connclose(conn, "LDAP connection always disable reuse");
 
 
   return result;
   return result;

+ 1 - 0
lib/md4.c

@@ -28,6 +28,7 @@
 
 
 #include <string.h>
 #include <string.h>
 
 
+#include "strdup.h"
 #include "curl_md4.h"
 #include "curl_md4.h"
 #include "warnless.h"
 #include "warnless.h"
 
 

+ 222 - 5
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,
 static size_t encoder_qp_read(char *buffer, size_t size, bool ateof,
                               curl_mimepart *part);
                               curl_mimepart *part);
 static curl_off_t encoder_qp_size(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[] = {
 static const struct mime_encoder encoders[] = {
   {"binary", encoder_nop_read, encoder_nop_size},
   {"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. */
 /* 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?
   return mime_part_rewind(part) == CURL_SEEKFUNC_OK?
          CURLE_OK: CURLE_SEND_FAIL_REWIND;
          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. */
   size = boundarysize;  /* Final boundary - CRLF after headers. */
 
 
   for(part = mime->firstpart; part; part = part->nextpart) {
   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)
     if(sz < 0)
       size = sz;
       size = sz;
@@ -1647,7 +1648,7 @@ static curl_off_t multipart_size(curl_mime *mime)
 }
 }
 
 
 /* Get/compute mime size. */
 /* 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;
   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. */
 /* 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) {
     if(part->lastreadstatus == CURL_READFUNC_PAUSE)
     if(part->lastreadstatus == CURL_READFUNC_PAUSE)
@@ -1908,12 +1909,228 @@ void Curl_mime_unpause(curl_mimepart *part)
         curl_mimepart *subpart;
         curl_mimepart *subpart;
 
 
         for(subpart = mime->firstpart; subpart; subpart = subpart->nextpart)
         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 ||
 #else /* !CURL_DISABLE_MIME && (!CURL_DISABLE_HTTP ||
                                 !CURL_DISABLE_SMTP || !CURL_DISABLE_IMAP) */
                                 !CURL_DISABLE_SMTP || !CURL_DISABLE_IMAP) */

+ 7 - 6
lib/mime.h

@@ -151,12 +151,15 @@ CURLcode Curl_mime_prepare_headers(struct Curl_easy *data,
                                    const char *contenttype,
                                    const char *contenttype,
                                    const char *disposition,
                                    const char *disposition,
                                    enum mimestrategy strategy);
                                    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,
 size_t Curl_mime_read(char *buffer, size_t size, size_t nitems,
                       void *instream);
                       void *instream);
-CURLcode Curl_mime_rewind(struct curl_mimepart *part);
 const char *Curl_mime_contenttype(const char *filename);
 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
 #else
 /* if disabled */
 /* 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_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_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_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_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
 #endif
 
 
 
 

+ 4 - 13
lib/mprintf.c

@@ -48,16 +48,6 @@
 #  endif
 #  endif
 #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
  * Max integer data types that mprintf.c is capable
  */
  */
@@ -349,8 +339,9 @@ static int parsefmt(const char *format,
         case 'h':
         case 'h':
           flags |= FLAGS_SHORT;
           flags |= FLAGS_SHORT;
           break;
           break;
-#if defined(MP_HAVE_INT_EXTENSIONS)
+#if defined(_WIN32) || defined(_WIN32_WCE)
         case 'I':
         case 'I':
+          /* Non-ANSI integer extensions I32 I64 */
           if((fmt[0] == '3') && (fmt[1] == '2')) {
           if((fmt[0] == '3') && (fmt[1] == '2')) {
             flags |= FLAGS_LONG;
             flags |= FLAGS_LONG;
             fmt += 2;
             fmt += 2;
@@ -367,7 +358,7 @@ static int parsefmt(const char *format,
 #endif
 #endif
           }
           }
           break;
           break;
-#endif
+#endif /* _WIN32 || _WIN32_WCE */
         case 'l':
         case 'l':
           if(flags & FLAGS_LONG)
           if(flags & FLAGS_LONG)
             flags |= FLAGS_LONGLONG;
             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
  * On success, the input array describes the type of all arguments and their
  * values.
  * 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).
  * by one until done. Using the appropriate input arguments (if any).
  *
  *
  * All output is sent to the 'stream()' callback, one byte at a time.
  * All output is sent to the 'stream()' callback, one byte at a time.

+ 7 - 10
lib/mqtt.c

@@ -119,12 +119,12 @@ static CURLcode mqtt_send(struct Curl_easy *data,
 {
 {
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
   struct MQTT *mq = data->req.p.mqtt;
   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)
   if(result)
     return result;
     return result;
   Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)n);
   Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)n);
-  if(len != (size_t)n) {
+  if(len != n) {
     size_t nsend = len - n;
     size_t nsend = len - n;
     char *sendleftovers = Curl_memdup(&buf[n], nsend);
     char *sendleftovers = Curl_memdup(&buf[n], nsend);
     if(!sendleftovers)
     if(!sendleftovers)
@@ -366,8 +366,7 @@ static CURLcode mqtt_recv_atleast(struct Curl_easy *data, size_t nbytes)
     ssize_t nread;
     ssize_t nread;
 
 
     DEBUGASSERT(nbytes - rlen < sizeof(readbuf));
     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)
     if(result)
       return result;
       return result;
     DEBUGASSERT(nread >= 0);
     DEBUGASSERT(nread >= 0);
@@ -622,7 +621,6 @@ static CURLcode mqtt_read_publish(struct Curl_easy *data, bool *done)
 {
 {
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
   struct connectdata *conn = data->conn;
   struct connectdata *conn = data->conn;
-  curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
   ssize_t nread;
   ssize_t nread;
   size_t remlen;
   size_t remlen;
   struct mqtt_conn *mqtt = &conn->proto.mqtt;
   struct mqtt_conn *mqtt = &conn->proto.mqtt;
@@ -679,7 +677,7 @@ MQTT_SUBACK_COMING:
     size_t rest = mq->npacket;
     size_t rest = mq->npacket;
     if(rest > sizeof(buffer))
     if(rest > sizeof(buffer))
       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(result) {
       if(CURLE_AGAIN == result) {
       if(CURLE_AGAIN == result) {
         infof(data, "EEEE AAAAGAIN");
         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_conn *mqtt = &conn->proto.mqtt;
   struct MQTT *mq = data->req.p.mqtt;
   struct MQTT *mq = data->req.p.mqtt;
   ssize_t nread;
   ssize_t nread;
-  curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
   unsigned char byte;
   unsigned char byte;
 
 
   *done = FALSE;
   *done = FALSE;
@@ -762,7 +759,7 @@ static CURLcode mqtt_doing(struct Curl_easy *data, bool *done)
   switch(mqtt->state) {
   switch(mqtt->state) {
   case MQTT_FIRST:
   case MQTT_FIRST:
     /* Read the initial byte only */
     /* 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)
     if(result)
       break;
       break;
     else if(!nread) {
     else if(!nread) {
@@ -778,7 +775,7 @@ static CURLcode mqtt_doing(struct Curl_easy *data, bool *done)
     FALLTHROUGH();
     FALLTHROUGH();
   case MQTT_REMAINING_LENGTH:
   case MQTT_REMAINING_LENGTH:
     do {
     do {
-      result = Curl_read(data, sockfd, (char *)&byte, 1, &nread);
+      result = Curl_xfer_recv(data, (char *)&byte, 1, &nread);
       if(!nread)
       if(!nread)
         break;
         break;
       Curl_debug(data, CURLINFO_HEADER_IN, (char *)&byte, 1);
       Curl_debug(data, CURLINFO_HEADER_IN, (char *)&byte, 1);

+ 153 - 111
lib/multi.c

@@ -94,6 +94,7 @@ static CURLMcode add_next_timeout(struct curltime now,
 static CURLMcode multi_timeout(struct Curl_multi *multi,
 static CURLMcode multi_timeout(struct Curl_multi *multi,
                                long *timeout_ms);
                                long *timeout_ms);
 static void process_pending_handles(struct Curl_multi *multi);
 static void process_pending_handles(struct Curl_multi *multi);
+static void multi_xfer_bufs_free(struct Curl_multi *multi);
 
 
 #ifdef DEBUGBUILD
 #ifdef DEBUGBUILD
 static const char * const multi_statename[]={
 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' */
     /* changing to COMPLETED means there's one less easy handle 'alive' */
     DEBUGASSERT(data->multi->num_alive > 0);
     DEBUGASSERT(data->multi->num_alive > 0);
     data->multi->num_alive--;
     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 */
   /* 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;
     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 */
   /* Initialize timeout list for this handle */
   Curl_llist_init(&data->state.timeoutlist, NULL);
   Curl_llist_init(&data->state.timeoutlist, NULL);
 
 
@@ -640,7 +652,7 @@ static CURLcode multi_done(struct Curl_easy *data,
                                                 after an error was detected */
                                                 after an error was detected */
                            bool premature)
                            bool premature)
 {
 {
-  CURLcode result;
+  CURLcode result, r2;
   struct connectdata *conn = data->conn;
   struct connectdata *conn = data->conn;
 
 
 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
 #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;
       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 */
   /* Inform connection filters that this transfer is done */
   Curl_conn_ev_data_done(data, premature);
   Curl_conn_ev_data_done(data, premature);
 
 
   process_pending_handles(data->multi); /* connection / multiplex */
   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);
   CONNCACHE_LOCK(data);
   Curl_detach_connection(data);
   Curl_detach_connection(data);
@@ -784,7 +800,6 @@ static CURLcode multi_done(struct Curl_easy *data,
       data->state.lastconnect_id = -1;
       data->state.lastconnect_id = -1;
   }
   }
 
 
-  Curl_safefree(data->state.buffer);
   return result;
   return result;
 }
 }
 
 
@@ -998,7 +1013,7 @@ static int connecting_getsock(struct Curl_easy *data, curl_socket_t *socks)
 {
 {
   struct connectdata *conn = data->conn;
   struct connectdata *conn = data->conn;
   (void)socks;
   (void)socks;
-  /* Not using `conn->sockfd` as `Curl_setup_transfer()` initializes
+  /* Not using `conn->sockfd` as `Curl_xfer_setup()` initializes
    * that *after* the connect. */
    * that *after* the connect. */
   if(conn && conn->sock[FIRSTSOCKET] != CURL_SOCKET_BAD) {
   if(conn && conn->sock[FIRSTSOCKET] != CURL_SOCKET_BAD) {
     /* Default is to wait to something from the server */
     /* 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 */
   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
  * Curl_preconnect() is called immediately before a connect starts. When a
  * redirect is followed, this is then called multiple times during a single
  * 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)
 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;
   return CURLE_OK;
 }
 }
 
 
@@ -1914,7 +1843,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
   bool async;
   bool async;
   bool protocol_connected = FALSE;
   bool protocol_connected = FALSE;
   bool dophase_done = FALSE;
   bool dophase_done = FALSE;
-  bool done = FALSE;
   CURLMcode rc;
   CURLMcode rc;
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
   timediff_t recv_timeout_ms;
   timediff_t recv_timeout_ms;
@@ -2058,7 +1986,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
         hostname = conn->host.name;
         hostname = conn->host.name;
 
 
       /* check if we have the name resolved by now */
       /* 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) {
       if(dns) {
 #ifdef CURLRES_ASYNCH
 #ifdef CURLRES_ASYNCH
@@ -2153,9 +2081,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
       break;
       break;
 
 
     case MSTATE_PROTOCONNECT:
     case MSTATE_PROTOCONNECT:
-      if(data->state.rewindbeforesend)
-        result = readrewind(data);
-
       if(!result && data->conn->bits.reuse) {
       if(!result && data->conn->bits.reuse) {
         /* ftp seems to hang when protoconnect on reused connection
         /* ftp seems to hang when protoconnect on reused connection
          * since we handle PROTOCONNECT in general inside the filers, it
          * 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 */
         /* call the prerequest callback function */
         Curl_set_in_callback(data, true);
         Curl_set_in_callback(data, true);
         prereq_rc = data->set.fprereq(data->set.prereq_userp,
         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);
         Curl_set_in_callback(data, false);
         if(prereq_rc != CURL_PREREQFUNC_OK) {
         if(prereq_rc != CURL_PREREQFUNC_OK) {
           failf(data, "operation aborted by pre-request callback");
           failf(data, "operation aborted by pre-request callback");
@@ -2450,7 +2375,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
     {
     {
       char *newurl = NULL;
       char *newurl = NULL;
       bool retry = FALSE;
       bool retry = FALSE;
-      DEBUGASSERT(data->state.buffer);
       /* check if over send speed */
       /* check if over send speed */
       send_timeout_ms = 0;
       send_timeout_ms = 0;
       if(data->set.max_send_speed)
       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 */
       /* 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
         /* If CURLE_RECV_ERROR happens early enough, we assume it was a race
          * condition and the server closed the reused connection exactly when
          * condition and the server closed the reused connection exactly when
          * we wanted to use it, so figure out if that is indeed the case.
          * 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
           /* if we are to retry, set the result to OK and consider the
              request as done */
              request as done */
           result = CURLE_OK;
           result = CURLE_OK;
-          done = TRUE;
+          data->req.done = TRUE;
         }
         }
       }
       }
       else if((CURLE_HTTP2_STREAM == result) &&
       else if((CURLE_HTTP2_STREAM == result) &&
@@ -2517,7 +2441,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
              as done */
              as done */
           retry = TRUE;
           retry = TRUE;
           result = CURLE_OK;
           result = CURLE_OK;
-          done = TRUE;
+          data->req.done = TRUE;
         }
         }
         else
         else
           result = ret;
           result = ret;
@@ -2539,7 +2463,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
         Curl_posttransfer(data);
         Curl_posttransfer(data);
         multi_done(data, result, TRUE);
         multi_done(data, result, TRUE);
       }
       }
-      else if(done) {
+      else if(data->req.done) {
 
 
         /* call this even if the readwrite function returned error */
         /* call this even if the readwrite function returned error */
         Curl_posttransfer(data);
         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);
     Curl_free_multi_ssl_backend_data(multi->ssl_backend_data);
 #endif
 #endif
 
 
+    multi_xfer_bufs_free(multi);
     free(multi);
     free(multi);
 
 
     return CURLM_OK;
     return CURLM_OK;
@@ -3242,7 +3167,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
 
 
         if(data->conn && !(data->conn->handler->flags & PROTOPT_DIRLOCK))
         if(data->conn && !(data->conn->handler->flags & PROTOPT_DIRLOCK))
           /* set socket event bitmask if they're not locked */
           /* 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);
         Curl_expire(data, 0, EXPIRE_RUN_NOW);
       }
       }
@@ -3819,3 +3744,120 @@ struct Curl_easy **curl_multi_get_handles(struct Curl_multi *multi)
   }
   }
   return a;
   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;
+}

+ 9 - 0
lib/multihandle.h

@@ -124,6 +124,13 @@ struct Curl_multi {
      times of all currently set timers */
      times of all currently set timers */
   struct Curl_tree *timetree;
   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)
 #if defined(USE_SSL)
   struct multi_ssl_backend_data *ssl_backend_data;
   struct multi_ssl_backend_data *ssl_backend_data;
 #endif
 #endif
@@ -171,6 +178,8 @@ struct Curl_multi {
 #endif
 #endif
   BIT(dead); /* a callback returned error, everything needs to crash and
   BIT(dead); /* a callback returned error, everything needs to crash and
                 burn */
                 burn */
+  BIT(xfer_buf_borrowed);      /* xfer_buf is currently being borrowed */
+  BIT(xfer_ulbuf_borrowed);      /* xfer_buf is currently being borrowed */
 #ifdef DEBUGBUILD
 #ifdef DEBUGBUILD
   BIT(warned);                 /* true after user warned of DEBUGBUILD */
   BIT(warned);                 /* true after user warned of DEBUGBUILD */
 #endif
 #endif

+ 49 - 0
lib/multiif.h

@@ -94,4 +94,53 @@ CURLMcode Curl_multi_add_perform(struct Curl_multi *multi,
 /* Return the value of the CURLMOPT_MAX_CONCURRENT_STREAMS option */
 /* Return the value of the CURLMOPT_MAX_CONCURRENT_STREAMS option */
 unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi);
 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 */
 #endif /* HEADER_CURL_MULTIIF_H */

+ 7 - 3
lib/netrc.c

@@ -53,6 +53,8 @@ enum host_lookup_state {
 #define NETRC_FAILED -1
 #define NETRC_FAILED -1
 #define NETRC_SUCCESS 0
 #define NETRC_SUCCESS 0
 
 
+#define MAX_NETRC_LINE 4096
+
 /*
 /*
  * Returns zero on success.
  * Returns zero on success.
  */
  */
@@ -80,13 +82,14 @@ static int parsenetrc(const char *host,
   file = fopen(netrcfile, FOPEN_READTEXT);
   file = fopen(netrcfile, FOPEN_READTEXT);
   if(file) {
   if(file) {
     bool done = FALSE;
     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;
       char *tok_end;
       char *tok_end;
       bool quoted;
       bool quoted;
+      char *netrcbuffer = Curl_dyn_ptr(&buf);
       if(state == MACDEF) {
       if(state == MACDEF) {
         if((netrcbuffer[0] == '\n') || (netrcbuffer[0] == '\r'))
         if((netrcbuffer[0] == '\n') || (netrcbuffer[0] == '\r'))
           state = NOTHING;
           state = NOTHING;
@@ -245,6 +248,7 @@ static int parsenetrc(const char *host,
     } /* while Curl_get_line() */
     } /* while Curl_get_line() */
 
 
 out:
 out:
+    Curl_dyn_free(&buf);
     if(!retcode) {
     if(!retcode) {
       /* success */
       /* success */
       if(login_alloc) {
       if(login_alloc) {

+ 1 - 1
lib/openldap.c

@@ -916,7 +916,7 @@ static CURLcode oldap_do(struct Curl_easy *data, bool *done)
       else {
       else {
         lr->msgid = msgid;
         lr->msgid = msgid;
         data->req.p.ldap = lr;
         data->req.p.ldap = lr;
-        Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
+        Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1);
         *done = TRUE;
         *done = TRUE;
       }
       }
     }
     }

+ 24 - 15
lib/pingpong.c

@@ -164,7 +164,7 @@ CURLcode Curl_pp_vsendf(struct Curl_easy *data,
                         const char *fmt,
                         const char *fmt,
                         va_list args)
                         va_list args)
 {
 {
-  ssize_t bytes_written = 0;
+  size_t bytes_written = 0;
   size_t write_len;
   size_t write_len;
   char *s;
   char *s;
   CURLcode result;
   CURLcode result;
@@ -199,8 +199,11 @@ CURLcode Curl_pp_vsendf(struct Curl_easy *data,
 #ifdef HAVE_GSSAPI
 #ifdef HAVE_GSSAPI
   conn->data_prot = PROT_CMD;
   conn->data_prot = PROT_CMD;
 #endif
 #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;
     return result;
 #ifdef HAVE_GSSAPI
 #ifdef HAVE_GSSAPI
   data_sec = conn->data_prot;
   data_sec = conn->data_prot;
@@ -208,9 +211,9 @@ CURLcode Curl_pp_vsendf(struct Curl_easy *data,
   conn->data_prot = (unsigned char)data_sec;
   conn->data_prot = (unsigned char)data_sec;
 #endif
 #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 */
     /* the whole chunk was not sent, keep it around and adjust sizes */
     pp->sendthis = s;
     pp->sendthis = s;
     pp->sendsize = write_len;
     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,
 static CURLcode pingpong_read(struct Curl_easy *data,
-                              curl_socket_t sockfd,
+                              int sockindex,
                               char *buffer,
                               char *buffer,
                               size_t buflen,
                               size_t buflen,
                               ssize_t *nread)
                               ssize_t *nread)
@@ -261,7 +264,7 @@ static CURLcode pingpong_read(struct Curl_easy *data,
   enum protection_level prot = data->conn->data_prot;
   enum protection_level prot = data->conn->data_prot;
   data->conn->data_prot = PROT_CLEAR;
   data->conn->data_prot = PROT_CLEAR;
 #endif
 #endif
-  result = Curl_read(data, sockfd, buffer, buflen, nread);
+  result = Curl_conn_recv(data, sockindex, buffer, buflen, nread);
 #ifdef HAVE_GSSAPI
 #ifdef HAVE_GSSAPI
   DEBUGASSERT(prot  > PROT_NONE && prot < PROT_LAST);
   DEBUGASSERT(prot  > PROT_NONE && prot < PROT_LAST);
   data->conn->data_prot = (unsigned char)prot;
   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.
  * Reads a piece of a server response.
  */
  */
 CURLcode Curl_pp_readresp(struct Curl_easy *data,
 CURLcode Curl_pp_readresp(struct Curl_easy *data,
-                          curl_socket_t sockfd,
+                          int sockindex,
                           struct pingpong *pp,
                           struct pingpong *pp,
                           int *code, /* return the server code if done */
                           int *code, /* return the server code if done */
                           size_t *size) /* size of the response */
                           size_t *size) /* size of the response */
@@ -300,7 +303,7 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data,
     ssize_t gotbytes = 0;
     ssize_t gotbytes = 0;
     char buffer[900];
     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)
     if(result == CURLE_AGAIN)
       return CURLE_OK;
       return CURLE_OK;
 
 
@@ -395,14 +398,20 @@ CURLcode Curl_pp_flushsend(struct Curl_easy *data,
                            struct pingpong *pp)
                            struct pingpong *pp)
 {
 {
   /* we have a piece of a command still left to send */
   /* 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)
   if(result)
     return result;
     return result;
 
 
-  if(written != (ssize_t)pp->sendleft) {
+  if(written != pp->sendleft) {
     /* only a fraction was sent */
     /* only a fraction was sent */
     pp->sendleft -= written;
     pp->sendleft -= written;
   }
   }
@@ -423,7 +432,7 @@ CURLcode Curl_pp_disconnect(struct pingpong *pp)
 
 
 bool Curl_pp_moredata(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
 #endif

+ 1 - 1
lib/pingpong.h

@@ -132,7 +132,7 @@ CURLcode Curl_pp_vsendf(struct Curl_easy *data,
  * Reads a piece of a server response.
  * Reads a piece of a server response.
  */
  */
 CURLcode Curl_pp_readresp(struct Curl_easy *data,
 CURLcode Curl_pp_readresp(struct Curl_easy *data,
-                          curl_socket_t sockfd,
+                          int sockindex,
                           struct pingpong *pp,
                           struct pingpong *pp,
                           int *code, /* return the server code if done */
                           int *code, /* return the server code if done */
                           size_t *size); /* size of the response */
                           size_t *size); /* size of the response */

+ 2 - 3
lib/pop3.c

@@ -934,7 +934,7 @@ static CURLcode pop3_state_command_resp(struct Curl_easy *data,
 
 
   if(pop3->transfer == PPTRANSFER_BODY) {
   if(pop3->transfer == PPTRANSFER_BODY) {
     /* POP3 download */
     /* POP3 download */
-    Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
+    Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1);
 
 
     if(pp->overflow) {
     if(pp->overflow) {
       /* The recv buffer contains data that is actually body content so send
       /* 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)
                                   struct connectdata *conn)
 {
 {
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
-  curl_socket_t sock = conn->sock[FIRSTSOCKET];
   int pop3code;
   int pop3code;
   struct pop3_conn *pop3c = &conn->proto.pop3c;
   struct pop3_conn *pop3c = &conn->proto.pop3c;
   struct pingpong *pp = &pop3c->pp;
   struct pingpong *pp = &pop3c->pp;
@@ -987,7 +986,7 @@ static CURLcode pop3_statemachine(struct Curl_easy *data,
 
 
  do {
  do {
     /* Read the response from the server */
     /* 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)
    if(result)
      return result;
      return result;
 
 

+ 409 - 0
lib/request.c

@@ -0,0 +1,409 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <[email protected]>, 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;
+}

+ 227 - 0
lib/request.h

@@ -0,0 +1,227 @@
+#ifndef HEADER_CURL_REQUEST_H
+#define HEADER_CURL_REQUEST_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <[email protected]>, 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 */

+ 64 - 57
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,
 static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data,
                                     const char *buf,
                                     const char *buf,
                                     size_t blen,
                                     size_t blen,
-                                    bool is_eos,
-                                    bool *done);
+                                    bool is_eos);
 
 
 static CURLcode rtsp_setup_connection(struct Curl_easy *data,
 static CURLcode rtsp_setup_connection(struct Curl_easy *data,
                                       struct connectdata *conn);
                                       struct connectdata *conn);
@@ -225,8 +224,6 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
   Curl_RtspReq rtspreq = data->set.rtspreq;
   Curl_RtspReq rtspreq = data->set.rtspreq;
   struct RTSP *rtsp = data->req.p.rtsp;
   struct RTSP *rtsp = data->req.p.rtsp;
   struct dynbuf req_buffer;
   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_request = NULL;
   const char *p_session_id = 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;
   const char *p_userpwd = NULL;
 
 
   *done = TRUE;
   *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_sent = data->state.rtsp_next_client_CSeq;
   rtsp->CSeq_recv = 0;
   rtsp->CSeq_recv = 0;
@@ -310,9 +309,8 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
   }
   }
 
 
   if(rtspreq == RTSPREQ_RECEIVE) {
   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];
   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))) {
      (rtspreq & ~(RTSPREQ_OPTIONS | RTSPREQ_DESCRIBE | RTSPREQ_SETUP))) {
     failf(data, "Refusing to issue an RTSP request [%s] without a session ID.",
     failf(data, "Refusing to issue an RTSP request [%s] without a session ID.",
           p_request);
           p_request);
-    return CURLE_BAD_FUNCTION_ARGUMENT;
+    result = CURLE_BAD_FUNCTION_ARGUMENT;
+    goto out;
   }
   }
 
 
   /* Stream URI. Default to server '*' if not specified */
   /* Stream URI. Default to server '*' if not specified */
@@ -347,7 +346,8 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
     else {
     else {
       failf(data,
       failf(data,
             "Refusing to issue an RTSP SETUP without a Transport: header.");
             "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;
     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 =
       data->state.aptr.accept_encoding =
         aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_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;
       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,
   result = Curl_http_output_auth(data, conn, p_request, HTTPREQ_GET,
                                  p_stream_uri, FALSE);
                                  p_stream_uri, FALSE);
   if(result)
   if(result)
-    return result;
+    goto out;
 
 
   p_proxyuserpwd = data->state.aptr.proxyuserpwd;
   p_proxyuserpwd = data->state.aptr.proxyuserpwd;
   p_userpwd = data->state.aptr.userpwd;
   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"))) {
   if(Curl_checkheaders(data, STRCONST("CSeq"))) {
     failf(data, "CSeq cannot be set as a custom header.");
     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"))) {
   if(Curl_checkheaders(data, STRCONST("Session"))) {
     failf(data, "Session ID cannot be set as a custom header.");
     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 =
   result =
     Curl_dyn_addf(&req_buffer,
     Curl_dyn_addf(&req_buffer,
                   "%s %s RTSP/1.0\r\n" /* Request Stream-URI RTSP/1.0 */
                   "%s %s RTSP/1.0\r\n" /* Request Stream-URI RTSP/1.0 */
                   "CSeq: %ld\r\n", /* CSeq */
                   "CSeq: %ld\r\n", /* CSeq */
                   p_request, p_stream_uri, rtsp->CSeq_sent);
                   p_request, p_stream_uri, rtsp->CSeq_sent);
   if(result)
   if(result)
-    return result;
+    goto out;
 
 
   /*
   /*
    * Rather than do a normal alloc line, keep the session_id unformatted
    * 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) {
   if(p_session_id) {
     result = Curl_dyn_addf(&req_buffer, "Session: %s\r\n", p_session_id);
     result = Curl_dyn_addf(&req_buffer, "Session: %s\r\n", p_session_id);
     if(result)
     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);
   Curl_safefree(data->state.aptr.userpwd);
 
 
   if(result)
   if(result)
-    return result;
+    goto out;
 
 
   if((rtspreq == RTSPREQ_SETUP) || (rtspreq == RTSPREQ_DESCRIBE)) {
   if((rtspreq == RTSPREQ_SETUP) || (rtspreq == RTSPREQ_DESCRIBE)) {
     result = Curl_add_timecondition(data, &req_buffer);
     result = Curl_add_timecondition(data, &req_buffer);
     if(result)
     if(result)
-      return result;
+      goto out;
   }
   }
 
 
   result = Curl_add_custom_headers(data, FALSE, &req_buffer);
   result = Curl_add_custom_headers(data, FALSE, &req_buffer);
   if(result)
   if(result)
-    return result;
+    goto out;
 
 
   if(rtspreq == RTSPREQ_ANNOUNCE ||
   if(rtspreq == RTSPREQ_ANNOUNCE ||
      rtspreq == RTSPREQ_SET_PARAMETER ||
      rtspreq == RTSPREQ_SET_PARAMETER ||
      rtspreq == RTSPREQ_GET_PARAMETER) {
      rtspreq == RTSPREQ_GET_PARAMETER) {
+    curl_off_t req_clen; /* request content length */
 
 
     if(data->state.upload) {
     if(data->state.upload) {
-      putsize = data->state.infilesize;
+      req_clen = data->state.infilesize;
       data->state.httpreq = HTTPREQ_PUT;
       data->state.httpreq = HTTPREQ_PUT;
-
+      result = Curl_creader_set_fread(data, req_clen);
+      if(result)
+        goto out;
     }
     }
     else {
     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
       /* As stated in the http comments, it is probably not wise to
        * actually set a custom Content-Length in the headers */
        * actually set a custom Content-Length in the headers */
       if(!Curl_checkheaders(data, STRCONST("Content-Length"))) {
       if(!Curl_checkheaders(data, STRCONST("Content-Length"))) {
         result =
         result =
           Curl_dyn_addf(&req_buffer,
           Curl_dyn_addf(&req_buffer,
                         "Content-Length: %" CURL_FORMAT_CURL_OFF_T"\r\n",
                         "Content-Length: %" CURL_FORMAT_CURL_OFF_T"\r\n",
-                        (data->state.upload ? putsize : postsize));
+                        req_clen);
         if(result)
         if(result)
-          return result;
+          goto out;
       }
       }
 
 
       if(rtspreq == RTSPREQ_SET_PARAMETER ||
       if(rtspreq == RTSPREQ_SET_PARAMETER ||
@@ -528,7 +542,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
                                  STRCONST("Content-Type: "
                                  STRCONST("Content-Type: "
                                           "text/parameters\r\n"));
                                           "text/parameters\r\n"));
           if(result)
           if(result)
-            return result;
+            goto out;
         }
         }
       }
       }
 
 
@@ -538,11 +552,9 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
                                  STRCONST("Content-Type: "
                                  STRCONST("Content-Type: "
                                           "application/sdp\r\n"));
                                           "application/sdp\r\n"));
           if(result)
           if(result)
-            return result;
+            goto out;
         }
         }
       }
       }
-
-      data->state.expect100header = FALSE; /* RTSP posts are simple/small */
     }
     }
     else if(rtspreq == RTSPREQ_GET_PARAMETER) {
     else if(rtspreq == RTSPREQ_GET_PARAMETER) {
       /* Check for an empty GET_PARAMETER (heartbeat) request */
       /* 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;
       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 */
   /* Finish the request buffer */
   result = Curl_dyn_addn(&req_buffer, STRCONST("\r\n"));
   result = Curl_dyn_addn(&req_buffer, STRCONST("\r\n"));
   if(result)
   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 */
   /* 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) {
   if(result) {
     failf(data, "Failed sending RTSP request");
     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 */
   /* Increment the CSeq on success */
   data->state.rtsp_next_client_CSeq++;
   data->state.rtsp_next_client_CSeq++;
 
 
@@ -585,7 +592,8 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
     if(Curl_pgrsUpdate(data))
     if(Curl_pgrsUpdate(data))
       result = CURLE_ABORTED_BY_CALLBACK;
       result = CURLE_ABORTED_BY_CALLBACK;
   }
   }
-
+out:
+  Curl_dyn_free(&req_buffer);
   return result;
   return result;
 }
 }
 
 
@@ -779,8 +787,7 @@ out:
 static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data,
 static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data,
                                     const char *buf,
                                     const char *buf,
                                     size_t blen,
                                     size_t blen,
-                                    bool is_eos,
-                                    bool *done)
+                                    bool is_eos)
 {
 {
   struct rtsp_conn *rtspc = &(data->conn->proto.rtspc);
   struct rtsp_conn *rtspc = &(data->conn->proto.rtspc);
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
@@ -788,7 +795,6 @@ static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data,
 
 
   if(!data->req.header)
   if(!data->req.header)
     rtspc->in_header = FALSE;
     rtspc->in_header = FALSE;
-  *done = FALSE;
   if(!blen) {
   if(!blen) {
     goto out;
     goto out;
   }
   }
@@ -812,7 +818,7 @@ static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data,
   /* we want to parse headers, do so */
   /* we want to parse headers, do so */
   if(data->req.header && blen) {
   if(data->req.header && blen) {
     rtspc->in_header = TRUE;
     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)
     if(result)
       goto out;
       goto out;
 
 
@@ -838,13 +844,14 @@ static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data,
   }
   }
 
 
   if(rtspc->state != RTP_PARSE_SKIP)
   if(rtspc->state != RTP_PARSE_SKIP)
-    *done = FALSE;
+    data->req.done = FALSE;
   /* we SHOULD have consumed all bytes, unless the response is borked.
   /* we SHOULD have consumed all bytes, unless the response is borked.
    * In which case we write out the left over bytes, letting the client
    * 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). */
    * 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 "
   DEBUGF(infof(data, "rtsp_rtp_write_resp(len=%zu, in_header=%d, done=%d "
                " rtspc->state=%d, req.size=%" CURL_FORMAT_CURL_OFF_T ")",
                " 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)) {
   if(!result && (is_eos || blen)) {
     result = Curl_client_write(data, CLIENTWRITE_BODY|
     result = Curl_client_write(data, CLIENTWRITE_BODY|
                                (is_eos? CLIENTWRITE_EOS:0),
                                (is_eos? CLIENTWRITE_EOS:0),

+ 958 - 437
lib/sendf.c

@@ -41,6 +41,7 @@
 #include "cfilters.h"
 #include "cfilters.h"
 #include "connect.h"
 #include "connect.h"
 #include "content_encoding.h"
 #include "content_encoding.h"
+#include "cw-out.h"
 #include "vtls/vtls.h"
 #include "vtls/vtls.h"
 #include "vssh/ssh.h"
 #include "vssh/ssh.h"
 #include "easyif.h"
 #include "easyif.h"
@@ -49,8 +50,8 @@
 #include "select.h"
 #include "select.h"
 #include "strdup.h"
 #include "strdup.h"
 #include "http2.h"
 #include "http2.h"
-#include "headers.h"
 #include "progress.h"
 #include "progress.h"
+#include "warnless.h"
 #include "ws.h"
 #include "ws.h"
 
 
 /* The last 3 #include files should be in this order */
 /* The last 3 #include files should be in this order */
@@ -59,342 +60,18 @@
 #include "memdebug.h"
 #include "memdebug.h"
 
 
 
 
-static CURLcode do_init_stack(struct Curl_easy *data);
-
-#if defined(CURL_DO_LINEEND_CONV) && !defined(CURL_DISABLE_FTP)
-/*
- * convert_lineends() changes CRLF (\r\n) end-of-line markers to a single LF
- * (\n), with special processing for CRLF sequences that are split between two
- * blocks of data.  Remaining, bare CRs are changed to LFs.  The possibly new
- * size of the data is returned.
- */
-static size_t convert_lineends(struct Curl_easy *data,
-                               char *startPtr, size_t size)
-{
-  char *inPtr, *outPtr;
-
-  /* sanity check */
-  if(!startPtr || (size < 1)) {
-    return size;
-  }
-
-  if(data->state.prev_block_had_trailing_cr) {
-    /* The previous block of incoming data
-       had a trailing CR, which was turned into a LF. */
-    if(*startPtr == '\n') {
-      /* This block of incoming data starts with the
-         previous block's LF so get rid of it */
-      memmove(startPtr, startPtr + 1, size-1);
-      size--;
-      /* and it wasn't a bare CR but a CRLF conversion instead */
-      data->state.crlf_conversions++;
-    }
-    data->state.prev_block_had_trailing_cr = FALSE; /* reset the flag */
-  }
-
-  /* find 1st CR, if any */
-  inPtr = outPtr = memchr(startPtr, '\r', size);
-  if(inPtr) {
-    /* at least one CR, now look for CRLF */
-    while(inPtr < (startPtr + size-1)) {
-      /* note that it's size-1, so we'll never look past the last byte */
-      if(memcmp(inPtr, "\r\n", 2) == 0) {
-        /* CRLF found, bump past the CR and copy the NL */
-        inPtr++;
-        *outPtr = *inPtr;
-        /* keep track of how many CRLFs we converted */
-        data->state.crlf_conversions++;
-      }
-      else {
-        if(*inPtr == '\r') {
-          /* lone CR, move LF instead */
-          *outPtr = '\n';
-        }
-        else {
-          /* not a CRLF nor a CR, just copy whatever it is */
-          *outPtr = *inPtr;
-        }
-      }
-      outPtr++;
-      inPtr++;
-    } /* end of while loop */
-
-    if(inPtr < startPtr + size) {
-      /* handle last byte */
-      if(*inPtr == '\r') {
-        /* deal with a CR at the end of the buffer */
-        *outPtr = '\n'; /* copy a NL instead */
-        /* note that a CRLF might be split across two blocks */
-        data->state.prev_block_had_trailing_cr = TRUE;
-      }
-      else {
-        /* copy last byte */
-        *outPtr = *inPtr;
-      }
-      outPtr++;
-    }
-    if(outPtr < startPtr + size)
-      /* tidy up by null terminating the now shorter data */
-      *outPtr = '\0';
-
-    return (outPtr - startPtr);
-  }
-  return size;
-}
-#endif /* CURL_DO_LINEEND_CONV && !CURL_DISABLE_FTP */
-
-/*
- * Curl_nwrite() is an internal write function that sends data to the
- * server. Works with a socket index for the connection.
- *
- * If the write would block (CURLE_AGAIN), it returns CURLE_OK and
- * (*nwritten == 0). Otherwise we return regular CURLcode value.
- */
-CURLcode Curl_nwrite(struct Curl_easy *data,
-                     int sockindex,
-                     const void *buf,
-                     size_t blen,
-                     ssize_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);
-  if(result == CURLE_AGAIN) {
-    nwritten = 0;
-    result = CURLE_OK;
-  }
-  else if(result) {
-    nwritten = -1; /* make sure */
-  }
-  else {
-    DEBUGASSERT(nwritten >= 0);
-  }
-
-  *pnwritten = nwritten;
-  return result;
-}
-
-/*
- * Curl_write() is an internal write function that sends data to the
- * server. Works with plain sockets, SCP, SSL or kerberos.
- *
- * If the write would block (CURLE_AGAIN), we return CURLE_OK and
- * (*written == 0). Otherwise we return regular CURLcode value.
- */
-CURLcode Curl_write(struct Curl_easy *data,
-                    curl_socket_t sockfd,
-                    const void *mem,
-                    size_t len,
-                    ssize_t *written)
-{
-  struct connectdata *conn;
-  int num;
-
-  DEBUGASSERT(data);
-  DEBUGASSERT(data->conn);
-  conn = data->conn;
-  num = (sockfd != CURL_SOCKET_BAD && sockfd == conn->sock[SECONDARYSOCKET]);
-  return Curl_nwrite(data, num, mem, len, written);
-}
-
-static CURLcode pausewrite(struct Curl_easy *data,
-                           int type, /* what type of data */
-                           bool paused_body,
-                           const char *ptr,
-                           size_t len)
-{
-  /* signalled to pause sending on this connection, but since we have data
-     we want to send we need to dup it to save a copy for when the sending
-     is again enabled */
-  struct SingleRequest *k = &data->req;
-  struct UrlState *s = &data->state;
-  unsigned int i;
-  bool newtype = TRUE;
-
-  Curl_conn_ev_data_pause(data, TRUE);
-
-  if(s->tempcount) {
-    for(i = 0; i< s->tempcount; i++) {
-      if(s->tempwrite[i].type == type &&
-         !!s->tempwrite[i].paused_body == !!paused_body) {
-        /* data for this type exists */
-        newtype = FALSE;
-        break;
-      }
-    }
-    DEBUGASSERT(i < 3);
-    if(i >= 3)
-      /* There are more types to store than what fits: very bad */
-      return CURLE_OUT_OF_MEMORY;
-  }
-  else
-    i = 0;
-
-  if(newtype) {
-    /* store this information in the state struct for later use */
-    Curl_dyn_init(&s->tempwrite[i].b, DYN_PAUSE_BUFFER);
-    s->tempwrite[i].type = type;
-    s->tempwrite[i].paused_body = paused_body;
-    s->tempcount++;
-  }
-
-  if(Curl_dyn_addn(&s->tempwrite[i].b, (unsigned char *)ptr, len))
-    return CURLE_OUT_OF_MEMORY;
-
-  /* mark the connection as RECV paused */
-  k->keepon |= KEEP_RECV_PAUSE;
-
-  return CURLE_OK;
-}
-
-
-/* chop_write() writes chunks of data not larger than CURL_MAX_WRITE_SIZE via
- * client write callback(s) and takes care of pause requests from the
- * callbacks.
- */
-static CURLcode chop_write(struct Curl_easy *data,
-                           int type,
-                           bool skip_body_write,
-                           char *optr,
-                           size_t olen)
-{
-  struct connectdata *conn = data->conn;
-  curl_write_callback writeheader = NULL;
-  curl_write_callback writebody = NULL;
-  char *ptr = optr;
-  size_t len = olen;
-  void *writebody_ptr = data->set.out;
-
-  if(!len)
-    return CURLE_OK;
-
-  /* If reading is paused, append this data to the already held data for this
-     type. */
-  if(data->req.keepon & KEEP_RECV_PAUSE)
-    return pausewrite(data, type, !skip_body_write, ptr, len);
-
-  /* Determine the callback(s) to use. */
-  if(!skip_body_write &&
-     ((type & CLIENTWRITE_BODY) ||
-      ((type & CLIENTWRITE_HEADER) && data->set.include_header))) {
-    writebody = data->set.fwrite_func;
-  }
-  if((type & (CLIENTWRITE_HEADER|CLIENTWRITE_INFO)) &&
-     (data->set.fwrite_header || data->set.writeheader)) {
-    /*
-     * Write headers to the same callback or to the especially setup
-     * header callback function (added after version 7.7.1).
-     */
-    writeheader =
-      data->set.fwrite_header? data->set.fwrite_header: data->set.fwrite_func;
-  }
-
-  /* Chop data, write chunks. */
-  while(len) {
-    size_t chunklen = len <= CURL_MAX_WRITE_SIZE? len: CURL_MAX_WRITE_SIZE;
-
-    if(writebody) {
-      size_t wrote;
-      Curl_set_in_callback(data, true);
-      wrote = writebody(ptr, 1, chunklen, writebody_ptr);
-      Curl_set_in_callback(data, false);
-
-      if(CURL_WRITEFUNC_PAUSE == wrote) {
-        if(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;
-        }
-        return pausewrite(data, type, TRUE, ptr, len);
-      }
-      if(wrote != chunklen) {
-        failf(data, "Failure writing output to destination");
-        return CURLE_WRITE_ERROR;
-      }
-    }
-
-    ptr += chunklen;
-    len -= chunklen;
-  }
-
-#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HEADERS_API)
-  /* HTTP header, but not status-line */
-  if((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
-     (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, optr, htype);
-    if(result)
-      return result;
-  }
-#endif
-
-  if(writeheader) {
-    size_t wrote;
-
-    Curl_set_in_callback(data, true);
-    wrote = writeheader(optr, 1, olen, data->set.writeheader);
-    Curl_set_in_callback(data, false);
-
-    if(CURL_WRITEFUNC_PAUSE == wrote)
-      return pausewrite(data, type, FALSE, optr, olen);
-    if(wrote != olen) {
-      failf(data, "Failed writing header");
-      return CURLE_WRITE_ERROR;
-    }
-  }
-
-  return CURLE_OK;
-}
-
+static CURLcode do_init_writer_stack(struct Curl_easy *data);
 
 
 /* Curl_client_write() sends data to the write callback(s)
 /* Curl_client_write() sends data to the write callback(s)
 
 
    The bit pattern defines to what "streams" to write to. Body and/or header.
    The bit pattern defines to what "streams" to write to. Body and/or header.
    The defines are in sendf.h of course.
    The defines are in sendf.h of course.
-
-   If CURL_DO_LINEEND_CONV is enabled, data is converted IN PLACE to the
-   local character encoding.  This is a problem and should be changed in
-   the future to leave the original data alone.
  */
  */
 CURLcode Curl_client_write(struct Curl_easy *data,
 CURLcode Curl_client_write(struct Curl_easy *data,
-                           int type, char *buf, size_t blen)
+                           int type, const char *buf, size_t blen)
 {
 {
   CURLcode result;
   CURLcode result;
 
 
-#if !defined(CURL_DISABLE_FTP) && defined(CURL_DO_LINEEND_CONV)
-  /* FTP data may need conversion. */
-  if((type & CLIENTWRITE_BODY) &&
-     (data->conn->handler->protocol & PROTO_FAMILY_FTP) &&
-     data->conn->proto.ftpc.transfertype == 'A') {
-    /* convert end-of-line markers */
-    blen = convert_lineends(data, buf, blen);
-  }
-#endif
   /* it is one of those, at least */
   /* it is one of those, at least */
   DEBUGASSERT(type & (CLIENTWRITE_BODY|CLIENTWRITE_HEADER|CLIENTWRITE_INFO));
   DEBUGASSERT(type & (CLIENTWRITE_BODY|CLIENTWRITE_HEADER|CLIENTWRITE_INFO));
   /* BODY is only BODY (with optional EOS) */
   /* BODY is only BODY (with optional EOS) */
@@ -405,7 +82,7 @@ CURLcode Curl_client_write(struct Curl_easy *data,
               ((type & ~(CLIENTWRITE_INFO|CLIENTWRITE_EOS)) == 0));
               ((type & ~(CLIENTWRITE_INFO|CLIENTWRITE_EOS)) == 0));
 
 
   if(!data->req.writer_stack) {
   if(!data->req.writer_stack) {
-    result = do_init_stack(data);
+    result = do_init_writer_stack(data);
     if(result)
     if(result)
       return result;
       return result;
     DEBUGASSERT(data->req.writer_stack);
     DEBUGASSERT(data->req.writer_stack);
@@ -414,58 +91,86 @@ CURLcode Curl_client_write(struct Curl_easy *data,
   return Curl_cwriter_write(data, data->req.writer_stack, type, buf, blen);
   return Curl_cwriter_write(data, data->req.writer_stack, type, buf, blen);
 }
 }
 
 
-CURLcode Curl_client_unpause(struct Curl_easy *data)
-{
-  CURLcode result = CURLE_OK;
-
-  if(data->state.tempcount) {
-    /* there are buffers for sending that can be delivered as the receive
-       pausing is lifted! */
-    unsigned int i;
-    unsigned int count = data->state.tempcount;
-    struct tempbuf writebuf[3]; /* there can only be three */
-
-    /* copy the structs to allow for immediate re-pausing */
-    for(i = 0; i < data->state.tempcount; i++) {
-      writebuf[i] = data->state.tempwrite[i];
-      Curl_dyn_init(&data->state.tempwrite[i].b, DYN_PAUSE_BUFFER);
-    }
-    data->state.tempcount = 0;
-
-    for(i = 0; i < count; i++) {
-      /* even if one function returns error, this loops through and frees
-         all buffers */
-      if(!result)
-        result = chop_write(data, writebuf[i].type,
-                            !writebuf[i].paused_body,
-                            Curl_dyn_ptr(&writebuf[i].b),
-                            Curl_dyn_len(&writebuf[i].b));
-      Curl_dyn_free(&writebuf[i].b);
-    }
-  }
-  return result;
-}
-
-void Curl_client_cleanup(struct Curl_easy *data)
+static void cl_reset_writer(struct Curl_easy *data)
 {
 {
   struct Curl_cwriter *writer = data->req.writer_stack;
   struct Curl_cwriter *writer = data->req.writer_stack;
-  size_t i;
-
   while(writer) {
   while(writer) {
     data->req.writer_stack = writer->next;
     data->req.writer_stack = writer->next;
     writer->cwt->do_close(data, writer);
     writer->cwt->do_close(data, writer);
     free(writer);
     free(writer);
     writer = data->req.writer_stack;
     writer = data->req.writer_stack;
   }
   }
+}
 
 
-  for(i = 0; i < data->state.tempcount; i++) {
-    Curl_dyn_free(&data->state.tempwrite[i].b);
+static void cl_reset_reader(struct Curl_easy *data)
+{
+  struct Curl_creader *reader = data->req.reader_stack;
+  while(reader) {
+    data->req.reader_stack = reader->next;
+    reader->crt->do_close(data, reader);
+    free(reader);
+    reader = data->req.reader_stack;
   }
   }
-  data->state.tempcount = 0;
+}
+
+void Curl_client_cleanup(struct Curl_easy *data)
+{
+  DEBUGF(infof(data, "Curl_client_cleanup()"));
+  cl_reset_reader(data);
+  cl_reset_writer(data);
+
   data->req.bytecount = 0;
   data->req.bytecount = 0;
   data->req.headerline = 0;
   data->req.headerline = 0;
 }
 }
 
 
+void Curl_client_reset(struct Curl_easy *data)
+{
+  if(data->req.rewind_read) {
+    /* already requested */
+    DEBUGF(infof(data, "Curl_client_reset(), will rewind_read"));
+  }
+  else {
+    DEBUGF(infof(data, "Curl_client_reset(), clear readers"));
+    cl_reset_reader(data);
+  }
+  cl_reset_writer(data);
+
+  data->req.bytecount = 0;
+  data->req.headerline = 0;
+}
+
+CURLcode Curl_client_start(struct Curl_easy *data)
+{
+  if(data->req.rewind_read) {
+    struct Curl_creader *r = data->req.reader_stack;
+    CURLcode result = CURLE_OK;
+
+    DEBUGF(infof(data, "client start, rewind readers"));
+    while(r) {
+      result = r->crt->rewind(data, r);
+      if(result) {
+        failf(data, "rewind of client reader '%s' failed: %d",
+              r->crt->name, result);
+        return result;
+      }
+      r = r->next;
+    }
+    data->req.rewind_read = FALSE;
+    cl_reset_reader(data);
+  }
+  return CURLE_OK;
+}
+
+bool Curl_creader_will_rewind(struct Curl_easy *data)
+{
+  return data->req.rewind_read;
+}
+
+void Curl_creader_set_rewind(struct Curl_easy *data, bool enable)
+{
+  data->req.rewind_read = !!enable;
+}
+
 /* Write data using an unencoding writer stack. "nbytes" is not
 /* Write data using an unencoding writer stack. "nbytes" is not
    allowed to be 0. */
    allowed to be 0. */
 CURLcode Curl_cwriter_write(struct Curl_easy *data,
 CURLcode Curl_cwriter_write(struct Curl_easy *data,
@@ -499,26 +204,6 @@ void Curl_cwriter_def_close(struct Curl_easy *data,
   (void) writer;
   (void) writer;
 }
 }
 
 
-/* Real client writer to installed callbacks. */
-static CURLcode cw_client_write(struct Curl_easy *data,
-                                struct Curl_cwriter *writer, int type,
-                                const char *buf, size_t nbytes)
-{
-  (void)writer;
-  if(!nbytes)
-    return CURLE_OK;
-  return chop_write(data, type, FALSE, (char *)buf, nbytes);
-}
-
-static const struct Curl_cwtype cw_client = {
-  "client",
-  NULL,
-  Curl_cwriter_def_init,
-  cw_client_write,
-  Curl_cwriter_def_close,
-  sizeof(struct Curl_cwriter)
-};
-
 static size_t get_max_body_write_len(struct Curl_easy *data, curl_off_t limit)
 static size_t get_max_body_write_len(struct Curl_easy *data, curl_off_t limit)
 {
 {
   if(limit != -1) {
   if(limit != -1) {
@@ -541,28 +226,32 @@ static size_t get_max_body_write_len(struct Curl_easy *data, curl_off_t limit)
   return SIZE_T_MAX;
   return SIZE_T_MAX;
 }
 }
 
 
+struct cw_download_ctx {
+  struct Curl_cwriter super;
+  BIT(started_response);
+};
 /* Download client writer in phase CURL_CW_PROTOCOL that
 /* Download client writer in phase CURL_CW_PROTOCOL that
  * sees the "real" download body data. */
  * sees the "real" download body data. */
 static CURLcode cw_download_write(struct Curl_easy *data,
 static CURLcode cw_download_write(struct Curl_easy *data,
                                   struct Curl_cwriter *writer, int type,
                                   struct Curl_cwriter *writer, int type,
                                   const char *buf, size_t nbytes)
                                   const char *buf, size_t nbytes)
 {
 {
+  struct cw_download_ctx *ctx = writer->ctx;
   CURLcode result;
   CURLcode result;
   size_t nwrite, excess_len = 0;
   size_t nwrite, excess_len = 0;
+  bool is_connect = !!(type & CLIENTWRITE_CONNECT);
+
+  if(!is_connect && !ctx->started_response) {
+    Curl_pgrsTime(data, TIMER_STARTTRANSFER);
+    ctx->started_response = TRUE;
+  }
 
 
   if(!(type & CLIENTWRITE_BODY)) {
   if(!(type & CLIENTWRITE_BODY)) {
-    if((type & CLIENTWRITE_CONNECT) && data->set.suppress_connect_headers)
+    if(is_connect && data->set.suppress_connect_headers)
       return CURLE_OK;
       return CURLE_OK;
     return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
     return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
   }
   }
 
 
-  if(!data->req.bytecount) {
-    Curl_pgrsTime(data, TIMER_STARTTRANSFER);
-    if(data->req.exp100 > EXP100_SEND_DATA)
-      /* set time stamp to compare with when waiting for the 100 */
-      data->req.start100 = Curl_now();
-  }
-
   /* Here, we deal with REAL BODY bytes. All filtering and transfer
   /* Here, we deal with REAL BODY bytes. All filtering and transfer
    * encodings have been applied and only the true content, e.g. BODY,
    * encodings have been applied and only the true content, e.g. BODY,
    * bytes are passed here.
    * bytes are passed here.
@@ -575,6 +264,9 @@ static CURLcode cw_download_write(struct Curl_easy *data,
     DEBUGF(infof(data, "did not want a BODY, but seeing %zu bytes",
     DEBUGF(infof(data, "did not want a BODY, but seeing %zu bytes",
                  nbytes));
                  nbytes));
     data->req.download_done = TRUE;
     data->req.download_done = TRUE;
+    if(data->info.header_size)
+      /* if headers have been received, this is fine */
+      return CURLE_OK;
     return CURLE_WEIRD_SERVER_REPLY;
     return CURLE_WEIRD_SERVER_REPLY;
   }
   }
 
 
@@ -604,14 +296,14 @@ static CURLcode cw_download_write(struct Curl_easy *data,
     }
     }
   }
   }
 
 
-  /* Update stats, write and report progress */
-  data->req.bytecount += nwrite;
-  ++data->req.bodywrites;
-  if(!data->req.ignorebody && nwrite) {
+  if(!data->req.ignorebody && (nwrite || (type & CLIENTWRITE_EOS))) {
     result = Curl_cwriter_write(data, writer->next, type, buf, nwrite);
     result = Curl_cwriter_write(data, writer->next, type, buf, nwrite);
     if(result)
     if(result)
       return result;
       return result;
   }
   }
+  /* Update stats, write and report progress */
+  data->req.bytecount += nwrite;
+  ++data->req.bodywrites;
   result = Curl_pgrsSetDownloadCounter(data, data->req.bytecount);
   result = Curl_pgrsSetDownloadCounter(data, data->req.bytecount);
   if(result)
   if(result)
     return result;
     return result;
@@ -646,7 +338,7 @@ static const struct Curl_cwtype cw_download = {
   Curl_cwriter_def_init,
   Curl_cwriter_def_init,
   cw_download_write,
   cw_download_write,
   Curl_cwriter_def_close,
   Curl_cwriter_def_close,
-  sizeof(struct Curl_cwriter)
+  sizeof(struct cw_download_ctx)
 };
 };
 
 
 /* RAW client writer in phase CURL_CW_RAW that
 /* RAW client writer in phase CURL_CW_RAW that
@@ -676,15 +368,18 @@ CURLcode Curl_cwriter_create(struct Curl_cwriter **pwriter,
                                    const struct Curl_cwtype *cwt,
                                    const struct Curl_cwtype *cwt,
                                    Curl_cwriter_phase phase)
                                    Curl_cwriter_phase phase)
 {
 {
-  struct Curl_cwriter *writer;
+  struct Curl_cwriter *writer = NULL;
   CURLcode result = CURLE_OUT_OF_MEMORY;
   CURLcode result = CURLE_OUT_OF_MEMORY;
+  void *p;
 
 
   DEBUGASSERT(cwt->cwriter_size >= sizeof(struct Curl_cwriter));
   DEBUGASSERT(cwt->cwriter_size >= sizeof(struct Curl_cwriter));
-  writer = (struct Curl_cwriter *) calloc(1, cwt->cwriter_size);
-  if(!writer)
+  p = calloc(1, cwt->cwriter_size);
+  if(!p)
     goto out;
     goto out;
 
 
+  writer = (struct Curl_cwriter *)p;
   writer->cwt = cwt;
   writer->cwt = cwt;
+  writer->ctx = p;
   writer->phase = phase;
   writer->phase = phase;
   result = cwt->do_init(data, writer);
   result = cwt->do_init(data, writer);
 
 
@@ -716,14 +411,14 @@ size_t Curl_cwriter_count(struct Curl_easy *data, Curl_cwriter_phase phase)
   return n;
   return n;
 }
 }
 
 
-static CURLcode do_init_stack(struct Curl_easy *data)
+static CURLcode do_init_writer_stack(struct Curl_easy *data)
 {
 {
   struct Curl_cwriter *writer;
   struct Curl_cwriter *writer;
   CURLcode result;
   CURLcode result;
 
 
   DEBUGASSERT(!data->req.writer_stack);
   DEBUGASSERT(!data->req.writer_stack);
   result = Curl_cwriter_create(&data->req.writer_stack,
   result = Curl_cwriter_create(&data->req.writer_stack,
-                               data, &cw_client, CURL_CW_CLIENT);
+                               data, &Curl_cwt_out, CURL_CW_CLIENT);
   if(result)
   if(result)
     return result;
     return result;
 
 
@@ -752,7 +447,7 @@ CURLcode Curl_cwriter_add(struct Curl_easy *data,
   struct Curl_cwriter **anchor = &data->req.writer_stack;
   struct Curl_cwriter **anchor = &data->req.writer_stack;
 
 
   if(!*anchor) {
   if(!*anchor) {
-    result = do_init_stack(data);
+    result = do_init_writer_stack(data);
     if(result)
     if(result)
       return result;
       return result;
   }
   }
@@ -766,6 +461,28 @@ CURLcode Curl_cwriter_add(struct Curl_easy *data,
   return CURLE_OK;
   return CURLE_OK;
 }
 }
 
 
+struct Curl_cwriter *Curl_cwriter_get_by_name(struct Curl_easy *data,
+                                              const char *name)
+{
+  struct Curl_cwriter *writer;
+  for(writer = data->req.writer_stack; writer; writer = writer->next) {
+    if(!strcmp(name, writer->cwt->name))
+      return writer;
+  }
+  return NULL;
+}
+
+struct Curl_cwriter *Curl_cwriter_get_by_type(struct Curl_easy *data,
+                                              const struct Curl_cwtype *cwt)
+{
+  struct Curl_cwriter *writer;
+  for(writer = data->req.writer_stack; writer; writer = writer->next) {
+    if(writer->cwt == cwt)
+      return writer;
+  }
+  return NULL;
+}
+
 void Curl_cwriter_remove_by_name(struct Curl_easy *data,
 void Curl_cwriter_remove_by_name(struct Curl_easy *data,
                                  const char *name)
                                  const char *name)
 {
 {
@@ -782,40 +499,844 @@ void Curl_cwriter_remove_by_name(struct Curl_easy *data,
   }
   }
 }
 }
 
 
-/*
- * Internal read-from-socket function. This is meant to deal with plain
- * sockets, SSL sockets and kerberos sockets.
- *
- * Returns a regular CURLcode value.
- */
-CURLcode Curl_read(struct Curl_easy *data,   /* transfer */
-                   curl_socket_t sockfd,     /* read from this socket */
-                   char *buf,                /* store read data here */
-                   size_t sizerequested,     /* max amount to read */
-                   ssize_t *n)               /* amount bytes read */
-{
-  CURLcode result = CURLE_RECV_ERROR;
-  ssize_t nread = 0;
-  size_t bytesfromsocket = 0;
-  char *buffertofill = NULL;
-  struct connectdata *conn = data->conn;
-
-  /* Set 'num' to 0 or 1, depending on which socket that has been sent here.
-     If it is the second socket, we set num to 1. Otherwise to 0. This lets
-     us use the correct ssl handle. */
-  int num = (sockfd == conn->sock[SECONDARYSOCKET]);
-
-  *n = 0; /* reset amount to zero */
-
-  bytesfromsocket = CURLMIN(sizerequested, (size_t)data->set.buffer_size);
-  buffertofill = buf;
-
-  nread = conn->recv[num](data, num, buffertofill, bytesfromsocket, &result);
-  if(nread < 0)
+CURLcode Curl_creader_read(struct Curl_easy *data,
+                           struct Curl_creader *reader,
+                           char *buf, size_t blen, size_t *nread, bool *eos)
+{
+  if(!reader)
+    return CURLE_READ_ERROR;
+  return reader->crt->do_read(data, reader, buf, blen, nread, eos);
+}
+
+CURLcode Curl_creader_def_init(struct Curl_easy *data,
+                               struct Curl_creader *reader)
+{
+  (void)data;
+  (void)reader;
+  return CURLE_OK;
+}
+
+void Curl_creader_def_close(struct Curl_easy *data,
+                            struct Curl_creader *reader)
+{
+  (void)data;
+  (void)reader;
+}
+
+CURLcode Curl_creader_def_read(struct Curl_easy *data,
+                               struct Curl_creader *reader,
+                               char *buf, size_t blen,
+                               size_t *nread, bool *eos)
+{
+  if(reader->next)
+    return reader->next->crt->do_read(data, reader->next, buf, blen,
+                                      nread, eos);
+  else {
+    *nread = 0;
+    *eos = FALSE;
+    return CURLE_READ_ERROR;
+  }
+}
+
+bool Curl_creader_def_needs_rewind(struct Curl_easy *data,
+                                   struct Curl_creader *reader)
+{
+  (void)data;
+  (void)reader;
+  return FALSE;
+}
+
+curl_off_t Curl_creader_def_total_length(struct Curl_easy *data,
+                                         struct Curl_creader *reader)
+{
+  return reader->next?
+         reader->next->crt->total_length(data, reader->next) : -1;
+}
+
+CURLcode Curl_creader_def_resume_from(struct Curl_easy *data,
+                                      struct Curl_creader *reader,
+                                      curl_off_t offset)
+{
+  (void)data;
+  (void)reader;
+  (void)offset;
+  return CURLE_READ_ERROR;
+}
+
+CURLcode Curl_creader_def_rewind(struct Curl_easy *data,
+                                 struct Curl_creader *reader)
+{
+  (void)data;
+  (void)reader;
+  return CURLE_OK;
+}
+
+CURLcode Curl_creader_def_unpause(struct Curl_easy *data,
+                                  struct Curl_creader *reader)
+{
+  (void)data;
+  (void)reader;
+  return CURLE_OK;
+}
+
+void Curl_creader_def_done(struct Curl_easy *data,
+                           struct Curl_creader *reader, int premature)
+{
+  (void)data;
+  (void)reader;
+  (void)premature;
+}
+
+struct cr_in_ctx {
+  struct Curl_creader super;
+  curl_read_callback read_cb;
+  void *cb_user_data;
+  curl_off_t total_len;
+  curl_off_t read_len;
+  CURLcode error_result;
+  BIT(seen_eos);
+  BIT(errored);
+  BIT(has_used_cb);
+};
+
+static CURLcode cr_in_init(struct Curl_easy *data, struct Curl_creader *reader)
+{
+  struct cr_in_ctx *ctx = reader->ctx;
+  (void)data;
+  ctx->read_cb = data->state.fread_func;
+  ctx->cb_user_data = data->state.in;
+  ctx->total_len = -1;
+  ctx->read_len = 0;
+  return CURLE_OK;
+}
+
+/* Real client reader to installed client callbacks. */
+static CURLcode cr_in_read(struct Curl_easy *data,
+                           struct Curl_creader *reader,
+                           char *buf, size_t blen,
+                           size_t *pnread, bool *peos)
+{
+  struct cr_in_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(ctx->read_cb && blen) {
+    Curl_set_in_callback(data, true);
+    nread = ctx->read_cb(buf, 1, blen, ctx->cb_user_data);
+    Curl_set_in_callback(data, false);
+    ctx->has_used_cb = TRUE;
+  }
+
+  switch(nread) {
+  case 0:
+    if((ctx->total_len >= 0) && (ctx->read_len < ctx->total_len)) {
+      failf(data, "client read function 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:
+    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 */
+    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_in_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_in_needs_rewind(struct Curl_easy *data,
+                               struct Curl_creader *reader)
+{
+  struct cr_in_ctx *ctx = reader->ctx;
+  (void)data;
+  return ctx->has_used_cb;
+}
+
+static curl_off_t cr_in_total_length(struct Curl_easy *data,
+                                     struct Curl_creader *reader)
+{
+  struct cr_in_ctx *ctx = reader->ctx;
+  (void)data;
+  return ctx->total_len;
+}
+
+static CURLcode cr_in_resume_from(struct Curl_easy *data,
+                                  struct Curl_creader *reader,
+                                  curl_off_t offset)
+{
+  struct cr_in_ctx *ctx = reader->ctx;
+  int seekerr = CURL_SEEKFUNC_CANTSEEK;
+
+  DEBUGASSERT(data->conn);
+  /* already started reading? */
+  if(ctx->read_len)
+    return CURLE_READ_ERROR;
+
+  if(data->set.seek_func) {
+    Curl_set_in_callback(data, true);
+    seekerr = data->set.seek_func(data->set.seek_client, offset, SEEK_SET);
+    Curl_set_in_callback(data, false);
+  }
+
+  if(seekerr != CURL_SEEKFUNC_OK) {
+    curl_off_t passed = 0;
+
+    if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
+      failf(data, "Could not seek stream");
+      return CURLE_READ_ERROR;
+    }
+    /* when seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
+    do {
+      char scratch[4*1024];
+      size_t readthisamountnow =
+        (offset - passed > (curl_off_t)sizeof(scratch)) ?
+        sizeof(scratch) :
+        curlx_sotouz(offset - passed);
+      size_t actuallyread;
+
+      Curl_set_in_callback(data, true);
+      actuallyread = ctx->read_cb(scratch, 1, readthisamountnow,
+                                  ctx->cb_user_data);
+      Curl_set_in_callback(data, false);
+
+      passed += actuallyread;
+      if((actuallyread == 0) || (actuallyread > 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 input", 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, "File already completely uploaded");
+      return CURLE_PARTIAL_FILE;
+    }
+  }
+  /* we've passed, proceed as normal */
+  return CURLE_OK;
+}
+
+static CURLcode cr_in_rewind(struct Curl_easy *data,
+                             struct Curl_creader *reader)
+{
+  struct cr_in_ctx *ctx = reader->ctx;
+
+  /* If we never invoked the callback, there is noting to rewind */
+  if(!ctx->has_used_cb)
+    return CURLE_OK;
+
+  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);
+    DEBUGF(infof(data, "cr_in, rewind via set.seek_func -> %d", err));
+    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);
+    DEBUGF(infof(data, "cr_in, rewind via set.ioctl_func -> %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) {
+      int err = fseek(data->state.in, 0, SEEK_SET);
+      DEBUGF(infof(data, "cr_in, rewind via fseek -> %d(%d)",
+             (int)err, (int)errno));
+      if(-1 != err)
+        /* 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;
+}
+
+
+static const struct Curl_crtype cr_in = {
+  "cr-in",
+  cr_in_init,
+  cr_in_read,
+  Curl_creader_def_close,
+  cr_in_needs_rewind,
+  cr_in_total_length,
+  cr_in_resume_from,
+  cr_in_rewind,
+  Curl_creader_def_unpause,
+  Curl_creader_def_done,
+  sizeof(struct cr_in_ctx)
+};
+
+CURLcode Curl_creader_create(struct Curl_creader **preader,
+                             struct Curl_easy *data,
+                             const struct Curl_crtype *crt,
+                             Curl_creader_phase phase)
+{
+  struct Curl_creader *reader = NULL;
+  CURLcode result = CURLE_OUT_OF_MEMORY;
+  void *p;
+
+  DEBUGASSERT(crt->creader_size >= sizeof(struct Curl_creader));
+  p = calloc(1, crt->creader_size);
+  if(!p)
     goto out;
     goto out;
 
 
-  *n += nread;
-  result = CURLE_OK;
+  reader = (struct Curl_creader *)p;
+  reader->crt = crt;
+  reader->ctx = p;
+  reader->phase = phase;
+  result = crt->do_init(data, reader);
+
 out:
 out:
+  *preader = result? NULL : reader;
+  if(result)
+    free(reader);
+  return result;
+}
+
+void Curl_creader_free(struct Curl_easy *data, struct Curl_creader *reader)
+{
+  if(reader) {
+    reader->crt->do_close(data, reader);
+    free(reader);
+  }
+}
+
+struct cr_lc_ctx {
+  struct Curl_creader super;
+  struct bufq buf;
+  BIT(read_eos);  /* we read an EOS from the next reader */
+  BIT(eos);       /* we have returned an EOS */
+};
+
+static CURLcode cr_lc_init(struct Curl_easy *data, struct Curl_creader *reader)
+{
+  struct cr_lc_ctx *ctx = reader->ctx;
+  (void)data;
+  Curl_bufq_init2(&ctx->buf, (16 * 1024), 1, BUFQ_OPT_SOFT_LIMIT);
+  return CURLE_OK;
+}
+
+static void cr_lc_close(struct Curl_easy *data, struct Curl_creader *reader)
+{
+  struct cr_lc_ctx *ctx = reader->ctx;
+  (void)data;
+  Curl_bufq_free(&ctx->buf);
+}
+
+/* client reader doing line end conversions. */
+static CURLcode cr_lc_read(struct Curl_easy *data,
+                           struct Curl_creader *reader,
+                           char *buf, size_t blen,
+                           size_t *pnread, bool *peos)
+{
+  struct cr_lc_ctx *ctx = reader->ctx;
+  CURLcode result;
+  size_t nread, i, start, n;
+  bool eos;
+
+  if(ctx->eos) {
+    *pnread = 0;
+    *peos = TRUE;
+    return CURLE_OK;
+  }
+
+  if(Curl_bufq_is_empty(&ctx->buf)) {
+    if(ctx->read_eos) {
+      ctx->eos = TRUE;
+      *pnread = 0;
+      *peos = TRUE;
+      return CURLE_OK;
+    }
+    /* Still getting data form the next reader, ctx->buf is empty */
+    result = Curl_creader_read(data, reader->next, buf, blen, &nread, &eos);
+    if(result)
+      return result;
+    ctx->read_eos = eos;
+
+    if(!nread || !memchr(buf, '\n', nread)) {
+      /* nothing to convert, return this right away */
+      if(ctx->read_eos)
+        ctx->eos = TRUE;
+      *pnread = nread;
+      *peos = ctx->eos;
+      return CURLE_OK;
+    }
+
+    /* at least one \n needs conversion to '\r\n', place into ctx->buf */
+    for(i = start = 0; i < nread; ++i) {
+      if(buf[i] != '\n')
+        continue;
+      /* on a soft limit bufq, we do not need to check length */
+      result = Curl_bufq_cwrite(&ctx->buf, buf + start, i - start, &n);
+      if(!result)
+        result = Curl_bufq_cwrite(&ctx->buf, STRCONST("\r\n"), &n);
+      if(result)
+        return result;
+      start = i + 1;
+      if(!data->set.crlf && (data->state.infilesize != -1)) {
+        /* we're here only because FTP is in ASCII mode...
+           bump infilesize for the LF we just added */
+        data->state.infilesize++;
+        /* comment: this might work for FTP, but in HTTP we could not change
+         * the content length after having started the request... */
+      }
+    }
+  }
+
+  DEBUGASSERT(!Curl_bufq_is_empty(&ctx->buf));
+  *peos = FALSE;
+  result = Curl_bufq_cread(&ctx->buf, buf, blen, pnread);
+  if(!result && ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) {
+    /* no more data, read all, done. */
+    ctx->eos = TRUE;
+    *peos = TRUE;
+  }
+  return result;
+}
+
+static curl_off_t cr_lc_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_lc = {
+  "cr-lineconv",
+  cr_lc_init,
+  cr_lc_read,
+  cr_lc_close,
+  Curl_creader_def_needs_rewind,
+  cr_lc_total_length,
+  Curl_creader_def_resume_from,
+  Curl_creader_def_rewind,
+  Curl_creader_def_unpause,
+  Curl_creader_def_done,
+  sizeof(struct cr_lc_ctx)
+};
+
+static CURLcode cr_lc_add(struct Curl_easy *data)
+{
+  struct Curl_creader *reader = NULL;
+  CURLcode result;
+
+  result = Curl_creader_create(&reader, data, &cr_lc,
+                               CURL_CR_CONTENT_ENCODE);
+  if(!result)
+    result = Curl_creader_add(data, reader);
+
+  if(result && reader)
+    Curl_creader_free(data, reader);
+  return result;
+}
+
+static CURLcode do_init_reader_stack(struct Curl_easy *data,
+                                     struct Curl_creader *r)
+{
+  CURLcode result = CURLE_OK;
+  curl_off_t clen;
+
+  DEBUGASSERT(r);
+  DEBUGASSERT(r->crt);
+  DEBUGASSERT(r->phase == CURL_CR_CLIENT);
+  DEBUGASSERT(!data->req.reader_stack);
+
+  data->req.reader_stack = r;
+  clen = r->crt->total_length(data, r);
+  /* if we do not have 0 length init, and crlf conversion is wanted,
+   * add the reader for it */
+  if(clen && (data->set.crlf
+#ifdef CURL_DO_LINEEND_CONV
+     || data->state.prefer_ascii
+#endif
+    )) {
+    result = cr_lc_add(data);
+    if(result)
+      return result;
+  }
+
+  return result;
+}
+
+CURLcode Curl_creader_set_fread(struct Curl_easy *data, curl_off_t len)
+{
+  CURLcode result;
+  struct Curl_creader *r;
+  struct cr_in_ctx *ctx;
+
+  result = Curl_creader_create(&r, data, &cr_in, CURL_CR_CLIENT);
+  if(result)
+    return result;
+  ctx = r->ctx;
+  ctx->total_len = len;
+
+  cl_reset_reader(data);
+  return do_init_reader_stack(data, r);
+}
+
+CURLcode Curl_creader_add(struct Curl_easy *data,
+                          struct Curl_creader *reader)
+{
+  CURLcode result;
+  struct Curl_creader **anchor = &data->req.reader_stack;
+
+  if(!*anchor) {
+    result = Curl_creader_set_fread(data, data->state.infilesize);
+    if(result)
+      return result;
+  }
+
+  /* Insert the writer as first in its phase.
+   * Skip existing readers of lower phases. */
+  while(*anchor && (*anchor)->phase < reader->phase)
+    anchor = &((*anchor)->next);
+  reader->next = *anchor;
+  *anchor = reader;
+  return CURLE_OK;
+}
+
+CURLcode Curl_creader_set(struct Curl_easy *data, struct Curl_creader *r)
+{
+  CURLcode result;
+
+  DEBUGASSERT(r);
+  DEBUGASSERT(r->crt);
+  DEBUGASSERT(r->phase == CURL_CR_CLIENT);
+
+  cl_reset_reader(data);
+  result = do_init_reader_stack(data, r);
+  if(result)
+    Curl_creader_free(data, r);
+  return result;
+}
+
+CURLcode Curl_client_read(struct Curl_easy *data, char *buf, size_t blen,
+                          size_t *nread, bool *eos)
+{
+  CURLcode result;
+
+  DEBUGASSERT(buf);
+  DEBUGASSERT(blen);
+  DEBUGASSERT(nread);
+  DEBUGASSERT(eos);
+
+  if(!data->req.reader_stack) {
+    result = Curl_creader_set_fread(data, data->state.infilesize);
+    if(result)
+      return result;
+    DEBUGASSERT(data->req.reader_stack);
+  }
+
+  result = Curl_creader_read(data, data->req.reader_stack, buf, blen,
+                             nread, eos);
+  return result;
+}
+
+bool Curl_creader_needs_rewind(struct Curl_easy *data)
+{
+  struct Curl_creader *reader = data->req.reader_stack;
+  while(reader) {
+    if(reader->crt->needs_rewind(data, reader))
+      return TRUE;
+    reader = reader->next;
+  }
+  return FALSE;
+}
+
+static CURLcode cr_null_read(struct Curl_easy *data,
+                             struct Curl_creader *reader,
+                             char *buf, size_t blen,
+                             size_t *pnread, bool *peos)
+{
+  (void)data;
+  (void)reader;
+  (void)buf;
+  (void)blen;
+  *pnread = 0;
+  *peos = TRUE;
+  return CURLE_OK;
+}
+
+static curl_off_t cr_null_total_length(struct Curl_easy *data,
+                                       struct Curl_creader *reader)
+{
+  /* this reader changes length depending on input */
+  (void)data;
+  (void)reader;
+  return 0;
+}
+
+static const struct Curl_crtype cr_null = {
+  "cr-null",
+  Curl_creader_def_init,
+  cr_null_read,
+  Curl_creader_def_close,
+  Curl_creader_def_needs_rewind,
+  cr_null_total_length,
+  Curl_creader_def_resume_from,
+  Curl_creader_def_rewind,
+  Curl_creader_def_unpause,
+  Curl_creader_def_done,
+  sizeof(struct Curl_creader)
+};
+
+CURLcode Curl_creader_set_null(struct Curl_easy *data)
+{
+  struct Curl_creader *r;
+  CURLcode result;
+
+  result = Curl_creader_create(&r, data, &cr_null, CURL_CR_CLIENT);
+  if(result)
+    return result;
+
+  cl_reset_reader(data);
+  return do_init_reader_stack(data, r);
+}
+
+struct cr_buf_ctx {
+  struct Curl_creader super;
+  const char *buf;
+  size_t blen;
+  size_t index;
+};
+
+static CURLcode cr_buf_read(struct Curl_easy *data,
+                            struct Curl_creader *reader,
+                            char *buf, size_t blen,
+                            size_t *pnread, bool *peos)
+{
+  struct cr_buf_ctx *ctx = reader->ctx;
+  size_t nread = ctx->blen - ctx->index;
+
+  (void)data;
+  if(!nread || !ctx->buf) {
+    *pnread = 0;
+    *peos = TRUE;
+  }
+  else {
+    if(nread > blen)
+      nread = blen;
+    memcpy(buf, ctx->buf + ctx->index, nread);
+    *pnread = nread;
+    ctx->index += nread;
+    *peos = (ctx->index == ctx->blen);
+  }
+  return CURLE_OK;
+}
+
+static bool cr_buf_needs_rewind(struct Curl_easy *data,
+                                struct Curl_creader *reader)
+{
+  struct cr_buf_ctx *ctx = reader->ctx;
+  (void)data;
+  return ctx->index > 0;
+}
+
+static curl_off_t cr_buf_total_length(struct Curl_easy *data,
+                                      struct Curl_creader *reader)
+{
+  struct cr_buf_ctx *ctx = reader->ctx;
+  (void)data;
+  return (curl_off_t)ctx->blen;
+}
+
+static CURLcode cr_buf_resume_from(struct Curl_easy *data,
+                                   struct Curl_creader *reader,
+                                   curl_off_t offset)
+{
+  struct cr_buf_ctx *ctx = reader->ctx;
+  size_t boffset;
+
+  (void)data;
+  DEBUGASSERT(data->conn);
+  /* already started reading? */
+  if(ctx->index)
+    return CURLE_READ_ERROR;
+  if(offset <= 0)
+    return CURLE_OK;
+  boffset = (size_t)offset;
+  if(boffset > ctx->blen)
+    return CURLE_READ_ERROR;
+
+  ctx->buf += boffset;
+  ctx->blen -= boffset;
+  return CURLE_OK;
+}
+
+static const struct Curl_crtype cr_buf = {
+  "cr-buf",
+  Curl_creader_def_init,
+  cr_buf_read,
+  Curl_creader_def_close,
+  cr_buf_needs_rewind,
+  cr_buf_total_length,
+  cr_buf_resume_from,
+  Curl_creader_def_rewind,
+  Curl_creader_def_unpause,
+  Curl_creader_def_done,
+  sizeof(struct cr_buf_ctx)
+};
+
+CURLcode Curl_creader_set_buf(struct Curl_easy *data,
+                               const char *buf, size_t blen)
+{
+  CURLcode result;
+  struct Curl_creader *r;
+  struct cr_buf_ctx *ctx;
+
+  result = Curl_creader_create(&r, data, &cr_buf, CURL_CR_CLIENT);
+  if(result)
+    return result;
+  ctx = r->ctx;
+  ctx->buf = buf;
+  ctx->blen = blen;
+  ctx->index = 0;
+
+  cl_reset_reader(data);
+  return do_init_reader_stack(data, r);
+}
+
+curl_off_t Curl_creader_total_length(struct Curl_easy *data)
+{
+  struct Curl_creader *r = data->req.reader_stack;
+  return r? r->crt->total_length(data, r) : -1;
+}
+
+curl_off_t Curl_creader_client_length(struct Curl_easy *data)
+{
+  struct Curl_creader *r = data->req.reader_stack;
+  while(r && r->phase != CURL_CR_CLIENT)
+    r = r->next;
+  return r? r->crt->total_length(data, r) : -1;
+}
+
+CURLcode Curl_creader_resume_from(struct Curl_easy *data, curl_off_t offset)
+{
+  struct Curl_creader *r = data->req.reader_stack;
+  while(r && r->phase != CURL_CR_CLIENT)
+    r = r->next;
+  return r? r->crt->resume_from(data, r, offset) : CURLE_READ_ERROR;
+}
+
+CURLcode Curl_creader_unpause(struct Curl_easy *data)
+{
+  struct Curl_creader *reader = data->req.reader_stack;
+  CURLcode result = CURLE_OK;
+
+  while(reader) {
+    result = reader->crt->unpause(data, reader);
+    if(result)
+      break;
+    reader = reader->next;
+  }
   return result;
   return result;
 }
 }
+
+void Curl_creader_done(struct Curl_easy *data, int premature)
+{
+  struct Curl_creader *reader = data->req.reader_stack;
+  while(reader) {
+    reader->crt->done(data, reader, premature);
+    reader = reader->next;
+  }
+}
+
+struct Curl_creader *Curl_creader_get_by_type(struct Curl_easy *data,
+                                              const struct Curl_crtype *crt)
+{
+  struct Curl_creader *r;
+  for(r = data->req.reader_stack; r; r = r->next) {
+    if(r->crt == crt)
+      return r;
+  }
+  return NULL;
+
+}

+ 227 - 23
lib/sendf.h

@@ -55,20 +55,25 @@
  * Write `len` bytes at `prt` to the client. `type` indicates what
  * Write `len` bytes at `prt` to the client. `type` indicates what
  * kind of data is being written.
  * 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;
                            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.
+ * Free all resources related to client writing.
  */
  */
-CURLcode Curl_client_unpause(struct Curl_easy *data);
+void Curl_client_cleanup(struct Curl_easy *data);
 
 
 /**
 /**
- * Free all resources related to client writing.
+ * Reset readers and writer chains, keep rewind information
+ * when necessary.
  */
  */
-void Curl_client_cleanup(struct Curl_easy *data);
+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.
  * Client Writers - a chain passing transfer BODY data to the client.
@@ -112,10 +117,16 @@ struct Curl_cwtype {
   size_t cwriter_size;  /* sizeof() allocated struct Curl_cwriter */
   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 {
 struct Curl_cwriter {
   const struct Curl_cwtype *cwt;  /* type implementation */
   const struct Curl_cwtype *cwt;  /* type implementation */
   struct Curl_cwriter *next;  /* Downstream writer. */
   struct Curl_cwriter *next;  /* Downstream writer. */
+  void *ctx;                  /* allocated instance pointer */
   Curl_cwriter_phase phase; /* phase at which it operates */
   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,
 CURLcode Curl_cwriter_add(struct Curl_easy *data,
                           struct Curl_cwriter *writer);
                           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,
 void Curl_cwriter_remove_by_name(struct Curl_easy *data,
                                  const char *name);
                                  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
  * Convenience method for calling `writer->do_write()` that
  * checks for NULL writer.
  * checks for NULL writer.
@@ -172,22 +193,205 @@ void Curl_cwriter_def_close(struct Curl_easy *data,
                             struct Curl_cwriter *writer);
                             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 */
+};
+
+/* 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);
 
 
-/* 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);
+/**
+ * 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 */
 #endif /* HEADER_CURL_SENDF_H */

+ 10 - 14
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)
 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)
   if(!str)
     return CURLE_BAD_FUNCTION_ARGUMENT;
     return CURLE_BAD_FUNCTION_ARGUMENT;
 
 
@@ -163,8 +169,6 @@ static CURLcode protocol2num(const char *str, curl_prot_t *val)
     return CURLE_OK;
     return CURLE_OK;
   }
   }
 
 
-  *val = 0;
-
   do {
   do {
     const char *token = str;
     const char *token = str;
     size_t tlen;
     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.
      * The application kindly asks for a differently sized receive buffer.
      * If it seems reasonable, we'll use it.
      * If it seems reasonable, we'll use it.
      */
      */
-    if(data->state.buffer)
-      return CURLE_BAD_FUNCTION_ARGUMENT;
-
     arg = va_arg(param, long);
     arg = va_arg(param, long);
 
 
     if(arg > READBUFFER_MAX)
     if(arg > READBUFFER_MAX)
@@ -2238,7 +2239,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
       arg = UPLOADBUFFER_MIN;
       arg = UPLOADBUFFER_MIN;
 
 
     data->set.upload_buffer_size = (unsigned int)arg;
     data->set.upload_buffer_size = (unsigned int)arg;
-    Curl_safefree(data->state.ulbuf); /* force a realloc next opportunity */
     break;
     break;
 
 
   case CURLOPT_NOSIGNAL:
   case CURLOPT_NOSIGNAL:
@@ -2657,22 +2657,18 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     break;
     break;
 
 
   case CURLOPT_PROTOCOLS_STR: {
   case CURLOPT_PROTOCOLS_STR: {
-    curl_prot_t prot;
     argptr = va_arg(param, char *);
     argptr = va_arg(param, char *);
-    result = protocol2num(argptr, &prot);
+    result = protocol2num(argptr, &data->set.allowed_protocols);
     if(result)
     if(result)
       return result;
       return result;
-    data->set.allowed_protocols = prot;
     break;
     break;
   }
   }
 
 
   case CURLOPT_REDIR_PROTOCOLS_STR: {
   case CURLOPT_REDIR_PROTOCOLS_STR: {
-    curl_prot_t prot;
     argptr = va_arg(param, char *);
     argptr = va_arg(param, char *);
-    result = protocol2num(argptr, &prot);
+    result = protocol2num(argptr, &data->set.redir_protocols);
     if(result)
     if(result)
       return result;
       return result;
-    data->set.redir_protocols = prot;
     break;
     break;
   }
   }
 
 
@@ -2867,13 +2863,13 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
 #endif
 #endif
   case CURLOPT_TLSAUTH_TYPE:
   case CURLOPT_TLSAUTH_TYPE:
     argptr = va_arg(param, char *);
     argptr = va_arg(param, char *);
-    if(argptr && !strncasecompare(argptr, "SRP", strlen("SRP")))
+    if(argptr && !strcasecompare(argptr, "SRP"))
       return CURLE_BAD_FUNCTION_ARGUMENT;
       return CURLE_BAD_FUNCTION_ARGUMENT;
     break;
     break;
 #ifndef CURL_DISABLE_PROXY
 #ifndef CURL_DISABLE_PROXY
   case CURLOPT_PROXY_TLSAUTH_TYPE:
   case CURLOPT_PROXY_TLSAUTH_TYPE:
     argptr = va_arg(param, char *);
     argptr = va_arg(param, char *);
-    if(argptr || !strncasecompare(argptr, "SRP", strlen("SRP")))
+    if(argptr && !strcasecompare(argptr, "SRP"))
       return CURLE_BAD_FUNCTION_ARGUMENT;
       return CURLE_BAD_FUNCTION_ARGUMENT;
     break;
     break;
 #endif
 #endif

+ 26 - 25
lib/smb.c

@@ -456,6 +456,9 @@ static CURLcode smb_connect(struct Curl_easy *data, bool *done)
   smbc->recv_buf = malloc(MAX_MESSAGE_SIZE);
   smbc->recv_buf = malloc(MAX_MESSAGE_SIZE);
   if(!smbc->recv_buf)
   if(!smbc->recv_buf)
     return CURLE_OUT_OF_MEMORY;
     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 */
   /* Multiple requests are allowed with this connection */
   connkeep(conn, "SMB default");
   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)
 static CURLcode smb_recv_message(struct Curl_easy *data, void **msg)
 {
 {
   struct connectdata *conn = data->conn;
   struct connectdata *conn = data->conn;
-  curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
   struct smb_conn *smbc = &conn->proto.smbc;
   struct smb_conn *smbc = &conn->proto.smbc;
   char *buf = smbc->recv_buf;
   char *buf = smbc->recv_buf;
   ssize_t bytes_read;
   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;
   size_t len = MAX_MESSAGE_SIZE - smbc->got;
   CURLcode result;
   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)
   if(result)
     return 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);
   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)
                          size_t upload_size)
 {
 {
   struct connectdata *conn = data->conn;
   struct connectdata *conn = data->conn;
   struct smb_conn *smbc = &conn->proto.smbc;
   struct smb_conn *smbc = &conn->proto.smbc;
-  ssize_t bytes_written;
+  size_t bytes_written;
   CURLcode result;
   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)
   if(result)
     return result;
     return result;
 
 
@@ -587,16 +588,15 @@ static CURLcode smb_flush(struct Curl_easy *data)
 {
 {
   struct connectdata *conn = data->conn;
   struct connectdata *conn = data->conn;
   struct smb_conn *smbc = &conn->proto.smbc;
   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;
   CURLcode result;
 
 
   if(!smbc->send_size)
   if(!smbc->send_size)
     return CURLE_OK;
     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)
   if(result)
     return 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,
 static CURLcode smb_send_message(struct Curl_easy *data, unsigned char cmd,
                                  const void *msg, size_t msg_len)
                                  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);
                      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);
   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)
 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_write *msg;
   struct smb_request *req = data->req.p.smb;
   struct smb_request *req = data->req.p.smb;
   curl_off_t offset = data->req.offset;
   curl_off_t offset = data->req.offset;
   curl_off_t upload_size = data->req.size - data->req.bytecount;
   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 */
   if(upload_size >= MAX_PAYLOAD_SIZE - 1) /* There is one byte of padding */
     upload_size = MAX_PAYLOAD_SIZE - 1;
     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 */
   /* Check if there is data in the transfer buffer */
   if(!smbc->send_size && smbc->upload_size) {
   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)
     if(result && result != CURLE_AGAIN)
       return result;
       return result;
     if(!nread)
     if(!nread)
@@ -1133,6 +1133,7 @@ static CURLcode smb_disconnect(struct Curl_easy *data,
   Curl_safefree(smbc->share);
   Curl_safefree(smbc->share);
   Curl_safefree(smbc->domain);
   Curl_safefree(smbc->domain);
   Curl_safefree(smbc->recv_buf);
   Curl_safefree(smbc->recv_buf);
+  Curl_safefree(smbc->send_buf);
   return CURLE_OK;
   return CURLE_OK;
 }
 }
 
 

+ 1 - 0
lib/smb.h

@@ -42,6 +42,7 @@ struct smb_conn {
   unsigned int session_key;
   unsigned int session_key;
   unsigned short uid;
   unsigned short uid;
   char *recv_buf;
   char *recv_buf;
+  char *send_buf;
   size_t upload_size;
   size_t upload_size;
   size_t send_size;
   size_t send_size;
   size_t sent;
   size_t sent;

+ 185 - 165
lib/smtp.c

@@ -111,6 +111,7 @@ static CURLcode smtp_continue_auth(struct Curl_easy *data, const char *mech,
                                    const struct bufref *resp);
                                    const struct bufref *resp);
 static CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech);
 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 smtp_get_message(struct Curl_easy *data, struct bufref *out);
+static CURLcode cr_eob_add(struct Curl_easy *data);
 
 
 /*
 /*
  * SMTP protocol handler.
  * 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],
     result = smtp_parse_address(data->set.str[STRING_MAIL_FROM],
                                 &address, &host);
                                 &address, &host);
     if(result)
     if(result)
-      return result;
+      goto out;
 
 
     /* Establish whether we should report SMTPUTF8 to the server for this
     /* 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 */
        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 */
     /* Null reverse-path, RFC-5321, sect. 3.6.3 */
     from = strdup("<>");
     from = strdup("<>");
 
 
-  if(!from)
-    return CURLE_OUT_OF_MEMORY;
+  if(!from) {
+    result = CURLE_OUT_OF_MEMORY;
+    goto out;
+  }
 
 
   /* Calculate the optional AUTH parameter */
   /* Calculate the optional AUTH parameter */
   if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.sasl.authused) {
   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 */
          converting the host name to an IDN A-label if necessary */
       result = smtp_parse_address(data->set.str[STRING_MAIL_AUTH],
       result = smtp_parse_address(data->set.str[STRING_MAIL_AUTH],
                                   &address, &host);
                                   &address, &host);
-      if(result) {
-        free(from);
-        return result;
-      }
+      if(result)
+        goto out;
 
 
       /* Establish whether we should report SMTPUTF8 to the server for this
       /* 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 */
          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
         /* An invalid mailbox was provided but we'll simply let the server
            worry about it */
            worry about it */
         auth = aprintf("<%s>", address);
         auth = aprintf("<%s>", address);
-
       free(address);
       free(address);
     }
     }
     else
     else
@@ -684,12 +684,12 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
       auth = strdup("<>");
       auth = strdup("<>");
 
 
     if(!auth) {
     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. */
   /* Prepare the mime data if some. */
   if(data->set.mimepost.kind != MIMEKIND_NONE) {
   if(data->set.mimepost.kind != MIMEKIND_NONE) {
     /* Use the whole structure as data. */
     /* 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,
         result = Curl_mime_add_header(&data->set.mimepost.curlheaders,
                                       "Mime-Version: 1.0");
                                       "Mime-Version: 1.0");
 
 
-    /* Make sure we will read the entire mime structure. */
     if(!result)
     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 */
   /* 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);
     size = aprintf("%" CURL_FORMAT_CURL_OFF_T, data->state.infilesize);
 
 
     if(!size) {
     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 */
   /* Send the MAIL command */
   result = Curl_pp_sendf(data, &conn->proto.smtpc.pp,
   result = Curl_pp_sendf(data, &conn->proto.smtpc.pp,
                          "MAIL FROM:%s%s%s%s%s%s",
                          "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 */
                          utf8 ? " SMTPUTF8"    /* Internationalised mailbox */
                                : "");          /* included in our envelope  */
                                : "");          /* included in our envelope  */
 
 
+out:
   free(from);
   free(from);
   free(auth);
   free(auth);
   free(size);
   free(size);
@@ -1162,7 +1162,7 @@ static CURLcode smtp_state_data_resp(struct Curl_easy *data, int smtpcode,
     Curl_pgrsSetUploadSize(data, data->state.infilesize);
     Curl_pgrsSetUploadSize(data, data->state.infilesize);
 
 
     /* SMTP upload */
     /* SMTP upload */
-    Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
+    Curl_xfer_setup(data, -1, -1, FALSE, FIRSTSOCKET);
 
 
     /* End of DO phase */
     /* End of DO phase */
     smtp_state(data, SMTP_STOP);
     smtp_state(data, SMTP_STOP);
@@ -1194,7 +1194,6 @@ static CURLcode smtp_statemachine(struct Curl_easy *data,
                                   struct connectdata *conn)
                                   struct connectdata *conn)
 {
 {
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
-  curl_socket_t sock = conn->sock[FIRSTSOCKET];
   int smtpcode;
   int smtpcode;
   struct smtp_conn *smtpc = &conn->proto.smtpc;
   struct smtp_conn *smtpc = &conn->proto.smtpc;
   struct pingpong *pp = &smtpc->pp;
   struct pingpong *pp = &smtpc->pp;
@@ -1210,7 +1209,7 @@ static CURLcode smtp_statemachine(struct Curl_easy *data,
 
 
   do {
   do {
     /* Read the response from the server */
     /* 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)
     if(result)
       return result;
       return result;
 
 
@@ -1392,10 +1391,6 @@ static CURLcode smtp_done(struct Curl_easy *data, CURLcode status,
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
   struct connectdata *conn = data->conn;
   struct connectdata *conn = data->conn;
   struct SMTP *smtp = data->req.p.smtp;
   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;
   (void)premature;
 
 
@@ -1410,47 +1405,7 @@ static CURLcode smtp_done(struct Curl_easy *data, CURLcode status,
     result = status;         /* use the already set error code */
     result = status;         /* use the already set error code */
   }
   }
   else if(!data->set.connect_only && data->set.mail_rcpt &&
   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);
     smtp_state(data, SMTP_POSTDATA);
 
 
@@ -1502,7 +1457,7 @@ static CURLcode smtp_perform(struct Curl_easy *data, bool *connected,
   smtp->eob = 2;
   smtp->eob = 2;
 
 
   /* Start the first command in the DO phase */
   /* 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 */
     /* MAIL transfer */
     result = smtp_perform_mail(data);
     result = smtp_perform_mail(data);
   else
   else
@@ -1593,7 +1548,7 @@ static CURLcode smtp_dophase_done(struct Curl_easy *data, bool connected)
 
 
   if(smtp->transfer != PPTRANSFER_BODY)
   if(smtp->transfer != PPTRANSFER_BODY)
     /* no data to transfer */
     /* no data to transfer */
-    Curl_setup_transfer(data, -1, -1, FALSE, -1);
+    Curl_xfer_setup(data, -1, -1, FALSE, -1);
 
 
   return CURLE_OK;
   return CURLE_OK;
 }
 }
@@ -1818,108 +1773,173 @@ static CURLcode smtp_parse_address(const char *fqma, char **address,
   return result;
   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;
+  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;
+}
 
 
-  /* Do we need to allocate a scratch buffer? */
-  if(!scratch || data->set.crlf) {
-    oldscratch = scratch;
+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);
+}
 
 
-    scratch = newscratch = malloc(2 * data->set.upload_buffer_size);
-    if(!newscratch) {
-      failf(data, "Failed to alloc scratch buffer");
+/* this is the 5-bytes End-Of-Body marker for SMTP */
+#define SMTP_EOB "\r\n.\r\n"
+#define SMTP_EOB_FIND_LEN 3
 
 
-      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;
+/* 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;
 
 
-      eob_sent = 0;
+    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++;
+        }
 
 
-      /* Reset the trailing CRLF flag as there was more data */
-      smtp->trailing_crlf = FALSE;
+        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;
+      }
     }
     }
 
 
-    /* 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;
+    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;
     }
     }
-    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;
+  *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;
+}
 
 
-  /* Only use the new buffer if we replaced something */
-  if(si != nread) {
-    /* Upload from the new (replaced) buffer instead */
-    data->req.upload_fromhere = scratch;
+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;
+}
 
 
-    /* Save the buffer so it can be freed later */
-    data->state.scratch = scratch;
+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)
+};
 
 
-    /* Free the old scratch buffer */
-    free(oldscratch);
+static CURLcode cr_eob_add(struct Curl_easy *data)
+{
+  struct Curl_creader *reader = NULL;
+  CURLcode result;
 
 
-    /* Set the new amount too */
-    data->req.upload_present = si;
-  }
-  else
-    free(newscratch);
+  result = Curl_creader_create(&reader, data, &cr_eob,
+                               CURL_CR_CONTENT_ENCODE);
+  if(!result)
+    result = Curl_creader_add(data, reader);
 
 
-  return CURLE_OK;
+  if(result && reader)
+    Curl_creader_free(data, reader);
+  return result;
 }
 }
 
 
 #endif /* CURL_DISABLE_SMTP */
 #endif /* CURL_DISABLE_SMTP */

+ 0 - 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_smtp;
 extern const struct Curl_handler Curl_handler_smtps;
 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 */
 #endif /* HEADER_CURL_SMTP_H */

+ 2 - 2
lib/socks.c

@@ -341,7 +341,7 @@ static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf,
 
 
   case CONNECT_RESOLVING:
   case CONNECT_RESOLVING:
     /* check if we have the name resolved by now */
     /* 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) {
     if(dns) {
 #ifdef CURLRES_ASYNCH
 #ifdef CURLRES_ASYNCH
@@ -1175,7 +1175,7 @@ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf,
   result = connect_SOCKS(cf, sx, data);
   result = connect_SOCKS(cf, sx, data);
   if(!result && sx->state == CONNECT_DONE) {
   if(!result && sx->state == CONNECT_DONE) {
     cf->connected = TRUE;
     cf->connected = TRUE;
-    Curl_verboseconnect(data, conn);
+    Curl_verboseconnect(data, conn, cf->sockindex);
     socks_proxy_cf_free(cf);
     socks_proxy_cf_free(cf);
   }
   }
 
 

+ 1 - 1
lib/socks_gssapi.c

@@ -475,7 +475,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
                               gss_recv_token.length, &actualread);
                               gss_recv_token.length, &actualread);
 
 
   if(result || (actualread != us_length)) {
   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_release_buffer(&gss_status, &gss_recv_token);
     gss_delete_sec_context(&gss_status, &gss_context, NULL);
     gss_delete_sec_context(&gss_status, &gss_context, NULL);
     return CURLE_COULDNT_CONNECT;
     return CURLE_COULDNT_CONNECT;

+ 8 - 13
lib/strtoofft.c

@@ -79,11 +79,10 @@ static int get_char(char c, int base);
 static curl_off_t strtooff(const char *nptr, char **endptr, int base)
 static curl_off_t strtooff(const char *nptr, char **endptr, int base)
 {
 {
   char *end;
   char *end;
-  int is_negative = 0;
-  int overflow;
+  bool is_negative = FALSE;
+  bool overflow = FALSE;
   int i;
   int i;
   curl_off_t value = 0;
   curl_off_t value = 0;
-  curl_off_t newval;
 
 
   /* Skip leading whitespace. */
   /* Skip leading whitespace. */
   end = (char *)nptr;
   end = (char *)nptr;
@@ -93,7 +92,7 @@ static curl_off_t strtooff(const char *nptr, char **endptr, int base)
 
 
   /* Handle the sign, if any. */
   /* Handle the sign, if any. */
   if(end[0] == '-') {
   if(end[0] == '-') {
-    is_negative = 1;
+    is_negative = TRUE;
     end++;
     end++;
   }
   }
   else if(end[0] == '+') {
   else if(end[0] == '+') {
@@ -129,19 +128,15 @@ static curl_off_t strtooff(const char *nptr, char **endptr, int base)
   }
   }
 
 
   /* Loop handling digits. */
   /* Loop handling digits. */
-  value = 0;
-  overflow = 0;
   for(i = get_char(end[0], base);
   for(i = get_char(end[0], base);
       i != -1;
       i != -1;
       end++, i = get_char(end[0], base)) {
       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;
       break;
     }
     }
-    else
-      value = newval;
+    value = base * value + i;
   }
   }
 
 
   if(!overflow) {
   if(!overflow) {
@@ -217,7 +212,7 @@ static int get_char(char c, int base)
 CURLofft curlx_strtoofft(const char *str, char **endp, int base,
 CURLofft curlx_strtoofft(const char *str, char **endp, int base,
                          curl_off_t *num)
                          curl_off_t *num)
 {
 {
-  char *end;
+  char *end = NULL;
   curl_off_t number;
   curl_off_t number;
   errno = 0;
   errno = 0;
   *num = 0; /* clear by default */
   *num = 0; /* clear by default */

+ 13 - 9
lib/telnet.c

@@ -1231,20 +1231,24 @@ process_iac:
 static CURLcode send_telnet_data(struct Curl_easy *data,
 static CURLcode send_telnet_data(struct Curl_easy *data,
                                  char *buffer, ssize_t nread)
                                  char *buffer, ssize_t nread)
 {
 {
-  ssize_t i, outlen;
+  size_t i, outlen;
   unsigned char *outbuf;
   unsigned char *outbuf;
   CURLcode result = CURLE_OK;
   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 connectdata *conn = data->conn;
   struct TELNET *tn = data->req.p.telnet;
   struct TELNET *tn = data->req.p.telnet;
 
 
   DEBUGASSERT(tn);
   DEBUGASSERT(tn);
+  DEBUGASSERT(nread > 0);
+  if(nread < 0)
+    return CURLE_TOO_LARGE;
 
 
   if(memchr(buffer, CURL_IAC, nread)) {
   if(memchr(buffer, CURL_IAC, nread)) {
     /* only use the escape buffer when necessary */
     /* only use the escape buffer when necessary */
     Curl_dyn_reset(&tn->out);
     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);
       result = Curl_dyn_addn(&tn->out, &buffer[i], 1);
       if(!result && ((unsigned char)buffer[i] == CURL_IAC))
       if(!result && ((unsigned char)buffer[i] == CURL_IAC))
         /* IAC is FF in hex */
         /* IAC is FF in hex */
@@ -1255,7 +1259,7 @@ static CURLcode send_telnet_data(struct Curl_easy *data,
     outbuf = Curl_dyn_uptr(&tn->out);
     outbuf = Curl_dyn_uptr(&tn->out);
   }
   }
   else {
   else {
-    outlen = nread;
+    outlen = (size_t)nread;
     outbuf = (unsigned char *)buffer;
     outbuf = (unsigned char *)buffer;
   }
   }
   while(!result && total_written < outlen) {
   while(!result && total_written < outlen) {
@@ -1270,8 +1274,8 @@ static CURLcode send_telnet_data(struct Curl_easy *data,
         break;
         break;
       default:                    /* write! */
       default:                    /* write! */
         bytes_written = 0;
         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;
         total_written += bytes_written;
         break;
         break;
     }
     }
@@ -1464,7 +1468,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
       }
       }
       if(events.lNetworkEvents & FD_READ) {
       if(events.lNetworkEvents & FD_READ) {
         /* read data from network */
         /* 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 */
         /* read would've blocked. Loop again */
         if(result == CURLE_AGAIN)
         if(result == CURLE_AGAIN)
           break;
           break;
@@ -1545,7 +1549,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
     default:                    /* read! */
     default:                    /* read! */
       if(pfd[0].revents & POLLIN) {
       if(pfd[0].revents & POLLIN) {
         /* read data from network */
         /* 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 */
         /* read would've blocked. Loop again */
         if(result == CURLE_AGAIN)
         if(result == CURLE_AGAIN)
           break;
           break;
@@ -1635,7 +1639,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
   }
   }
 #endif
 #endif
   /* mark this as "no further transfer wanted" */
   /* 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;
   return result;
 }
 }

+ 8 - 7
lib/tftp.c

@@ -452,8 +452,6 @@ static CURLcode tftp_send_first(struct tftp_state_data *state,
     if(data->state.upload) {
     if(data->state.upload) {
       /* If we are uploading, send an WRQ */
       /* If we are uploading, send an WRQ */
       setpacketevent(&state->spacket, TFTP_EVENT_WRQ);
       setpacketevent(&state->spacket, TFTP_EVENT_WRQ);
-      state->data->req.upload_fromhere =
-        (char *)state->spacket.data + 4;
       if(data->state.infilesize != -1)
       if(data->state.infilesize != -1)
         Curl_pgrsSetUploadSize(data, data->state.infilesize);
         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;
   struct SingleRequest *k = &data->req;
   size_t cb; /* Bytes currently read */
   size_t cb; /* Bytes currently read */
   char buffer[STRERROR_LEN];
   char buffer[STRERROR_LEN];
+  char *bufptr;
+  bool eos;
 
 
   switch(event) {
   switch(event) {
 
 
@@ -771,13 +771,14 @@ static CURLcode tftp_tx(struct tftp_state_data *state, tftp_event_t event)
      * data block.
      * data block.
      * */
      * */
     state->sbytes = 0;
     state->sbytes = 0;
-    state->data->req.upload_fromhere = (char *)state->spacket.data + 4;
+    bufptr = (char *)state->spacket.data + 4;
     do {
     do {
-      result = Curl_fillreadbuffer(data, state->blksize - state->sbytes, &cb);
+      result = Curl_client_read(data, bufptr, state->blksize - state->sbytes,
+                                &cb, &eos);
       if(result)
       if(result)
         return result;
         return result;
       state->sbytes += (int)cb;
       state->sbytes += (int)cb;
-      state->data->req.upload_fromhere += cb;
+      bufptr += cb;
     } while(state->sbytes < state->blksize && cb);
     } while(state->sbytes < state->blksize && cb);
 
 
     sbytes = sendto(state->sockfd, (void *) state->spacket.data,
     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;
     *done = (state->state == TFTP_STATE_FIN) ? TRUE : FALSE;
     if(*done)
     if(*done)
       /* Tell curl we're done */
       /* Tell curl we're done */
-      Curl_setup_transfer(data, -1, -1, FALSE, -1);
+      Curl_xfer_setup(data, -1, -1, FALSE, -1);
   }
   }
   else {
   else {
     /* no timeouts to handle, check our socket */
     /* 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;
       *done = (state->state == TFTP_STATE_FIN) ? TRUE : FALSE;
       if(*done)
       if(*done)
         /* Tell curl we're 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 */
     /* if rc == 0, then select() timed out */
   }
   }

+ 124 - 589
lib/transfer.c

@@ -63,6 +63,7 @@
 #include "content_encoding.h"
 #include "content_encoding.h"
 #include "hostip.h"
 #include "hostip.h"
 #include "cfilters.h"
 #include "cfilters.h"
+#include "cw-out.h"
 #include "transfer.h"
 #include "transfer.h"
 #include "sendf.h"
 #include "sendf.h"
 #include "speedcheck.h"
 #include "speedcheck.h"
@@ -114,260 +115,6 @@ char *Curl_checkheaders(const struct Curl_easy *data,
 }
 }
 #endif
 #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)
 static int data_pending(struct Curl_easy *data)
 {
 {
   struct connectdata *conn = data->conn;
   struct connectdata *conn = data->conn;
@@ -447,7 +194,7 @@ static ssize_t Curl_xfer_recv_resp(struct Curl_easy *data,
     return 0;
     return 0;
   }
   }
 
 
-  *err = Curl_read(data, data->conn->sockfd, buf, blen, &nread);
+  *err = Curl_xfer_recv(data, buf, blen, &nread);
   if(*err)
   if(*err)
     return -1;
     return -1;
   DEBUGASSERT(nread >= 0);
   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,
 static CURLcode readwrite_data(struct Curl_easy *data,
                                struct SingleRequest *k,
                                struct SingleRequest *k,
-                               int *didwhat, bool *done)
+                               int *didwhat)
 {
 {
   struct connectdata *conn = data->conn;
   struct connectdata *conn = data->conn;
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
-  char *buf;
-  size_t blen;
+  char *buf, *xfer_buf;
+  size_t blen, xfer_blen;
   int maxloops = 10;
   int maxloops = 10;
   curl_off_t total_received = 0;
   curl_off_t total_received = 0;
   bool is_multiplex = FALSE;
   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
   /* This is where we loop until we have read everything there is to
      read or we get a CURLE_AGAIN */
      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);
       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) {
     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;
         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,
     nread = Curl_xfer_recv_resp(data, buf, bytestoread,
@@ -530,8 +279,8 @@ static CURLcode readwrite_data(struct Curl_easy *data,
     }
     }
     total_received += blen;
     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;
       goto out;
 
 
     /* if we are done, we stop receiving. On multiplexed connections,
     /* if we are done, we stop receiving. On multiplexed connections,
@@ -564,22 +313,12 @@ static CURLcode readwrite_data(struct Curl_easy *data,
   }
   }
 
 
 out:
 out:
+  Curl_multi_xfer_buf_release(data, xfer_buf);
   if(result)
   if(result)
     DEBUGF(infof(data, "readwrite_data() -> %d", result));
     DEBUGF(infof(data, "readwrite_data() -> %d", result));
   return 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)
 #if defined(_WIN32) && defined(USE_WINSOCK)
 #ifndef SIO_IDEAL_SEND_BACKLOG_QUERY
 #ifndef SIO_IDEAL_SEND_BACKLOG_QUERY
 #define SIO_IDEAL_SEND_BACKLOG_QUERY 0x4004747B
 #define SIO_IDEAL_SEND_BACKLOG_QUERY 0x4004747B
@@ -602,245 +341,42 @@ static void win_update_buffer_size(curl_socket_t sockfd)
 #endif
 #endif
 
 
 #define curl_upload_refill_watermark(data) \
 #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.
  * 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;
-
-  *didwhat |= KEEP_SEND;
-
-  do {
-    curl_off_t nbody;
-    ssize_t offset = 0;
-
-    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;
+  CURLcode result = CURLE_OK;
 
 
-          /* set the new amount too */
-          k->upload_present = nread;
-        }
-      }
+  if((data->req.keepon & KEEP_SEND_PAUSE))
+    return CURLE_OK;
 
 
-#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 */
-    }
+  /* 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));
 
 
-    /* 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)
     if(result)
       return result;
       return result;
 
 
 #if defined(_WIN32) && defined(USE_WINSOCK)
 #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();
       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
 #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)
 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
  * Curl_readwrite() is the low-level function to be called when data is to
  * be read and written to/from the connection.
  * 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 connectdata *conn = data->conn;
   struct SingleRequest *k = &data->req;
   struct SingleRequest *k = &data->req;
@@ -912,8 +447,8 @@ CURLcode Curl_readwrite(struct Curl_easy *data,
 
 
 #ifdef USE_HYPER
 #ifdef USE_HYPER
   if(conn->datastream) {
   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;
       goto out;
   }
   }
   else {
   else {
@@ -922,16 +457,17 @@ CURLcode Curl_readwrite(struct Curl_easy *data,
      the stream was rewound (in which case we have data in a
      the stream was rewound (in which case we have data in a
      buffer) */
      buffer) */
   if((k->keepon & KEEP_RECV) && (select_bits & CURL_CSELECT_IN)) {
   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;
       goto out;
   }
   }
 
 
   /* If we still have writing to do, we check if we have a writable socket. */
   /* 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 */
     /* write */
 
 
-    result = readwrite_upload(data, conn, &didwhat);
+    result = readwrite_upload(data, &didwhat);
     if(result)
     if(result)
       goto out;
       goto out;
   }
   }
@@ -941,31 +477,6 @@ CURLcode Curl_readwrite(struct Curl_easy *data,
 
 
   now = Curl_now();
   now = Curl_now();
   if(!didwhat) {
   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);
     result = Curl_conn_ev_data_idle(data);
     if(result)
     if(result)
       goto out;
       goto out;
@@ -1002,7 +513,6 @@ CURLcode Curl_readwrite(struct Curl_easy *data,
      * The transfer has been performed. Just make some general checks before
      * The transfer has been performed. Just make some general checks before
      * returning.
      * returning.
      */
      */
-
     if(!(data->req.no_body) && (k->size != -1) &&
     if(!(data->req.no_body) && (k->size != -1) &&
        (k->bytecount != k->size) &&
        (k->bytecount != k->size) &&
 #ifdef CURL_DO_LINEEND_CONV
 #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:
 out:
   if(result)
   if(result)
     DEBUGF(infof(data, "Curl_readwrite() -> %d", 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 = newurl;
   data->state.url_alloc = TRUE;
   data->state.url_alloc = TRUE;
-
+  Curl_req_soft_reset(&data->req, data);
   infof(data, "Issue another request to this URL: '%s'", data->state.url);
   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)) {
        && !(data->set.keep_post & CURL_REDIR_POST_301)) {
       infof(data, "Switch from POST to GET");
       infof(data, "Switch from POST to GET");
       data->state.httpreq = HTTPREQ_GET;
       data->state.httpreq = HTTPREQ_GET;
+      Curl_creader_set_rewind(data, FALSE);
     }
     }
     break;
     break;
   case 302: /* Found */
   case 302: /* Found */
@@ -1471,6 +984,7 @@ CURLcode Curl_follow(struct Curl_easy *data,
        && !(data->set.keep_post & CURL_REDIR_POST_302)) {
        && !(data->set.keep_post & CURL_REDIR_POST_302)) {
       infof(data, "Switch from POST to GET");
       infof(data, "Switch from POST to GET");
       data->state.httpreq = HTTPREQ_GET;
       data->state.httpreq = HTTPREQ_GET;
+      Curl_creader_set_rewind(data, FALSE);
     }
     }
     break;
     break;
 
 
@@ -1573,23 +1087,16 @@ CURLcode Curl_retry_request(struct Curl_easy *data, char **url)
                                 prevent i.e HTTP transfers to return
                                 prevent i.e HTTP transfers to return
                                 error just because nothing has been
                                 error just because nothing has been
                                 transferred! */
                                 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;
   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.
  * upcoming transfer.
  */
  */
-void
-Curl_setup_transfer(
+void Curl_xfer_setup(
   struct Curl_easy *data,   /* transfer */
   struct Curl_easy *data,   /* transfer */
   int sockindex,            /* socket index to read from or -1 */
   int sockindex,            /* socket index to read from or -1 */
   curl_off_t size,          /* -1 if unknown at this point */
   curl_off_t size,          /* -1 if unknown at this point */
@@ -1600,22 +1107,19 @@ Curl_setup_transfer(
 {
 {
   struct SingleRequest *k = &data->req;
   struct SingleRequest *k = &data->req;
   struct connectdata *conn = data->conn;
   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(conn != NULL);
   DEBUGASSERT((sockindex <= 1) && (sockindex >= -1));
   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! */
     /* when multiplexing, the read/write sockets need to be the same! */
     conn->sockfd = sockindex == -1 ?
     conn->sockfd = sockindex == -1 ?
       ((writesockindex == -1 ? CURL_SOCKET_BAD : conn->sock[writesockindex])) :
       ((writesockindex == -1 ? CURL_SOCKET_BAD : conn->sock[writesockindex])) :
       conn->sock[sockindex];
       conn->sock[sockindex];
     conn->writesockfd = conn->sockfd;
     conn->writesockfd = conn->sockfd;
-    if(httpsending)
+    if(want_send)
       /* special and very HTTP-specific */
       /* special and very HTTP-specific */
       writesockindex = FIRSTSOCKET;
       writesockindex = FIRSTSOCKET;
   }
   }
@@ -1644,51 +1148,22 @@ Curl_setup_transfer(
     if(sockindex != -1)
     if(sockindex != -1)
       k->keepon |= KEEP_RECV;
       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) */
   } /* if(k->getheader || !data->req.no_body) */
 
 
 }
 }
 
 
 CURLcode Curl_xfer_write_resp(struct Curl_easy *data,
 CURLcode Curl_xfer_write_resp(struct Curl_easy *data,
                               char *buf, size_t blen,
                               char *buf, size_t blen,
-                              bool is_eos, bool *done)
+                              bool is_eos)
 {
 {
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
 
 
   if(data->conn->handler->write_resp) {
   if(data->conn->handler->write_resp) {
     /* protocol handlers offering this function take full responsibility
     /* protocol handlers offering this function take full responsibility
      * for writing all received download data to the client. */
      * 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 {
   else {
     /* No special handling by protocol handler, write all received data
     /* 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;
   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;
+}

+ 29 - 10
lib/transfer.h

@@ -45,17 +45,11 @@ typedef enum {
 
 
 CURLcode Curl_follow(struct Curl_easy *data, char *newurl,
 CURLcode Curl_follow(struct Curl_easy *data, char *newurl,
                      followtype type);
                      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,
 int Curl_single_getsock(struct Curl_easy *data,
                         struct connectdata *conn, curl_socket_t *socks);
                         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);
 CURLcode Curl_retry_request(struct Curl_easy *data, char **url);
 bool Curl_meets_timecondition(struct Curl_easy *data, time_t timeofdoc);
 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.
  * 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,
 CURLcode Curl_xfer_write_resp(struct Curl_easy *data,
                               char *buf, size_t blen,
                               char *buf, size_t blen,
-                              bool is_eos, bool *done);
+                              bool is_eos);
 
 
 /* This sets up a forthcoming transfer */
 /* 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 */
                      int sockindex,     /* socket index to read from or -1 */
                      curl_off_t size,   /* -1 if unknown at this point */
                      curl_off_t size,   /* -1 if unknown at this point */
                      bool getheader,    /* TRUE if header parsing is wanted */
                      bool getheader,    /* TRUE if header parsing is wanted */
@@ -85,4 +78,30 @@ Curl_setup_transfer (struct Curl_easy *data,
                                            disables */
                                            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 */
 #endif /* HEADER_CURL_TRANSFER_H */

+ 46 - 78
lib/url.c

@@ -261,7 +261,7 @@ CURLcode Curl_close(struct Curl_easy **datap)
     free(data->state.range);
     free(data->state.range);
 
 
   /* freed here just in case DONE wasn't called */
   /* 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 */
   /* Close down all open SSL info and sessions */
   Curl_ssl_close_all(data);
   Curl_ssl_close_all(data);
@@ -269,10 +269,6 @@ CURLcode Curl_close(struct Curl_easy **datap)
   Curl_safefree(data->state.scratch);
   Curl_safefree(data->state.scratch);
   Curl_ssl_free_certinfo(data);
   Curl_ssl_free_certinfo(data);
 
 
-  /* Cleanup possible redirect junk */
-  free(data->req.newurl);
-  data->req.newurl = NULL;
-
   if(data->state.referer_alloc) {
   if(data->state.referer_alloc) {
     Curl_safefree(data->state.referer);
     Curl_safefree(data->state.referer);
     data->state.referer_alloc = FALSE;
     data->state.referer_alloc = FALSE;
@@ -280,9 +276,7 @@ CURLcode Curl_close(struct Curl_easy **datap)
   data->state.referer = NULL;
   data->state.referer = NULL;
 
 
   up_free(data);
   up_free(data);
-  Curl_safefree(data->state.buffer);
   Curl_dyn_free(&data->state.headerb);
   Curl_dyn_free(&data->state.headerb);
-  Curl_safefree(data->state.ulbuf);
   Curl_flush_cookies(data, TRUE);
   Curl_flush_cookies(data, TRUE);
   Curl_altsvc_save(data, data->asi, data->set.str[STRING_ALTSVC]);
   Curl_altsvc_save(data, data->asi, data->set.str[STRING_ALTSVC]);
   Curl_altsvc_cleanup(&data->asi);
   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.proxyuser);
   Curl_safefree(data->state.aptr.proxypasswd);
   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_mime_cleanpart(data->state.formp);
   Curl_safefree(data->state.formp);
   Curl_safefree(data->state.formp);
 #endif
 #endif
@@ -368,7 +353,6 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
   set->fread_func_set = (curl_read_callback)fread;
   set->fread_func_set = (curl_read_callback)fread;
   set->is_fread_set = 0;
   set->is_fread_set = 0;
 
 
-  set->seek_func = ZERO_NULL;
   set->seek_client = ZERO_NULL;
   set->seek_client = ZERO_NULL;
 
 
   set->filesize = -1;        /* we don't know the size */
   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;
   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);
   result = Curl_resolver_init(data, &data->state.async.resolver);
   if(result) {
   if(result) {
     DEBUGF(fprintf(stderr, "Error: resolver_init failed\n"));
     DEBUGF(fprintf(stderr, "Error: resolver_init failed\n"));
+    Curl_req_free(&data->req, data);
     free(data);
     free(data);
     return result;
     return result;
   }
   }
@@ -546,6 +538,7 @@ CURLcode Curl_open(struct Curl_easy **curl)
     Curl_resolver_cleanup(data->state.async.resolver);
     Curl_resolver_cleanup(data->state.async.resolver);
     Curl_dyn_free(&data->state.headerb);
     Curl_dyn_free(&data->state.headerb);
     Curl_freeset(data);
     Curl_freeset(data);
+    Curl_req_free(&data->req, data);
     free(data);
     free(data);
     data = NULL;
     data = NULL;
   }
   }
@@ -1009,9 +1002,9 @@ ConnectionExists(struct Curl_easy *data,
 
 
     if(!canmultiplex) {
     if(!canmultiplex) {
       if(Curl_resolver_asynch() &&
       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 */
             completed yet and until then we don't reuse this connection */
-         !check->primary_ip[0])
+         !check->primary.remote_ip[0])
         continue;
         continue;
     }
     }
 
 
@@ -1334,11 +1327,15 @@ ConnectionExists(struct Curl_easy *data,
  */
  */
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
 void Curl_verboseconnect(struct Curl_easy *data,
 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",
     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
 #endif
 
 
@@ -1358,7 +1355,7 @@ static struct connectdata *allocate_conn(struct Curl_easy *data)
   conn->sockfd = CURL_SOCKET_BAD;
   conn->sockfd = CURL_SOCKET_BAD;
   conn->writesockfd = CURL_SOCKET_BAD;
   conn->writesockfd = CURL_SOCKET_BAD;
   conn->connection_id = -1;    /* no ID */
   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 */
   conn->remote_port = -1; /* unknown at this point */
 
 
   /* Default protocol-independent behavior doesn't support persistent
   /* Default protocol-independent behavior doesn't support persistent
@@ -1971,7 +1968,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data,
   }
   }
   else {
   else {
     unsigned long port = strtoul(data->state.up.port, NULL, 10);
     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 && data->state.allow_port) ?
       data->set.use_port : curlx_ultous(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. */
     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
     /* we check for -1 here since if proxy was detected already, this
        was very likely already set to the proxy port */
        was very likely already set to the proxy port */
-    conn->port = p->defport;
+    conn->primary.remote_port = p->defport;
 
 
   return CURLE_OK;
   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
 #ifndef CURL_DISABLE_PROXY
 
 
@@ -2314,8 +2293,9 @@ static CURLcode parse_proxy(struct Curl_easy *data,
   }
   }
   if(port >= 0) {
   if(port >= 0) {
     proxyinfo->port = port;
     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 */
   /* now, clone the proxy host name */
@@ -3213,8 +3193,8 @@ static CURLcode resolve_proxy(struct Curl_easy *data,
   if(!conn->hostname_resolve)
   if(!conn->hostname_resolve)
     return CURLE_OUT_OF_MEMORY;
     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;
   conn->dns_entry = hostaddr;
   if(rc == CURLRESOLV_PENDING)
   if(rc == CURLRESOLV_PENDING)
     *async = TRUE;
     *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
   /* 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. */
    * 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;
     conn->remote_port;
 
 
   /* Resolve target host right on */
   /* Resolve target host right on */
@@ -3252,8 +3232,8 @@ static CURLcode resolve_host(struct Curl_easy *data,
   if(!conn->hostname_resolve)
   if(!conn->hostname_resolve)
     return CURLE_OUT_OF_MEMORY;
     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;
   conn->dns_entry = hostaddr;
   if(rc == CURLRESOLV_PENDING)
   if(rc == CURLRESOLV_PENDING)
     *async = TRUE;
     *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
     /* this is supposed to be the connect function so we better at least check
        that the file is present here! */
        that the file is present here! */
     DEBUGASSERT(conn->handler->connect_it);
     DEBUGASSERT(conn->handler->connect_it);
-    Curl_persistconninfo(data, conn, NULL, -1);
+    Curl_persistconninfo(data, conn, NULL);
     result = conn->handler->connect_it(data, &done);
     result = conn->handler->connect_it(data, &done);
 
 
     /* Setup a "faked" transfer that'll do nothing */
     /* 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);
         (void)conn->handler->done(data, result, FALSE);
         goto out;
         goto out;
       }
       }
-      Curl_setup_transfer(data, -1, -1, FALSE, -1);
+      Curl_xfer_setup(data, -1, -1, FALSE, -1);
     }
     }
 
 
     /* since we skip do_init() */
     /* since we skip do_init() */
@@ -3621,10 +3601,10 @@ static CURLcode create_conn(struct Curl_easy *data,
 #endif
 #endif
 
 
   /* Setup filter for network connections */
   /* 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;
   conn->bits.tcp_fastopen = data->set.tcp_fastopen;
 
 
   /* Complete the easy's SSL configuration for connection cache matching */
   /* 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. */
   /* 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
    * Resolve the address of the server or proxy
    *************************************************************/
    *************************************************************/
@@ -3849,6 +3822,9 @@ CURLcode Curl_setup_conn(struct Curl_easy *data,
   if(!conn->bits.reuse)
   if(!conn->bits.reuse)
     result = Curl_conn_setup(data, conn, FIRSTSOCKET, conn->dns_entry,
     result = Curl_conn_setup(data, conn, FIRSTSOCKET, conn->dns_entry,
                              CURL_CF_SSL_DEFAULT);
                              CURL_CF_SSL_DEFAULT);
+  if(!result)
+    result = Curl_headers_init(data);
+
   /* not sure we need this flag to be passed around any more */
   /* not sure we need this flag to be passed around any more */
   *protocol_done = FALSE;
   *protocol_done = FALSE;
   return result;
   return result;
@@ -3863,11 +3839,8 @@ CURLcode Curl_connect(struct Curl_easy *data,
 
 
   *asyncp = FALSE; /* assume synchronous resolves by default */
   *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 */
   /* call the stuff that needs to be called */
   result = create_conn(data, &conn, asyncp);
   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)
 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: */
   /* if this is a pushed stream, we need this: */
   CURLcode result = Curl_preconnect(data);
   CURLcode result = Curl_preconnect(data);
   if(result)
   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.done = FALSE; /* *_done() is not called yet */
-  data->state.expect100header = FALSE;
 
 
   if(data->req.no_body)
   if(data->req.no_body)
     /* in HTTP lingo, no body means using the HEAD request... */
     /* in HTTP lingo, no body means using the HEAD request... */
     data->state.httpreq = HTTPREQ_HEAD;
     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_speedinit(data);
   Curl_pgrsSetUploadCounter(data, 0);
   Curl_pgrsSetUploadCounter(data, 0);
   Curl_pgrsSetDownloadCounter(data, 0);
   Curl_pgrsSetDownloadCounter(data, 0);

+ 3 - 3
lib/url.h

@@ -41,7 +41,6 @@ void Curl_disconnect(struct Curl_easy *data,
                      struct connectdata *, bool dead_connection);
                      struct connectdata *, bool dead_connection);
 CURLcode Curl_setup_conn(struct Curl_easy *data,
 CURLcode Curl_setup_conn(struct Curl_easy *data,
                          bool *protocol_done);
                          bool *protocol_done);
-void Curl_free_request_state(struct Curl_easy *data);
 CURLcode Curl_parse_login_details(const char *login, const size_t len,
 CURLcode Curl_parse_login_details(const char *login, const size_t len,
                                   char **userptr, char **passwdptr,
                                   char **userptr, char **passwdptr,
                                   char **optionsptr);
                                   char **optionsptr);
@@ -59,9 +58,10 @@ const struct Curl_handler *Curl_getn_scheme_handler(const char *scheme,
                                              specified */
                                              specified */
 
 
 #ifdef CURL_DISABLE_VERBOSE_STRINGS
 #ifdef CURL_DISABLE_VERBOSE_STRINGS
-#define Curl_verboseconnect(x,y)  Curl_nop_stmt
+#define Curl_verboseconnect(x,y,z)  Curl_nop_stmt
 #else
 #else
-void Curl_verboseconnect(struct Curl_easy *data, struct connectdata *conn);
+void Curl_verboseconnect(struct Curl_easy *data, struct connectdata *conn,
+                         int sockindex);
 #endif
 #endif
 
 
 #if defined(USE_HTTP2) || defined(USE_HTTP3)
 #if defined(USE_HTTP2) || defined(USE_HTTP3)

+ 2 - 2
lib/urlapi.c

@@ -531,7 +531,7 @@ UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host,
     portptr = strchr(hostname, ':');
     portptr = strchr(hostname, ':');
 
 
   if(portptr) {
   if(portptr) {
-    char *rest;
+    char *rest = NULL;
     long port;
     long port;
     size_t keep = portptr - hostname;
     size_t keep = portptr - hostname;
 
 
@@ -681,7 +681,7 @@ static int ipv4_normalize(struct dynbuf *host)
     return HOST_IPV6;
     return HOST_IPV6;
 
 
   while(!done) {
   while(!done) {
-    char *endp;
+    char *endp = NULL;
     unsigned long l;
     unsigned long l;
     if(!ISDIGIT(*c))
     if(!ISDIGIT(*c))
       /* most importantly this doesn't allow a leading plus or minus */
       /* most importantly this doesn't allow a leading plus or minus */

+ 58 - 172
lib/urldata.h

@@ -53,6 +53,8 @@
 #define PORT_GOPHER 70
 #define PORT_GOPHER 70
 #define PORT_MQTT 1883
 #define PORT_MQTT 1883
 
 
+struct curl_trc_featt;
+
 #ifdef USE_WEBSOCKETS
 #ifdef USE_WEBSOCKETS
 /* CURLPROTO_GOPHERS (29) is the highest publicly used protocol bit number,
 /* 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
  * 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 "splay.h"
 #include "dynbuf.h"
 #include "dynbuf.h"
 #include "dynhds.h"
 #include "dynhds.h"
+#include "request.h"
 
 
 /* return the count of bytes sent, or -1 on error */
 /* return the count of bytes sent, or -1 on error */
 typedef ssize_t (Curl_send)(struct Curl_easy *data,   /* transfer */
 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,
 typedef CURLcode (*Curl_datastream)(struct Curl_easy *data,
                                     struct connectdata *conn,
                                     struct connectdata *conn,
                                     int *didwhat,
                                     int *didwhat,
-                                    bool *done,
                                     int select_res);
                                     int select_res);
 #endif
 #endif
 
 
@@ -266,11 +268,17 @@ typedef enum {
 /* SSL backend-specific data; declared differently by each SSL backend */
 /* SSL backend-specific data; declared differently by each SSL backend */
 struct ssl_backend_data;
 struct ssl_backend_data;
 
 
+typedef enum {
+  CURL_SSL_PEER_DNS,
+  CURL_SSL_PEER_IPV4,
+  CURL_SSL_PEER_IPV6
+} ssl_peer_type;
+
 struct ssl_peer {
 struct ssl_peer {
   char *hostname;        /* hostname for verification */
   char *hostname;        /* hostname for verification */
   char *dispname;        /* display version of hostname */
   char *dispname;        /* display version of hostname */
   char *sni;             /* SNI version of hostname or NULL if not usable */
   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 {
 struct ssl_primary_config {
@@ -519,10 +527,6 @@ struct ConnectBits {
                          the TCP layer connect */
                          the TCP layer connect */
   BIT(retry);         /* this connection is about to get closed and then
   BIT(retry);         /* this connection is about to get closed and then
                          re-attempted at another connection. */
                          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
 #ifndef CURL_DISABLE_FTP
   BIT(ftp_use_epsv);  /* As set with CURLOPT_FTP_USE_EPSV, but if we find out
   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
                          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_RECV_PAUSE (1<<4) /* reading is paused */
 #define KEEP_SEND_PAUSE (1<<5) /* writing 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_RECVBITS (KEEP_RECV | KEEP_RECV_HOLD | KEEP_RECV_PAUSE)
 #define KEEP_SENDBITS (KEEP_SEND | KEEP_SEND_HOLD | KEEP_SEND_PAUSE)
 #define KEEP_SENDBITS (KEEP_SEND | KEEP_SEND_HOLD | KEEP_SEND_PAUSE)
 
 
@@ -612,22 +624,6 @@ struct easy_pollset {
   unsigned char actions[MAX_SOCKSPEREASYHANDLE];
   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 {
 enum doh_slots {
   /* Explicit values for first two symbols so as to match hard-coded
   /* Explicit values for first two symbols so as to match hard-coded
    * constants in existing code
    * constants in existing code
@@ -646,111 +642,6 @@ enum doh_slots {
   DOH_PROBE_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.
  * Specific protocol handler.
  */
  */
@@ -819,7 +710,7 @@ struct Curl_handler {
      allow the protocol to do extra handling in writing response to
      allow the protocol to do extra handling in writing response to
      the client. */
      the client. */
   CURLcode (*write_resp)(struct Curl_easy *data, const char *buf, size_t blen,
   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
   /* This function can perform various checks on the connection. See
      CONNCHECK_* for more information about the checks that can be performed,
      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_NONE 0                /* No extra information. */
 #define CONNRESULT_DEAD (1<<0)           /* The connection is dead. */
 #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 proxy_info {
   struct hostname host;
   struct hostname host;
   int port;
   int port;
@@ -930,14 +828,13 @@ struct connectdata {
   struct proxy_info socks_proxy;
   struct proxy_info socks_proxy;
   struct proxy_info http_proxy;
   struct proxy_info http_proxy;
 #endif
 #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
      for the last attempt. When the connection is actually established
      these are updated with data which comes directly from the socket. */
      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 *user;    /* user name string, allocated */
   char *passwd;  /* password string, allocated */
   char *passwd;  /* password string, allocated */
   char *options; /* options string, allocated */
   char *options; /* options string, allocated */
@@ -990,14 +887,17 @@ struct connectdata {
 #endif                        /* however, some of them are ftp specific. */
 #endif                        /* however, some of them are ftp specific. */
 
 
   struct Curl_llist easyq;    /* List of easy handles using this connection */
   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 ************/
   /*************** Request - specific items ************/
 #if defined(USE_WINDOWS_SSPI) && defined(SECPKG_ATTR_ENDPOINT_BINDINGS)
 #if defined(USE_WINDOWS_SSPI) && defined(SECPKG_ATTR_ENDPOINT_BINDINGS)
   CtxtHandle *sslContext;
   CtxtHandle *sslContext;
 #endif
 #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
 #ifdef USE_GSASL
   struct gsasldata gsasl;
   struct gsasldata gsasl;
 #endif
 #endif
@@ -1080,7 +980,6 @@ struct connectdata {
   int socks5_gssapi_enctype;
   int socks5_gssapi_enctype;
 #endif
 #endif
   /* The field below gets set in connect.c:connecthost() */
   /* 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 remote_port; /* the remote port, not the proxy port! */
   int conn_to_port; /* the remote port to connect to. valid only if
   int conn_to_port; /* the remote port to connect to. valid only if
                        bits.conn_to_port is set */
                        bits.conn_to_port is set */
@@ -1135,22 +1034,16 @@ struct PureInfo {
   curl_off_t retry_after; /* info from Retry-After: header */
   curl_off_t retry_after; /* info from Retry-After: header */
   unsigned int header_size;  /* size of read header(s) in bytes */
   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
   int conn_remote_port;  /* this is the "remote port", which is the port
                             number of the used URL, independent of proxy or
                             number of the used URL, independent of proxy or
                             not */
                             not */
-  char conn_local_ip[MAX_IPADR_LEN];
-  int conn_local_port;
   const char *conn_scheme;
   const char *conn_scheme;
   unsigned int conn_protocol;
   unsigned int conn_protocol;
   struct curl_certinfo certs; /* info about the certs. Asked for with
   struct curl_certinfo certs; /* info about the certs. Asked for with
@@ -1158,6 +1051,7 @@ struct PureInfo {
   CURLproxycode pxcode;
   CURLproxycode pxcode;
   BIT(timecond);  /* set to TRUE if the time condition didn't match, which
   BIT(timecond);  /* set to TRUE if the time condition didn't match, which
                      thus made the document NOT get fetched */
                      thus made the document NOT get fetched */
+  BIT(used_proxy); /* the transfer used a proxy */
 };
 };
 
 
 
 
@@ -1263,18 +1157,6 @@ struct Curl_data_priority {
 #endif
 #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 */
 /* Timers */
 typedef enum {
 typedef enum {
   EXPIRE_100_TIMEOUT,
   EXPIRE_100_TIMEOUT,
@@ -1337,8 +1219,6 @@ struct UrlState {
   struct dynbuf headerb; /* buffer to store headers in */
   struct dynbuf headerb; /* buffer to store headers in */
   struct curl_slist *hstslist; /* list of HSTS files set by
   struct curl_slist *hstslist; /* list of HSTS files set by
                                   curl_easy_setopt(HSTS) calls */
                                   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,
   curl_off_t current_speed;  /* the ProgressShow() function sets this,
                                 bytes / second */
                                 bytes / second */
 
 
@@ -1353,8 +1233,6 @@ struct UrlState {
   int retrycount; /* number of retries on a new connection */
   int retrycount; /* number of retries on a new connection */
   struct Curl_ssl_session *session; /* array of 'max_ssl_sessions' size */
   struct Curl_ssl_session *session; /* array of 'max_ssl_sessions' size */
   long sessionage;                  /* number of the most recent session */
   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 */
   int os_errno;  /* filled in with errno whenever an error occurs */
   char *scratch; /* huge buffer[set.buffer_size*2] for upload CRLF replacing */
   char *scratch; /* huge buffer[set.buffer_size*2] for upload CRLF replacing */
   long followlocation; /* redirect counter */
   long followlocation; /* redirect counter */
@@ -1387,8 +1265,6 @@ struct UrlState {
 #if !defined(_WIN32) && !defined(MSDOS) && !defined(__EMX__)
 #if !defined(_WIN32) && !defined(MSDOS) && !defined(__EMX__)
 /* do FTP line-end conversions on most platforms */
 /* do FTP line-end conversions on most platforms */
 #define CURL_DO_LINEEND_CONV
 #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? */
   /* for FTP downloads: how many CRLFs did we converted to LFs? */
   curl_off_t crlf_conversions;
   curl_off_t crlf_conversions;
 #endif
 #endif
@@ -1422,8 +1298,10 @@ struct UrlState {
                                  this should be dealt with in pretransfer */
                                  this should be dealt with in pretransfer */
 #ifndef CURL_DISABLE_HTTP
 #ifndef CURL_DISABLE_HTTP
   curl_mimepart *mimepost;
   curl_mimepart *mimepost;
+#ifndef CURL_DISABLE_FORM_API
   curl_mimepart *formp; /* storage for old API form-posting, allocated on
   curl_mimepart *formp; /* storage for old API form-posting, allocated on
                            demand */
                            demand */
+#endif
   size_t trailers_bytes_sent;
   size_t trailers_bytes_sent;
   struct dynbuf trailers_buf; /* a buffer containing the compiled trailing
   struct dynbuf trailers_buf; /* a buffer containing the compiled trailing
                                  headers */
                                  headers */
@@ -1442,6 +1320,10 @@ struct UrlState {
   CURLcode hresult; /* used to pass return codes back from hyper callbacks */
   CURLcode hresult; /* used to pass return codes back from hyper callbacks */
 #endif
 #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
   /* Dynamically allocated strings, MUST be freed before this struct is
      killed. */
      killed. */
   struct dynamically_allocated_data {
   struct dynamically_allocated_data {
@@ -1490,7 +1372,6 @@ struct UrlState {
   BIT(authproblem); /* TRUE if there's some problem authenticating */
   BIT(authproblem); /* TRUE if there's some problem authenticating */
   /* set after initial USER failure, to prevent an authentication loop */
   /* set after initial USER failure, to prevent an authentication loop */
   BIT(wildcardmatch); /* enable wildcard matching */
   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
   BIT(disableexpect);    /* TRUE if Expect: is disabled due to a previous
                             417 response */
                             417 response */
   BIT(use_range);
   BIT(use_range);
@@ -1509,9 +1390,6 @@ struct UrlState {
   BIT(url_alloc);   /* URL string is malloc()'ed */
   BIT(url_alloc);   /* URL string is malloc()'ed */
   BIT(referer_alloc); /* referer 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(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(upload);         /* upload request */
   BIT(internal); /* internal: true if this easy handle was created for
   BIT(internal); /* internal: true if this easy handle was created for
                     internal use and the user does not have ownership of the
                     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 */
   curl_off_t set_resume_from;  /* continue [ftp] transfer from here */
   struct curl_slist *headers; /* linked list of extra headers */
   struct curl_slist *headers; /* linked list of extra headers */
   struct curl_httppost *httppost;  /* linked list of old POST data */
   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. */
   curl_mimepart mimepost;  /* MIME/POST data. */
+#endif
 #ifndef CURL_DISABLE_TELNET
 #ifndef CURL_DISABLE_TELNET
   struct curl_slist *telnet_options; /* linked list of telnet options */
   struct curl_slist *telnet_options; /* linked list of telnet options */
 #endif
 #endif
@@ -1933,6 +1813,12 @@ struct UserDefined {
 #endif
 #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 Names {
   struct Curl_hash *hostcache;
   struct Curl_hash *hostcache;
   enum {
   enum {

+ 52 - 15
lib/vauth/digest.c

@@ -38,6 +38,7 @@
 #include "curl_hmac.h"
 #include "curl_hmac.h"
 #include "curl_md5.h"
 #include "curl_md5.h"
 #include "curl_sha256.h"
 #include "curl_sha256.h"
+#include "curl_sha512_256.h"
 #include "vtls/vtls.h"
 #include "vtls/vtls.h"
 #include "warnless.h"
 #include "warnless.h"
 #include "strtok.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]);
     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 */
 static void auth_digest_sha256_to_ascii(unsigned char *source, /* 32 bytes */
                                      unsigned char *dest) /* 65 bytes */
                                      unsigned char *dest) /* 65 bytes */
 {
 {
@@ -601,10 +602,20 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
           digest->algo = ALGO_SHA256;
           digest->algo = ALGO_SHA256;
         else if(strcasecompare(content, "SHA-256-SESS"))
         else if(strcasecompare(content, "SHA-256-SESS"))
           digest->algo = ALGO_SHA256SESS;
           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;
           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;
           digest->algo = ALGO_SHA512_256SESS;
+#else  /* ! CURL_HAVE_SHA512_256 */
+          return CURLE_NOT_BUILT_IN;
+#endif /* ! CURL_HAVE_SHA512_256 */
+        }
         else
         else
           return CURLE_BAD_CONTENT_ENCODING;
           return CURLE_BAD_CONTENT_ENCODING;
       }
       }
@@ -717,8 +728,10 @@ static CURLcode auth_create_digest_http_message(
     if(!hashthis)
     if(!hashthis)
       return CURLE_OUT_OF_MEMORY;
       return CURLE_OUT_OF_MEMORY;
 
 
-    hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
+    result = hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
     free(hashthis);
     free(hashthis);
+    if(result)
+      return result;
     convert_to_ascii(hashbuf, (unsigned char *)userh);
     convert_to_ascii(hashbuf, (unsigned char *)userh);
   }
   }
 
 
@@ -738,8 +751,10 @@ static CURLcode auth_create_digest_http_message(
   if(!hashthis)
   if(!hashthis)
     return CURLE_OUT_OF_MEMORY;
     return CURLE_OUT_OF_MEMORY;
 
 
-  hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
+  result = hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
   free(hashthis);
   free(hashthis);
+  if(result)
+    return result;
   convert_to_ascii(hashbuf, ha1);
   convert_to_ascii(hashbuf, ha1);
 
 
   if(digest->algo & SESSION_ALGO) {
   if(digest->algo & SESSION_ALGO) {
@@ -748,8 +763,10 @@ static CURLcode auth_create_digest_http_message(
     if(!tmp)
     if(!tmp)
       return CURLE_OUT_OF_MEMORY;
       return CURLE_OUT_OF_MEMORY;
 
 
-    hash(hashbuf, (unsigned char *) tmp, strlen(tmp));
+    result = hash(hashbuf, (unsigned char *) tmp, strlen(tmp));
     free(tmp);
     free(tmp);
+    if(result)
+      return result;
     convert_to_ascii(hashbuf, ha1);
     convert_to_ascii(hashbuf, ha1);
   }
   }
 
 
@@ -775,7 +792,11 @@ static CURLcode auth_create_digest_http_message(
     char hashed[65];
     char hashed[65];
     char *hashthis2;
     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);
     convert_to_ascii(hashbuf, (unsigned char *)hashed);
 
 
     hashthis2 = aprintf("%s:%s", hashthis, hashed);
     hashthis2 = aprintf("%s:%s", hashthis, hashed);
@@ -786,8 +807,10 @@ static CURLcode auth_create_digest_http_message(
   if(!hashthis)
   if(!hashthis)
     return CURLE_OUT_OF_MEMORY;
     return CURLE_OUT_OF_MEMORY;
 
 
-  hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
+  result = hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
   free(hashthis);
   free(hashthis);
+  if(result)
+    return result;
   convert_to_ascii(hashbuf, ha2);
   convert_to_ascii(hashbuf, ha2);
 
 
   if(digest->qop) {
   if(digest->qop) {
@@ -801,8 +824,10 @@ static CURLcode auth_create_digest_http_message(
   if(!hashthis)
   if(!hashthis)
     return CURLE_OUT_OF_MEMORY;
     return CURLE_OUT_OF_MEMORY;
 
 
-  hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
+  result = hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
   free(hashthis);
   free(hashthis);
+  if(result)
+    return result;
   convert_to_ascii(hashbuf, request_digest);
   convert_to_ascii(hashbuf, request_digest);
 
 
   /* For test case 64 (snooped from a Mozilla 1.3a request)
   /* 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,
                                            outptr, outlen,
                                            auth_digest_md5_to_ascii,
                                            auth_digest_md5_to_ascii,
                                            Curl_md5it);
                                            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;
 }
 }
 
 
 /*
 /*

+ 6 - 0
lib/version.c

@@ -212,9 +212,15 @@ char *curl_version(void)
 
 
 #ifdef USE_LIBPSL
 #ifdef USE_LIBPSL
   {
   {
+#if defined(PSL_VERSION_MAJOR) && (PSL_VERSION_MAJOR > 0 ||     \
+                                   PSL_VERSION_MINOR >= 11)
     int num = psl_check_version_number(0);
     int num = psl_check_version_number(0);
     msnprintf(psl_version, sizeof(psl_version), "libpsl/%d.%d.%d",
     msnprintf(psl_version, sizeof(psl_version), "libpsl/%d.%d.%d",
               num >> 16, (num >> 8) & 0xff, num & 0xff);
               num >> 16, (num >> 8) & 0xff, num & 0xff);
+#else
+    msnprintf(psl_version, sizeof(psl_version), "libpsl/%s",
+              psl_get_version());
+#endif
     src[i++] = psl_version;
     src[i++] = psl_version;
   }
   }
 #endif
 #endif

+ 0 - 21
lib/vquic/curl_msh3.c

@@ -722,23 +722,6 @@ static bool cf_msh3_data_pending(struct Curl_cfilter *cf,
   return pending;
   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,
 static CURLcode h3_data_pause(struct Curl_cfilter *cf,
                               struct Curl_easy *data,
                               struct Curl_easy *data,
                               bool pause)
                               bool pause)
@@ -785,10 +768,6 @@ static CURLcode cf_msh3_data_event(struct Curl_cfilter *cf,
       }
       }
     }
     }
     break;
     break;
-  case CF_CTRL_CONN_INFO_UPDATE:
-    CURL_TRC_CF(data, cf, "req: update info");
-    cf_msh3_active(cf, data);
-    break;
   default:
   default:
     break;
     break;
   }
   }

+ 42 - 137
lib/vquic/curl_ngtcp2.c

@@ -58,6 +58,7 @@
 #include "http1.h"
 #include "http1.h"
 #include "select.h"
 #include "select.h"
 #include "inet_pton.h"
 #include "inet_pton.h"
+#include "transfer.h"
 #include "vquic.h"
 #include "vquic.h"
 #include "vquic_int.h"
 #include "vquic_int.h"
 #include "vquic-tls.h"
 #include "vquic-tls.h"
@@ -145,11 +146,9 @@ struct cf_ngtcp2_ctx {
 struct h3_stream_ctx {
 struct h3_stream_ctx {
   int64_t id; /* HTTP/3 protocol identifier */
   int64_t id; /* HTTP/3 protocol identifier */
   struct bufq sendbuf;   /* h3 request body */
   struct bufq sendbuf;   /* h3 request body */
-  struct bufq recvbuf;   /* h3 response body */
   struct h1_req_parser h1; /* h1 request parsing */
   struct h1_req_parser h1; /* h1 request parsing */
   size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */
   size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */
   size_t upload_blocked_len; /* the amount written last and EGAINed */
   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 */
   uint64_t error3; /* HTTP/3 stream error code */
   curl_off_t upload_left; /* number of request bytes left to upload */
   curl_off_t upload_left; /* number of request bytes left to upload */
   int status_code; /* HTTP status code */
   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,
   Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp,
                   H3_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE);
                   H3_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE);
   stream->sendbuf_len_in_flight = 0;
   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);
   Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
 
 
   H3_STREAM_LCTX(data) = stream;
   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->sendbuf);
-    Curl_bufq_free(&stream->recvbuf);
     Curl_h1_req_parse_free(&stream->h1);
     Curl_h1_req_parse_free(&stream->h1);
     free(stream);
     free(stream);
     H3_STREAM_LCTX(data) = NULL;
     H3_STREAM_LCTX(data) = NULL;
@@ -387,36 +380,6 @@ static int cb_handshake_completed(ngtcp2_conn *tconn, void *user_data)
   return 0;
   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,
 static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags,
                                int64_t stream_id, uint64_t offset,
                                int64_t stream_id, uint64_t offset,
                                const uint8_t *buf, size_t buflen,
                                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;
   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,
 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)
                            void *user_data, void *stream_user_data)
 {
 {
   struct Curl_cfilter *cf = user_data;
   struct Curl_cfilter *cf = user_data;
+  struct cf_ngtcp2_ctx *ctx = cf->ctx;
   struct Curl_easy *data = stream_user_data;
   struct Curl_easy *data = stream_user_data;
   struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
   struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
   CURLcode result;
   CURLcode result;
@@ -846,14 +781,19 @@ static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id,
   if(!stream)
   if(!stream)
     return NGHTTP3_ERR_CALLBACK_FAILURE;
     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) {
   if(result) {
     CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, ERROR receiving %d",
     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;
     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;
   return 0;
 }
 }
 
 
@@ -888,7 +828,7 @@ static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id,
   if(!stream)
   if(!stream)
     return 0;
     return 0;
   /* add a CRLF only if we've received some headers */
   /* 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) {
   if(result) {
     return -1;
     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",
     ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n",
                       stream->status_code);
                       stream->status_code);
     CURL_TRC_CF(data, cf, "[%" PRId64 "] status: %s", stream_id, line);
     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) {
     if(result) {
       return -1;
       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",
     CURL_TRC_CF(data, cf, "[%" PRId64 "] header: %.*s: %.*s",
                 stream_id, (int)h3name.len, h3name.base,
                 stream_id, (int)h3name.len, h3name.base,
                 (int)h3val.len, h3val.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) {
     if(result) {
       return -1;
       return -1;
     }
     }
-    result = write_resp_raw(cf, data, ": ", 2, FALSE);
+    result = write_resp_hds(data, ": ", 2);
     if(result) {
     if(result) {
       return -1;
       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) {
     if(result) {
       return -1;
       return -1;
     }
     }
-    result = write_resp_raw(cf, data, "\r\n", 2, FALSE);
+    result = write_resp_hds(data, "\r\n", 2);
     if(result) {
     if(result) {
       return -1;
       return -1;
     }
     }
@@ -1092,7 +1032,7 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf,
   if(stream->reset) {
   if(stream->reset) {
     failf(data,
     failf(data,
           "HTTP/3 stream %" PRId64 " reset by server", stream->id);
           "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;
     goto out;
   }
   }
   else if(!stream->resp_hds_complete) {
   else if(!stream->resp_hds_complete) {
@@ -1112,7 +1052,7 @@ out:
 
 
 /* incoming data frames on the h3 stream */
 /* incoming data frames on the h3 stream */
 static ssize_t cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
 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 cf_ngtcp2_ctx *ctx = cf->ctx;
   struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
   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;
   struct pkt_io_ctx pktx;
 
 
   (void)ctx;
   (void)ctx;
+  (void)buf;
 
 
   CF_DATA_SAVE(save, cf, data);
   CF_DATA_SAVE(save, cf, data);
   DEBUGASSERT(cf->connected);
   DEBUGASSERT(cf->connected);
@@ -1136,46 +1077,18 @@ static ssize_t cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
     goto out;
     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)) {
   if(cf_progress_ingress(cf, data, &pktx)) {
     *err = CURLE_RECV_ERROR;
     *err = CURLE_RECV_ERROR;
     nread = -1;
     nread = -1;
     goto out;
     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:
 out:
   if(cf_progress_egress(cf, data, &pktx)) {
   if(cf_progress_egress(cf, data, &pktx)) {
@@ -1189,8 +1102,8 @@ out:
       nread = -1;
       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);
   CF_DATA_RESTORE(cf, save);
   return nread;
   return nread;
 }
 }
@@ -1593,7 +1506,6 @@ static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
   struct cf_ngtcp2_ctx *ctx = cf->ctx;
   struct cf_ngtcp2_ctx *ctx = cf->ctx;
   struct pkt_io_ctx local_pktx;
   struct pkt_io_ctx local_pktx;
   size_t pkts_chunk = 128, i;
   size_t pkts_chunk = 128, i;
-  size_t pkts_max = 10 * pkts_chunk;
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
 
 
   if(!pktx) {
   if(!pktx) {
@@ -1608,17 +1520,13 @@ static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
   if(result)
   if(result)
     return 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;
     pktx->pkt_count = 0;
     result = vquic_recv_packets(cf, data, &ctx->q, pkts_chunk,
     result = vquic_recv_packets(cf, data, &ctx->q, pkts_chunk,
                                 recv_pkt, pktx);
                                 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;
       break;
   }
   }
   return result;
   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
   /* 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
    * To detect if this minimum payload can be increased, ngtcp2 sends
    * now and then a packet payload larger than the minimum. It that
    * now and then a packet payload larger than the minimum. It that
    * is ACKed by the peer, both parties know that it works and
    * 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,
 static bool cf_ngtcp2_data_pending(struct Curl_cfilter *cf,
                                    const struct Curl_easy *data)
                                    const struct Curl_easy *data)
 {
 {
-  const struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
   (void)cf;
   (void)cf;
-  return stream && !Curl_bufq_is_empty(&stream->recvbuf);
+  (void)data;
+  return FALSE;
 }
 }
 
 
 static CURLcode h3_data_pause(struct Curl_cfilter *cf,
 static CURLcode h3_data_pause(struct Curl_cfilter *cf,
@@ -2070,8 +1978,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
   if(result)
   if(result)
     return 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)
   if(!sockaddr)
     return CURLE_QUIC_CONNECT_ERROR;
     return CURLE_QUIC_CONNECT_ERROR;
   ctx->q.local_addrlen = sizeof(ctx->q.local_addr);
   ctx->q.local_addrlen = sizeof(ctx->q.local_addr);
@@ -2186,13 +2093,11 @@ out:
 
 
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
   if(result) {
   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",
     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
 #endif
   if(!result && ctx->qconn) {
   if(!result && ctx->qconn) {

+ 91 - 33
lib/vquic/curl_osslq.c

@@ -67,7 +67,7 @@
  * Chunk size is large enough to take a full DATA frame */
  * Chunk size is large enough to take a full DATA frame */
 #define H3_STREAM_WINDOW_SIZE (128 * 1024)
 #define H3_STREAM_WINDOW_SIZE (128 * 1024)
 #define H3_STREAM_CHUNK_SIZE   (16 * 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.
  * seems good. More does not seem to improve performance.
  * The benefit of the pool is that stream buffer to not keep
  * The benefit of the pool is that stream buffer to not keep
  * spares. So memory consumption goes down when streams run empty,
  * 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,
 static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
                                     struct Curl_easy *data);
                                     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) {
   switch(err) {
   case SSL_ERROR_NONE:
   case SSL_ERROR_NONE:
@@ -139,7 +139,7 @@ static const char *SSL_ERROR_to_str(int err)
 }
 }
 
 
 /* Return error string for last OpenSSL error */
 /* 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);
   DEBUGASSERT(size);
   *buf = '\0';
   *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,
 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;
   struct cf_osslq_ctx *ctx = cf->ctx;
   CURLcode result = def_result;
   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
     /* If client certificate is required, communicate the
        error to client */
        error to client */
     result = CURLE_SSL_CLIENTCERT;
     result = CURLE_SSL_CLIENTCERT;
-    ossl_strerror(errdetail, ebuf, sizeof(ebuf));
+    osslq_strerror(errdetail, ebuf, sizeof(ebuf));
   }
   }
 #endif
 #endif
   else if((lib == ERR_LIB_SSL) && (reason == SSL_R_PROTOCOL_IS_SHUTDOWN)) {
   else if((lib == ERR_LIB_SSL) && (reason == SSL_R_PROTOCOL_IS_SHUTDOWN)) {
     ctx->protocol_shutdown = TRUE;
     ctx->protocol_shutdown = TRUE;
-    err_descr = "QUIC connectin has been shut down";
+    err_descr = "QUIC connection has been shut down";
     result = def_result;
     result = def_result;
   }
   }
   else {
   else {
     result = def_result;
     result = def_result;
-    ossl_strerror(errdetail, ebuf, sizeof(ebuf));
+    osslq_strerror(errdetail, ebuf, sizeof(ebuf));
   }
   }
 
 
   /* detail is already set to the SSL error above */
   /* 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) {
   if(CURLE_SSL_CONNECT_ERROR == result && errdetail == 0) {
     char extramsg[80]="";
     char extramsg[80]="";
     int sockerr = SOCKERRNO;
     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)
     if(sockerr && detail == SSL_ERROR_SYSCALL)
       Curl_strerror(sockerr, extramsg, sizeof(extramsg));
       Curl_strerror(sockerr, extramsg, sizeof(extramsg));
     failf(data, "QUIC connect: %s in connection to %s:%d (%s)",
     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 {
   else {
     /* Could be a CERT problem */
     /* 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,
 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;
   struct cf_osslq_h3conn *h3 = &ctx->h3;
   CURLcode result;
   CURLcode result;
@@ -1039,7 +1037,6 @@ static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf,
   CURLcode result;
   CURLcode result;
   int rv;
   int rv;
   const struct Curl_sockaddr_ex *peer_addr = NULL;
   const struct Curl_sockaddr_ex *peer_addr = NULL;
-  int peer_port;
   BIO *bio = NULL;
   BIO *bio = NULL;
   BIO_ADDR *baddr = NULL;
   BIO_ADDR *baddr = NULL;
 
 
@@ -1061,8 +1058,7 @@ static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf,
     goto out;
     goto out;
 
 
   result = CURLE_QUIC_CONNECT_ERROR;
   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)
   if(!peer_addr)
     goto out;
     goto out;
 
 
@@ -1078,7 +1074,20 @@ static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf,
     goto out;
     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);
   bio = BIO_new_dgram(ctx->q.sockfd, BIO_NOCLOSE);
+#endif
   if(!bio) {
   if(!bio) {
     result = CURLE_OUT_OF_MEMORY;
     result = CURLE_OUT_OF_MEMORY;
     goto out;
     goto out;
@@ -1095,6 +1104,16 @@ static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf,
     goto out;
     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);
   SSL_set_bio(ctx->tls.ssl, bio, bio);
   bio = NULL;
   bio = NULL;
   SSL_set_connect_state(ctx->tls.ssl);
   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);
       SSL_get_stream_read_error_code(x->s->ssl, &app_error_code);
       CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] h3_quic_recv -> RESET, "
       CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] h3_quic_recv -> RESET, "
                   "rv=%d, app_err=%" PRIu64,
                   "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) {
       if(app_error_code != NGHTTP3_H3_NO_ERROR) {
         x->s->reset = TRUE;
         x->s->reset = TRUE;
       }
       }
@@ -1361,7 +1380,7 @@ static CURLcode h3_send_streams(struct Curl_cfilter *cf,
     size_t written;
     size_t written;
     int eos, ok, rv;
     int eos, ok, rv;
     size_t total_len, acked_len = 0;
     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,
     n = nghttp3_conn_writev_stream(ctx->h3.conn, &stream_id, &eos,
                                    vec, ARRAYSIZE(vec));
                                    vec, ARRAYSIZE(vec));
@@ -1392,9 +1411,19 @@ static CURLcode h3_send_streams(struct Curl_cfilter *cf,
     for(i = 0; (i < n) && !blocked; ++i) {
     for(i = 0; (i < n) && !blocked; ++i) {
       /* Without stream->s.ssl, we closed that already, so
       /* Without stream->s.ssl, we closed that already, so
        * pretend the write did succeed. */
        * 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;
       written = vec[i].len;
       ok = !s->ssl || SSL_write_ex(s->ssl, vec[i].base, vec[i].len,
       ok = !s->ssl || SSL_write_ex(s->ssl, vec[i].base, vec[i].len,
                                    &written);
                                    &written);
+#endif
       if(ok) {
       if(ok) {
         /* As OpenSSL buffers the data, we count this as acknowledged
         /* As OpenSSL buffers the data, we count this as acknowledged
          * from nghttp3's point of view */
          * from nghttp3's point of view */
@@ -1409,7 +1438,7 @@ static CURLcode h3_send_streams(struct Curl_cfilter *cf,
         case SSL_ERROR_WANT_READ:
         case SSL_ERROR_WANT_READ:
           /* QUIC blocked us from writing more */
           /* QUIC blocked us from writing more */
           CURL_TRC_CF(data, cf, "[%"PRId64"] send %zu bytes to QUIC blocked",
           CURL_TRC_CF(data, cf, "[%"PRId64"] send %zu bytes to QUIC blocked",
-                s->id, vec[i].len);
+                      s->id, vec[i].len);
           written = 0;
           written = 0;
           nghttp3_conn_block_stream(ctx->h3.conn, s->id);
           nghttp3_conn_block_stream(ctx->h3.conn, s->id);
           s->send_blocked = blocked = TRUE;
           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)) {
     if(acked_len > 0 || (eos && !s->send_blocked)) {
       /* Since QUIC buffers the data written internally, we can tell
       /* Since QUIC buffers the data written internally, we can tell
        * nghttp3 that it can move forward on it */
        * 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);
       rv = nghttp3_conn_add_write_offset(ctx->h3.conn, s->id, acked_len);
       if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
       if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
         failf(data, "nghttp3_conn_add_write_offset returned error: %s\n",
         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);
                   "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 */
       /* wrote everything and H3 indicates end of stream */
       CURL_TRC_CF(data, cf, "[%" PRId64 "] closing QUIC stream", s->id);
       CURL_TRC_CF(data, cf, "[%" PRId64 "] closing QUIC stream", s->id);
       SSL_stream_conclude(s->ssl, 0);
       SSL_stream_conclude(s->ssl, 0);
@@ -1569,6 +1599,7 @@ static CURLcode cf_osslq_connect(struct Curl_cfilter *cf,
   if(err == 1) {
   if(err == 1) {
     /* connected */
     /* connected */
     ctx->handshake_at = now;
     ctx->handshake_at = now;
+    ctx->q.last_io = now;
     CURL_TRC_CF(data, cf, "handshake complete after %dms",
     CURL_TRC_CF(data, cf, "handshake complete after %dms",
                (int)Curl_timediff(now, ctx->started_at));
                (int)Curl_timediff(now, ctx->started_at));
     result = cf_osslq_verify_peer(cf, data);
     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);
     int detail = SSL_get_error(ctx->tls.ssl, err);
     switch(detail) {
     switch(detail) {
     case SSL_ERROR_WANT_READ:
     case SSL_ERROR_WANT_READ:
+      ctx->q.last_io = now;
       CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_RECV");
       CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_RECV");
       result = Curl_vquic_tls_before_recv(&ctx->tls, cf, data);
       result = Curl_vquic_tls_before_recv(&ctx->tls, cf, data);
       goto out;
       goto out;
     case SSL_ERROR_WANT_WRITE:
     case SSL_ERROR_WANT_WRITE:
+      ctx->q.last_io = now;
       CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_SEND");
       CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_SEND");
       result = CURLE_OK;
       result = CURLE_OK;
       goto out;
       goto out;
 #ifdef SSL_ERROR_WANT_ASYNC
 #ifdef SSL_ERROR_WANT_ASYNC
     case SSL_ERROR_WANT_ASYNC:
     case SSL_ERROR_WANT_ASYNC:
+      ctx->q.last_io = now;
       CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_ASYNC");
       CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_ASYNC");
       result = CURLE_OK;
       result = CURLE_OK;
       goto out;
       goto out;
@@ -1619,13 +1653,11 @@ out:
 
 
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
   if(result) {
   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",
     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
 #endif
   if(!result)
   if(!result)
@@ -1885,7 +1917,7 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf,
   if(stream->reset) {
   if(stream->reset) {
     failf(data,
     failf(data,
           "HTTP/3 stream %" PRId64 " reset by server", stream->s.id);
           "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;
     goto out;
   }
   }
   else if(!stream->resp_hds_complete) {
   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)
   if(!ctx->tls.ssl)
     goto out;
     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))
   if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending))
     goto out;
     goto out;
@@ -2111,15 +2160,24 @@ static CURLcode cf_osslq_query(struct Curl_cfilter *cf,
                                int query, int *pres1, void *pres2)
                                int query, int *pres1, void *pres2)
 {
 {
   struct cf_osslq_ctx *ctx = cf->ctx;
   struct cf_osslq_ctx *ctx = cf->ctx;
-  struct cf_call_data save;
 
 
   switch(query) {
   switch(query) {
   case CF_QUERY_MAX_CONCURRENT: {
   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;
     *pres1 = 100;
+#endif
     CURL_TRC_CF(data, cf, "query max_conncurrent -> %d", *pres1);
     CURL_TRC_CF(data, cf, "query max_conncurrent -> %d", *pres1);
-    CF_DATA_RESTORE(cf, save);
     return CURLE_OK;
     return CURLE_OK;
   }
   }
   case CF_QUERY_CONNECT_REPLY_MS:
   case CF_QUERY_CONNECT_REPLY_MS:

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini