]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
lib: further send/upload handling polish
authorStefan Eissing <stefan@eissing.org>
Wed, 28 Feb 2024 13:51:53 +0000 (14:51 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 4 Mar 2024 07:42:56 +0000 (08:42 +0100)
- Move all the "upload_done" handling to request.c

  - add possibility to abort sending of a request
  - add `Curl_req_done_sending()` for checks
  - transfer.c: readwrite_upload() now clean

- removing data->state.ulbuf and data->req.upload_fromhere

  - as well as data->req.upload_present
  - set data->req.upload_done on having read all from
    the client and completely flushed the send buffer

- tftp, remove setting of data->req.upload_fromhere

  - serves no purpose as `upload_present` is not set
    and the data itself is directly `sendto()` anyway

- smtp, make upload EOB conversion a client reader
- xfer_ulbuf addition

  - add xfer_ulbuf for borrowing, similar to xfer_buf
  - use in file upload
  - use in c-hyper body sending

- h1-proxy, remove init of data->state.uilbuf that is never used
- smb, add own send_buf instead of using data->state.ulbuf

Closes #13010

22 files changed:
lib/c-hyper.c
lib/cf-h1-proxy.c
lib/file.c
lib/http.c
lib/http.h
lib/multi.c
lib/multihandle.h
lib/multiif.h
lib/request.c
lib/request.h
lib/rtsp.c
lib/sendf.c
lib/setopt.c
lib/smb.c
lib/smb.h
lib/smtp.c
lib/smtp.h
lib/tftp.c
lib/transfer.c
lib/transfer.h
lib/url.c
lib/urldata.h

index d12d6de153de3c8f7af2d252418a2b113d3d8fcf..6bac1a357329ef89e5211fa3f8afacdeeb797425 100644 (file)
@@ -469,7 +469,7 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data,
       infof(data, "Got 417 while waiting for a 100");
       data->state.disableexpect = TRUE;
       data->req.newurl = strdup(data->state.url);
-      Curl_done_sending(data, k);
+      Curl_req_abort_sending(data);
     }
 
     result = status_line(data, conn,
@@ -663,7 +663,10 @@ static int uploadstreamed(void *userdata, hyper_context *ctx,
   size_t fillcount;
   struct Curl_easy *data = (struct Curl_easy *)userdata;
   CURLcode result;
+  char *xfer_ulbuf;
+  size_t xfer_ulblen;
   bool eos;
+  int rc = HYPER_POLL_ERROR;
   (void)ctx;
 
   if(data->req.exp100 > EXP100_SEND_DATA) {
@@ -677,39 +680,44 @@ static int uploadstreamed(void *userdata, hyper_context *ctx,
     return HYPER_POLL_PENDING;
   }
 
-  result = Curl_client_read(data, data->state.ulbuf,
-                            data->set.upload_buffer_size,
-                            &fillcount, &eos);
-  if(result) {
-    data->state.hresult = result;
-    return HYPER_POLL_ERROR;
-  }
+  result = Curl_multi_xfer_ulbuf_borrow(data, &xfer_ulbuf, &xfer_ulblen);
+  if(result)
+    goto out;
+
+  result = Curl_client_read(data, xfer_ulbuf, xfer_ulblen, &fillcount, &eos);
+  if(result)
+    goto out;
 
   if(fillcount) {
-    hyper_buf *copy = hyper_buf_copy((uint8_t *)data->state.ulbuf, fillcount);
+    hyper_buf *copy = hyper_buf_copy((uint8_t *)xfer_ulbuf, fillcount);
     if(copy)
       *chunk = copy;
     else {
-      data->state.hresult = CURLE_OUT_OF_MEMORY;
-      return HYPER_POLL_ERROR;
+      result = CURLE_OUT_OF_MEMORY;
+      goto out;
     }
     /* increasing the writebytecount here is a little premature but we
        don't know exactly when the body is sent */
     data->req.writebytecount += fillcount;
     Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
-    return HYPER_POLL_READY;
+    rc = HYPER_POLL_READY;
   }
   else if(eos) {
     *chunk = NULL;
-    return HYPER_POLL_READY;
+    rc = HYPER_POLL_READY;
   }
   else {
     /* paused, save a waker */
     if(data->hyp.send_body_waker)
       hyper_waker_free(data->hyp.send_body_waker);
     data->hyp.send_body_waker = hyper_context_waker(ctx);
-    return HYPER_POLL_PENDING;
+    rc = HYPER_POLL_PENDING;
   }
+
+out:
+  Curl_multi_xfer_ulbuf_release(data, xfer_ulbuf);
+  data->state.hresult = result;
+  return rc;
 }
 
 /*
@@ -722,7 +730,6 @@ static CURLcode bodysend(struct Curl_easy *data,
                          hyper_request *hyperreq,
                          Curl_HttpReq httpreq)
 {
-  struct HTTP *http = data->req.p.http;
   CURLcode result = CURLE_OK;
   struct dynbuf req;
   if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD))
@@ -731,21 +738,21 @@ static CURLcode bodysend(struct Curl_easy *data,
     hyper_body *body;
     Curl_dyn_init(&req, DYN_HTTP_REQUEST);
     result = Curl_http_req_complete(data, &req, httpreq);
+    if(result)
+      return result;
 
-    if(!result)
+    /* if the "complete" above did produce more than the closing line,
+       parse the added headers */
+    if(Curl_dyn_len(&req) != 2 || strcmp(Curl_dyn_ptr(&req), "\r\n")) {
       result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&req));
+      if(result)
+        return result;
+    }
 
     Curl_dyn_free(&req);
 
     body = hyper_body_new();
     hyper_body_set_userdata(body, data);
-    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);
 
     if(HYPERE_OK != hyper_request_set_body(hyperreq, body)) {
@@ -753,7 +760,6 @@ static CURLcode bodysend(struct Curl_easy *data,
       result = CURLE_OUT_OF_MEMORY;
     }
   }
-  http->sending = HTTPSEND_BODY;
   return result;
 }
 
index 280516061fc94a936fe017f994dcb56013c5b3ed..deb94580bfb1f08754982f6f8ebaa258eb600d90 100644 (file)
@@ -114,18 +114,12 @@ static CURLcode tunnel_init(struct Curl_cfilter *cf,
                             struct h1_tunnel_state **pts)
 {
   struct h1_tunnel_state *ts;
-  CURLcode result;
 
   if(cf->conn->handler->flags & PROTOPT_NOTCPPROXY) {
     failf(data, "%s cannot be done over CONNECT", cf->conn->handler->scheme);
     return CURLE_UNSUPPORTED_PROTOCOL;
   }
 
-  /* we might need the upload buffer for streaming a partial request */
-  result = Curl_get_upload_buffer(data);
-  if(result)
-    return result;
-
   ts = calloc(1, sizeof(*ts));
   if(!ts)
     return CURLE_OUT_OF_MEMORY;
index d3b08a81f0e33996999110a39b2efac4caa7ac86..07d879dfd128f0c9e88ab67886fceea7c4287e31 100644 (file)
@@ -291,8 +291,8 @@ static CURLcode file_upload(struct Curl_easy *data)
   int fd;
   int mode;
   CURLcode result = CURLE_OK;
-  char *xfer_buf;
-  size_t xfer_blen;
+  char *xfer_ulbuf;
+  size_t xfer_ulblen;
   curl_off_t bytecount = 0;
   struct_stat file_stat;
   const char *sendbuf;
@@ -340,7 +340,7 @@ static CURLcode file_upload(struct Curl_easy *data)
     data->state.resume_from = (curl_off_t)file_stat.st_size;
   }
 
-  result = Curl_multi_xfer_buf_borrow(data, &xfer_buf, &xfer_blen);
+  result = Curl_multi_xfer_ulbuf_borrow(data, &xfer_ulbuf, &xfer_ulblen);
   if(result)
     goto out;
 
@@ -349,7 +349,7 @@ static CURLcode file_upload(struct Curl_easy *data)
     ssize_t nwrite;
     size_t readcount;
 
-    result = Curl_client_read(data, xfer_buf, xfer_blen, &readcount, &eos);
+    result = Curl_client_read(data, xfer_ulbuf, xfer_ulblen, &readcount, &eos);
     if(result)
       break;
 
@@ -363,16 +363,16 @@ static CURLcode file_upload(struct Curl_easy *data)
       if((curl_off_t)nread <= data->state.resume_from) {
         data->state.resume_from -= nread;
         nread = 0;
-        sendbuf = xfer_buf;
+        sendbuf = xfer_ulbuf;
       }
       else {
-        sendbuf = xfer_buf + data->state.resume_from;
+        sendbuf = xfer_ulbuf + data->state.resume_from;
         nread -= (size_t)data->state.resume_from;
         data->state.resume_from = 0;
       }
     }
     else
-      sendbuf = xfer_buf;
+      sendbuf = xfer_ulbuf;
 
     /* write the data to the target */
     nwrite = write(fd, sendbuf, nread);
@@ -395,7 +395,7 @@ static CURLcode file_upload(struct Curl_easy *data)
 
 out:
   close(fd);
-  Curl_multi_xfer_buf_release(data, xfer_buf);
+  Curl_multi_xfer_ulbuf_release(data, xfer_ulbuf);
 
   return result;
 }
index 3f17f7cd92fc141bf37df50699cd5a6deca049c7..65febe77f7ab55e6b2ace0e6730a97d07b9a3a4b 100644 (file)
@@ -1283,7 +1283,6 @@ CURLcode Curl_http_done(struct Curl_easy *data,
   if(!http)
     return CURLE_OK;
 
-  Curl_dyn_free(&http->send_buffer);
   Curl_dyn_reset(&data->state.headerb);
   Curl_hyper_done(data);
   Curl_ws_done(data);
@@ -2151,6 +2150,12 @@ CURLcode Curl_http_req_complete(struct Curl_easy *data,
   CURLcode result = CURLE_OK;
   struct HTTP *http = data->req.p.http;
 
+  if(data->req.upload_chunky) {
+    result = Curl_httpchunk_add_reader(data);
+    if(result)
+      return result;
+  }
+
   DEBUGASSERT(data->conn);
   switch(httpreq) {
   case HTTPREQ_PUT: /* Let's PUT the data to the server! */
@@ -2252,7 +2257,6 @@ CURLcode Curl_http_req_complete(struct Curl_easy *data,
       data->state.in = (void *) data->state.mimepost;
       result = Client_reader_set_fread(data, data->state.infilesize);
     }
-    http->sending = HTTPSEND_BODY;
     break;
 #endif
   case HTTPREQ_POST:
@@ -2294,19 +2298,21 @@ CURLcode Curl_http_req_complete(struct Curl_easy *data,
       goto out;
 
     if(!http->postsize) {
-      Curl_pgrsSetUploadSize(data, -1);
+      Curl_pgrsSetUploadSize(data, 0);
       result = Client_reader_set_null(data);
     }
-    else if(data->set.postfields) {  /* we have the bytes */
+    else if(data->set.postfields) {
       Curl_pgrsSetUploadSize(data, http->postsize);
-      result = Client_reader_set_buf(data, data->set.postfields,
-                                     (size_t)http->postsize);
+      if(http->postsize > 0)
+        result = Client_reader_set_buf(data, data->set.postfields,
+                                       (size_t)http->postsize);
+      else
+        result = Client_reader_set_null(data);
     }
     else { /* we read the bytes from the callback */
       Curl_pgrsSetUploadSize(data, http->postsize);
       result = Client_reader_set_fread(data, http->postsize);
     }
-    http->sending = HTTPSEND_BODY;
     break;
 
   default:
@@ -2647,7 +2653,6 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
 {
   struct connectdata *conn = data->conn;
   CURLcode result = CURLE_OK;
-  struct HTTP *http;
   Curl_HttpReq httpreq;
   const char *te = ""; /* transfer-encoding */
   const char *request;
@@ -2699,9 +2704,6 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
   if(result)
     goto fail;
 
-  http = data->req.p.http;
-  DEBUGASSERT(http);
-
   result = Curl_http_host(data, conn);
   if(result)
     goto fail;
@@ -2880,7 +2882,6 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
     result = Curl_add_custom_headers(data, FALSE, &req);
 
   if(!result) {
-    http->postdata = NULL;  /* nothing to post at this point */
     if((httpreq == HTTPREQ_GET) ||
        (httpreq == HTTPREQ_HEAD))
       Curl_pgrsSetUploadSize(data, 0); /* nothing */
@@ -2896,29 +2897,6 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
   if(result)
     goto fail;
 
-  if(data->req.writebytecount) {
-    /* if a request-body has been sent off, we make sure this progress is noted
-       properly */
-    Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
-    if(Curl_pgrsUpdate(data))
-      result = CURLE_ABORTED_BY_CALLBACK;
-
-    if(!http->postsize) {
-      /* already sent the entire request body, mark the "upload" as
-         complete */
-      infof(data, "upload completely sent off: %" CURL_FORMAT_CURL_OFF_T
-            " out of %" CURL_FORMAT_CURL_OFF_T " bytes",
-            data->req.writebytecount, http->postsize);
-      data->req.upload_done = TRUE;
-      data->req.keepon &= ~KEEP_SEND; /* we're done writing */
-      data->req.exp100 = EXP100_SEND_DATA; /* already sent */
-      Curl_expire_done(data, EXPIRE_100_TIMEOUT);
-    }
-  }
-
-  if(data->req.upload_done)
-    Curl_conn_ev_data_done_send(data);
-
   if((conn->httpversion >= 20) && data->req.upload_chunky)
     /* upload_chunky was set above to set up the request in a chunky fashion,
        but is disabled here again to avoid that the chunked encoded version is
@@ -3782,7 +3760,7 @@ static CURLcode http_rw_headers(struct Curl_easy *data,
              * connection for closure after we've read the entire response.
              */
             Curl_expire_done(data, EXPIRE_100_TIMEOUT);
-            if(!k->upload_done) {
+            if(!Curl_req_done_sending(data)) {
               if((k->httpcode == 417) && data->state.expect100header) {
                 /* 417 Expectation Failed - try again without the Expect
                    header */
@@ -3801,7 +3779,7 @@ static CURLcode http_rw_headers(struct Curl_easy *data,
                 data->state.disableexpect = TRUE;
                 DEBUGASSERT(!data->req.newurl);
                 data->req.newurl = strdup(data->state.url);
-                Curl_done_sending(data, k);
+                Curl_req_abort_sending(data);
               }
               else if(data->set.http_keep_sending_on_error) {
                 infof(data, "HTTP error before end of send, keep sending");
@@ -3813,10 +3791,9 @@ static CURLcode http_rw_headers(struct Curl_easy *data,
               else {
                 infof(data, "HTTP error before end of send, stop sending");
                 streamclose(conn, "Stop sending data before everything sent");
-                result = Curl_done_sending(data, k);
+                result = Curl_req_abort_sending(data);
                 if(result)
                   return result;
-                k->upload_done = TRUE;
                 if(data->state.expect100header)
                   k->exp100 = EXP100_FAILED;
               }
@@ -3828,8 +3805,7 @@ static CURLcode http_rw_headers(struct Curl_easy *data,
           }
         }
 
-        if(data->state.rewindbeforesend &&
-           (conn->writesockfd != CURL_SOCKET_BAD)) {
+        if(data->state.rewindbeforesend && !Curl_req_done_sending(data)) {
           /* We rewind before next send, continue sending now */
           infof(data, "Keep sending data to get tossed away");
           k->keepon |= KEEP_SEND;
index b7fb877e6334f78ce27da795ccbfc363a7ee2114..fc058477d5f6b30441a917619ddad528af845216 100644 (file)
@@ -189,27 +189,10 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data);
  ***************************************************************************/
 struct HTTP {
   curl_off_t postsize; /* off_t to handle large file sizes */
-  const char *postdata;
-  struct back {
-    curl_read_callback fread_func; /* backup storage for fread pointer */
-    void *fread_in;           /* backup storage for fread_in pointer */
-    const char *postdata;
-    curl_off_t postsize;
-    struct Curl_easy *data;
-  } backup;
-
-  enum {
-    HTTPSEND_NADA,    /* init */
-    HTTPSEND_REQUEST, /* sending a request */
-    HTTPSEND_BODY     /* sending body */
-  } sending;
 
 #ifndef CURL_DISABLE_HTTP
   void *h2_ctx;              /* HTTP/2 implementation context */
   void *h3_ctx;              /* HTTP/3 implementation context */
-  struct dynbuf send_buffer; /* used if the request couldn't be sent in one
-                                chunk, points to an allocated send_buffer
-                                struct */
 #endif
 };
 
index e0e871fe679c06139f566f9fff41674ef0d8d287..3770ac607b57a2ad321edab93fc255b88011b349 100644 (file)
@@ -94,7 +94,7 @@ static CURLMcode add_next_timeout(struct curltime now,
 static CURLMcode multi_timeout(struct Curl_multi *multi,
                                long *timeout_ms);
 static void process_pending_handles(struct Curl_multi *multi);
-static void multi_xfer_buf_free(struct Curl_multi *multi);
+static void multi_xfer_bufs_free(struct Curl_multi *multi);
 
 #ifdef DEBUGBUILD
 static const char * const multi_statename[]={
@@ -192,7 +192,7 @@ static void mstate(struct Curl_easy *data, CURLMstate state
     data->multi->num_alive--;
     if(!data->multi->num_alive) {
       /* free the transfer buffer when we have no more active transfers */
-      multi_xfer_buf_free(data->multi);
+      multi_xfer_bufs_free(data->multi);
     }
   }
 
@@ -716,8 +716,6 @@ static CURLcode multi_done(struct Curl_easy *data,
   if(!result)
     result = Curl_req_done(&data->req, data, premature);
 
-  Curl_safefree(data->state.ulbuf);
-
   CONNCACHE_LOCK(data);
   Curl_detach_connection(data);
   if(CONN_INUSE(conn)) {
@@ -2900,7 +2898,7 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi)
     Curl_free_multi_ssl_backend_data(multi->ssl_backend_data);
 #endif
 
-    multi_xfer_buf_free(multi);
+    multi_xfer_bufs_free(multi);
     free(multi);
 
     return CURLM_OK;
@@ -3891,10 +3889,66 @@ void Curl_multi_xfer_buf_release(struct Curl_easy *data, char *buf)
   data->multi->xfer_buf_borrowed = FALSE;
 }
 
-static void multi_xfer_buf_free(struct Curl_multi *multi)
+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;
 }
index 3cccd343fec488fd4c51f744da529d4d109ed81a..3156c83ad347fe92e8e2c0c79a93cf2bdbd0167d 100644 (file)
@@ -127,6 +127,9 @@ struct Curl_multi {
   /* buffer used for transfer data, lazy initialized */
   char *xfer_buf; /* the actual buffer */
   size_t xfer_buf_len;      /* the allocated length */
+  /* buffer used for upload data, lazy initialized */
+  char *xfer_ulbuf; /* the actual buffer */
+  size_t xfer_ulbuf_len;      /* the allocated length */
 
 #if defined(USE_SSL)
   struct multi_ssl_backend_data *ssl_backend_data;
@@ -176,6 +179,7 @@ struct Curl_multi {
   BIT(dead); /* a callback returned error, everything needs to crash and
                 burn */
   BIT(xfer_buf_borrowed);      /* xfer_buf is currently being borrowed */
+  BIT(xfer_ulbuf_borrowed);      /* xfer_buf is currently being borrowed */
 #ifdef DEBUGBUILD
   BIT(warned);                 /* true after user warned of DEBUGBUILD */
 #endif
index de2ffeb9f432fd1ee178db1515273770b85e3fb9..37f7a442623c3a8f7eb08fb9d86b88ba5ab06d15 100644 (file)
@@ -118,4 +118,29 @@ CURLcode Curl_multi_xfer_buf_borrow(struct Curl_easy *data,
  */
 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 */
index cd5170a3282cdf6d0355e355055f0f05df043a1a..dfd96a6c4910f8ada5026c54e34423337b6c1a93 100644 (file)
@@ -25,6 +25,7 @@
 #include "curl_setup.h"
 
 #include "urldata.h"
+#include "cfilters.h"
 #include "dynbuf.h"
 #include "doh.h"
 #include "multiif.h"
@@ -67,12 +68,14 @@ CURLcode Curl_req_start(struct SingleRequest *req,
   return CURLE_OK;
 }
 
+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)Curl_req_flush(data);
+    (void)req_flush(data);
   Curl_client_reset(data);
   return CURLE_OK;
 }
@@ -129,9 +132,9 @@ void Curl_req_free(struct SingleRequest *req, struct Curl_easy *data)
 #endif
 }
 
-static CURLcode req_send(struct Curl_easy *data,
-                         const char *buf, size_t blen,
-                         size_t hds_len, size_t *pnwritten)
+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;
 
@@ -180,7 +183,7 @@ static CURLcode req_send_buffer_flush(struct Curl_easy *data)
 
   while(Curl_bufq_peek(&data->req.sendbuf, &buf, &blen)) {
     size_t nwritten, hds_len = CURLMIN(data->req.sendbuf_hds_len, blen);
-    result = req_send(data, (const char *)buf, blen, hds_len, &nwritten);
+    result = xfer_send(data, (const char *)buf, blen, hds_len, &nwritten);
     if(result)
       break;
 
@@ -206,7 +209,33 @@ static CURLcode req_send_buffer_flush(struct Curl_easy *data)
   return result;
 }
 
-CURLcode Curl_req_flush(struct Curl_easy *data)
+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; /* we're done sending */
+
+  /* FIXME: http specific stuff, need to go somewhere else */
+  data->req.exp100 = EXP100_SEND_DATA;
+  Curl_expire_done(data, EXPIRE_100_TIMEOUT);
+
+  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, "We are completely uploaded and fine");
+
+  return Curl_xfer_send_close(data);
+}
+
+static CURLcode req_flush(struct Curl_easy *data)
 {
   CURLcode result;
 
@@ -221,9 +250,30 @@ CURLcode Curl_req_flush(struct Curl_easy *data)
       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,
@@ -242,22 +292,6 @@ static CURLcode req_send_buffer_add(struct Curl_easy *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;
-}
-
 CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *buf)
 {
   CURLcode result;
@@ -275,22 +309,50 @@ CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *buf)
   if(result)
     return result;
 
-  if((data->req.exp100 == EXP100_SEND_DATA) &&
-     !Curl_bufq_is_full(&data->req.sendbuf)) {
+  return Curl_req_send_more(data);
+}
+#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 and
+   * we are not in a "expect-100" situation. */
+  if(!data->req.eos_read && !Curl_bufq_is_full(&data->req.sendbuf) &&
+     (data->req.exp100 == EXP100_SEND_DATA)) {
     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_send_buffer_flush(data);
+  result = req_flush(data);
   if(result == CURLE_AGAIN)
     result = CURLE_OK;
   return result;
 }
-#endif /* !USE_HYPER */
 
-bool Curl_req_want_send(struct Curl_easy *data)
+CURLcode Curl_req_abort_sending(struct Curl_easy *data)
 {
-  return data->req.sendbuf_init && !Curl_bufq_is_empty(&data->req.sendbuf);
+  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;
 }
index e7af4a8e537f281e1e8bb78b3a5dfc962b233456..91973d5dbc70caa21577dfc8e064a27490bc7951 100644 (file)
@@ -100,16 +100,6 @@ struct SingleRequest {
   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. */
-  size_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 {
@@ -139,8 +129,9 @@ struct SingleRequest {
   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(upload_done);   /* set to TRUE when doing chunked transfer-encoding
-                         upload and we're uploading the last chunk */
+  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 */
@@ -206,15 +197,26 @@ CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *buf);
 #endif /* !USE_HYPER */
 
 /**
- * Flush all buffered request bytes.
- * @return CURLE_OK on success, CURLE_AGAIN if sending was blocked,
- *         or the error on the sending.
+ * 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_flush(struct Curl_easy *data);
+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 */
index a0da9f138af8a5c4c9a9d7190e060f05f6f0ad94..0d8009ad424ee70538c086a63932f27a9dbbd1da 100644 (file)
@@ -577,6 +577,8 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
   if(result)
     goto out;
 
+  Curl_xfer_setup(data, FIRSTSOCKET, -1, TRUE, FIRSTSOCKET);
+
   /* issue the request */
   result = Curl_req_send(data, &req_buffer);
   if(result) {
@@ -584,8 +586,6 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
     goto out;
   }
 
-  Curl_xfer_setup(data, FIRSTSOCKET, -1, TRUE, FIRSTSOCKET);
-
   /* Increment the CSeq on success */
   data->state.rtsp_next_client_CSeq++;
 
index 8fe9c756143ef738408b4d04c0c03ecf81340d93..1b092f06b346b78b741e461b1a5cccd85d980b84 100644 (file)
@@ -576,8 +576,10 @@ static CURLcode cr_in_read(struct Curl_easy *data,
       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 = FALSE;
+    *peos = ctx->seen_eos;
     break;
   }
   DEBUGF(infof(data, "cr_in_read(len=%zu, total=%"CURL_FORMAT_CURL_OFF_T
@@ -743,7 +745,7 @@ static CURLcode cr_lc_add(struct Curl_easy *data)
   CURLcode result;
 
   result = Curl_creader_create(&reader, data, &cr_lc,
-                               CURL_CR_TRANSFER_ENCODE);
+                               CURL_CR_CONTENT_ENCODE);
   if(!result)
     result = Curl_creader_add(data, reader);
 
index ce1321fc80be9d8b13ebf3d44e5d640fe2bb3f44..8a5a5d7c33d21d24e7787a2e4d2d290775d9fbec 100644 (file)
@@ -2239,7 +2239,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
       arg = UPLOADBUFFER_MIN;
 
     data->set.upload_buffer_size = (unsigned int)arg;
-    Curl_safefree(data->state.ulbuf); /* force a realloc next opportunity */
     break;
 
   case CURLOPT_NOSIGNAL:
index 404dd84f43d18b8b9031d1c960f7a6b9b1dc5c67..77d34e31c4839534d27a7fbfb5b1575d5a3b4516 100644 (file)
--- a/lib/smb.c
+++ b/lib/smb.c
@@ -456,6 +456,9 @@ static CURLcode smb_connect(struct Curl_easy *data, bool *done)
   smbc->recv_buf = malloc(MAX_MESSAGE_SIZE);
   if(!smbc->recv_buf)
     return CURLE_OUT_OF_MEMORY;
+  smbc->send_buf = malloc(MAX_MESSAGE_SIZE);
+  if(!smbc->send_buf)
+    return CURLE_OUT_OF_MEMORY;
 
   /* Multiple requests are allowed with this connection */
   connkeep(conn, "SMB default");
@@ -567,7 +570,7 @@ static CURLcode smb_send(struct Curl_easy *data, size_t len,
   size_t bytes_written;
   CURLcode result;
 
-  result = Curl_xfer_send(data, data->state.ulbuf, len, &bytes_written);
+  result = Curl_xfer_send(data, smbc->send_buf, len, &bytes_written);
   if(result)
     return result;
 
@@ -592,7 +595,7 @@ static CURLcode smb_flush(struct Curl_easy *data)
   if(!smbc->send_size)
     return CURLE_OK;
 
-  result = Curl_xfer_send(data, data->state.ulbuf + smbc->sent, len,
+  result = Curl_xfer_send(data, smbc->send_buf + smbc->sent, len,
                           &bytes_written);
   if(result)
     return result;
@@ -608,13 +611,13 @@ static CURLcode smb_flush(struct Curl_easy *data)
 static CURLcode smb_send_message(struct Curl_easy *data, unsigned char cmd,
                                  const void *msg, size_t msg_len)
 {
-  CURLcode result = Curl_get_upload_buffer(data);
-  if(result)
-    return result;
-  smb_format_message(data, (struct smb_header *)data->state.ulbuf,
+  struct connectdata *conn = data->conn;
+  struct smb_conn *smbc = &conn->proto.smbc;
+
+  smb_format_message(data, (struct smb_header *)smbc->send_buf,
                      cmd, msg_len);
-  memcpy(data->state.ulbuf + sizeof(struct smb_header),
-         msg, msg_len);
+  DEBUGASSERT((sizeof(struct smb_header) + msg_len) <= MAX_MESSAGE_SIZE);
+  memcpy(smbc->send_buf + sizeof(struct smb_header), msg, msg_len);
 
   return smb_send(data, sizeof(struct smb_header) + msg_len, 0);
 }
@@ -772,15 +775,14 @@ static CURLcode smb_send_read(struct Curl_easy *data)
 
 static CURLcode smb_send_write(struct Curl_easy *data)
 {
+  struct connectdata *conn = data->conn;
+  struct smb_conn *smbc = &conn->proto.smbc;
   struct smb_write *msg;
   struct smb_request *req = data->req.p.smb;
   curl_off_t offset = data->req.offset;
   curl_off_t upload_size = data->req.size - data->req.bytecount;
-  CURLcode result = Curl_get_upload_buffer(data);
-  if(result)
-    return result;
-  msg = (struct smb_write *)data->state.ulbuf;
 
+  msg = (struct smb_write *)smbc->send_buf;
   if(upload_size >= MAX_PAYLOAD_SIZE - 1) /* There is one byte of padding */
     upload_size = MAX_PAYLOAD_SIZE - 1;
 
@@ -809,11 +811,11 @@ static CURLcode smb_send_and_recv(struct Curl_easy *data, void **msg)
 
   /* Check if there is data in the transfer buffer */
   if(!smbc->send_size && smbc->upload_size) {
-    size_t nread = smbc->upload_size > (size_t)data->set.upload_buffer_size ?
-      (size_t)data->set.upload_buffer_size : smbc->upload_size;
+    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, data->state.ulbuf, nread, &nread, &eos);
+    result = Curl_client_read(data, smbc->send_buf, nread, &nread, &eos);
     if(result && result != CURLE_AGAIN)
       return result;
     if(!nread)
@@ -1131,6 +1133,7 @@ static CURLcode smb_disconnect(struct Curl_easy *data,
   Curl_safefree(smbc->share);
   Curl_safefree(smbc->domain);
   Curl_safefree(smbc->recv_buf);
+  Curl_safefree(smbc->send_buf);
   return CURLE_OK;
 }
 
index 437f4a58a85d7dcb14e68665e0bd56a6e24e6d8a..9ea2a8cc31c8bc47137a9744adf4aa75761c8875 100644 (file)
--- a/lib/smb.h
+++ b/lib/smb.h
@@ -42,6 +42,7 @@ struct smb_conn {
   unsigned int session_key;
   unsigned short uid;
   char *recv_buf;
+  char *send_buf;
   size_t upload_size;
   size_t send_size;
   size_t sent;
index ddee2223ac48ca97eb5d3dbd8e1591677b32ce88..605bb16eeb0221fb528fdaa9387b6d05204db017 100644 (file)
@@ -111,6 +111,7 @@ static CURLcode smtp_continue_auth(struct Curl_easy *data, const char *mech,
                                    const struct bufref *resp);
 static CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech);
 static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out);
+static CURLcode cr_eob_add(struct Curl_easy *data);
 
 /*
  * SMTP protocol handler.
@@ -618,7 +619,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
     result = smtp_parse_address(data->set.str[STRING_MAIL_FROM],
                                 &address, &host);
     if(result)
-      return result;
+      goto out;
 
     /* Establish whether we should report SMTPUTF8 to the server for this
        mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */
@@ -642,8 +643,10 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
     /* Null reverse-path, RFC-5321, sect. 3.6.3 */
     from = strdup("<>");
 
-  if(!from)
-    return CURLE_OUT_OF_MEMORY;
+  if(!from) {
+    result = CURLE_OUT_OF_MEMORY;
+    goto out;
+  }
 
   /* Calculate the optional AUTH parameter */
   if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.sasl.authused) {
@@ -655,10 +658,8 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
          converting the host name to an IDN A-label if necessary */
       result = smtp_parse_address(data->set.str[STRING_MAIL_AUTH],
                                   &address, &host);
-      if(result) {
-        free(from);
-        return result;
-      }
+      if(result)
+        goto out;
 
       /* Establish whether we should report SMTPUTF8 to the server for this
          mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */
@@ -676,17 +677,14 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
         /* An invalid mailbox was provided but we'll simply let the server
            worry about it */
         auth = aprintf("<%s>", address);
-
-      free(address);
     }
     else
       /* Empty AUTH, RFC-2554, sect. 5 */
       auth = strdup("<>");
 
     if(!auth) {
-      free(from);
-
-      return CURLE_OUT_OF_MEMORY;
+      result = CURLE_OUT_OF_MEMORY;
+      goto out;
     }
   }
 
@@ -710,12 +708,8 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
     if(!result)
       result = Curl_mime_rewind(&data->set.mimepost);
 
-    if(result) {
-      free(from);
-      free(auth);
-
-      return result;
-    }
+    if(result)
+      goto out;
 
     data->state.infilesize = Curl_mime_size(&data->set.mimepost);
 
@@ -730,10 +724,8 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
     size = aprintf("%" CURL_FORMAT_CURL_OFF_T, data->state.infilesize);
 
     if(!size) {
-      free(from);
-      free(auth);
-
-      return CURLE_OUT_OF_MEMORY;
+      result = CURLE_OUT_OF_MEMORY;
+      goto out;
     }
   }
 
@@ -754,6 +746,15 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
     }
   }
 
+  /* Setup client reader for size and EOB conversion */
+  result = Client_reader_set_fread(data, data->state.infilesize);
+  if(result)
+    goto out;
+  /* Add the client reader doing STMP EOB escaping */
+  result = cr_eob_add(data);
+  if(result)
+    goto out;
+
   /* Send the MAIL command */
   result = Curl_pp_sendf(data, &conn->proto.smtpc.pp,
                          "MAIL FROM:%s%s%s%s%s%s",
@@ -765,6 +766,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
                          utf8 ? " SMTPUTF8"    /* Internationalised mailbox */
                                : "");          /* included in our envelope  */
 
+out:
   free(from);
   free(auth);
   free(size);
@@ -1393,9 +1395,6 @@ static CURLcode smtp_done(struct Curl_easy *data, CURLcode status,
   CURLcode result = CURLE_OK;
   struct connectdata *conn = data->conn;
   struct SMTP *smtp = data->req.p.smtp;
-  struct pingpong *pp = &conn->proto.smtpc.pp;
-  char *eob;
-  size_t len, bytes_written;
 
   (void)premature;
 
@@ -1411,46 +1410,6 @@ static CURLcode smtp_done(struct Curl_easy *data, CURLcode status,
   }
   else if(!data->set.connect_only && data->set.mail_rcpt &&
           (data->state.upload || IS_MIME_POST(data))) {
-    /* 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_xfer_send(data, 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);
-    }
 
     smtp_state(data, SMTP_POSTDATA);
 
@@ -1818,108 +1777,159 @@ static CURLcode smtp_parse_address(const char *fqma, char **address,
   return result;
 }
 
-CURLcode Curl_smtp_escape_eob(struct Curl_easy *data,
-                              const ssize_t nread,
-                              const ssize_t offset)
+struct cr_eob_ctx {
+  struct Curl_creader super;
+  struct bufq buf;
+  size_t n_eob; /* how many EOB bytes we matched so far */
+  size_t eob;       /* Number of bytes of the EOB (End Of Body) that
+                       have been received so far */
+  BIT(read_eos);  /* we read an EOS from the next reader */
+  BIT(eos);       /* we have returned an EOS */
+};
+
+static CURLcode cr_eob_init(struct Curl_easy *data,
+                            struct Curl_creader *reader)
 {
-  /* When sending a SMTP payload we must detect CRLF. sequences making sure
-     they are sent as CRLF.. instead, as a . on the beginning of a line will
-     be deleted by the server when not part of an EOB terminator and a
-     genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of
-     data by the server
-  */
-  ssize_t i;
-  ssize_t si;
-  struct SMTP *smtp = data->req.p.smtp;
-  char *scratch = data->state.scratch;
-  char *newscratch = NULL;
-  char *oldscratch = NULL;
-  size_t eob_sent;
+  struct cr_eob_ctx *ctx = (struct cr_eob_ctx *)reader;
+  (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 = (struct cr_eob_ctx *)reader;
+  (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 = (struct cr_eob_ctx *)reader;
+  CURLcode result = CURLE_OK;
+  size_t nread, i, start, n;
+  bool eos;
+
+  if(!ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) {
+    /* Get more and convert it when needed */
+    result = Curl_creader_read(data, reader->next, buf, blen, &nread, &eos);
+    if(result)
+      return result;
+
+    ctx->read_eos = eos;
+    if(nread) {
+      if(!ctx->n_eob && !memchr(buf, SMTP_EOB[0], nread)) {
+        /* not in the middle of a match, no EOB start found, just pass */
+        *pnread = nread;
+        *peos = FALSE;
+        return CURLE_OK;
+      }
+      /* scan for EOB (continuation) and convert */
+      for(i = start = 0; i < nread; ++i) {
+        if(ctx->n_eob >= SMTP_EOB_FIND_LEN) {
+          /* matched the EOB prefix and seeing additional char, add '.' */
+          result = Curl_bufq_cwrite(&ctx->buf, buf + start, i - start, &n);
+          if(result)
+            return result;
+          result = Curl_bufq_cwrite(&ctx->buf, ".", 1, &n);
+          if(result)
+            return result;
+          ctx->n_eob = 0;
+          start = i;
+          if(data->state.infilesize > 0)
+            data->state.infilesize++;
+        }
+
+        if(buf[i] != SMTP_EOB[ctx->n_eob])
+          ctx->n_eob = 0;
 
-      eob_sent = 0;
+        if(buf[i] == SMTP_EOB[ctx->n_eob]) {
+          /* matching another char of the EOB */
+          ++ctx->n_eob;
+        }
+      }
 
-      /* Reset the trailing CRLF flag as there was more data */
-      smtp->trailing_crlf = FALSE;
+      /* 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;
 
-  /* Only use the new buffer if we replaced something */
-  if(si != nread) {
-    /* Upload from the new (replaced) buffer instead */
-    data->req.upload_fromhere = scratch;
+  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;
+}
 
-    /* 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,
+  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 */
index 7a04c215499f982f9a7d1e7c8fc02d7539fa5005..7c2af6807390f5dc2a53dc3759dfa804f2278851 100644 (file)
@@ -84,17 +84,4 @@ struct smtp_conn {
 extern const struct Curl_handler Curl_handler_smtp;
 extern const struct Curl_handler Curl_handler_smtps;
 
-/* this is the 5-bytes End-Of-Body marker for SMTP */
-#define SMTP_EOB "\x0d\x0a\x2e\x0d\x0a"
-#define SMTP_EOB_LEN 5
-#define SMTP_EOB_FIND_LEN 3
-
-/* if found in data, replace it with this string instead */
-#define SMTP_EOB_REPL "\x0d\x0a\x2e\x2e"
-#define SMTP_EOB_REPL_LEN 4
-
-CURLcode Curl_smtp_escape_eob(struct Curl_easy *data,
-                              const ssize_t nread,
-                              const ssize_t offset);
-
 #endif /* HEADER_CURL_SMTP_H */
index a99fe1ddcf103910729e8d4526b3f9a46a890b9a..ff2b7b7457b2e2c37eef0f3f1fb80afab379d98f 100644 (file)
@@ -452,8 +452,6 @@ static CURLcode tftp_send_first(struct tftp_state_data *state,
     if(data->state.upload) {
       /* If we are uploading, send an WRQ */
       setpacketevent(&state->spacket, TFTP_EVENT_WRQ);
-      state->data->req.upload_fromhere =
-        (char *)state->spacket.data + 4;
       if(data->state.infilesize != -1)
         Curl_pgrsSetUploadSize(data, data->state.infilesize);
     }
index cbda03a2e7a9aa7ecc1498cf9f121aabf8582066..e6153c6bea369a224586ca41391afc06aa80fe57 100644 (file)
@@ -115,16 +115,6 @@ char *Curl_checkheaders(const struct Curl_easy *data,
 }
 #endif
 
-CURLcode Curl_get_upload_buffer(struct Curl_easy *data)
-{
-  if(!data->state.ulbuf) {
-    data->state.ulbuf = malloc(data->set.upload_buffer_size);
-    if(!data->state.ulbuf)
-      return CURLE_OUT_OF_MEMORY;
-  }
-  return CURLE_OK;
-}
-
 static int data_pending(struct Curl_easy *data)
 {
   struct connectdata *conn = data->conn;
@@ -330,17 +320,6 @@ out:
   return result;
 }
 
-CURLcode Curl_done_sending(struct Curl_easy *data,
-                           struct SingleRequest *k)
-{
-  k->keepon &= ~KEEP_SEND; /* we're done writing */
-
-  /* These functions should be moved into the handler struct! */
-  Curl_conn_ev_data_done_send(data);
-
-  return CURLE_OK;
-}
-
 #if defined(_WIN32) && defined(USE_WINSOCK)
 #ifndef SIO_IDEAL_SEND_BACKLOG_QUERY
 #define SIO_IDEAL_SEND_BACKLOG_QUERY 0x4004747B
@@ -368,174 +347,37 @@ static void win_update_buffer_size(curl_socket_t sockfd)
 /*
  * 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)
 {
-  size_t bytes_written;
-  CURLcode result;
-  ssize_t nread; /* number of bytes read */
-  struct SingleRequest *k = &data->req;
-
-  (void)conn;
-  *didwhat |= KEEP_SEND;
-
-  if(!(k->keepon & KEEP_SEND_PAUSE)) {
-    result = Curl_req_flush(data);
-    if(result == CURLE_AGAIN) /* unable to send all we have */
-      return CURLE_OK;
-    else if(result)
-      return result;
-  }
-
-  do {
-    curl_off_t nbody;
-    ssize_t offset = 0;
-    bool eos;
-
-    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 + (curl_off_t)k->upload_present ==
-         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;
-
-        if(k->exp100 == EXP100_SENDING_REQUEST) {
-          /* 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;
-        }
-
-        k->upload_fromhere += offset;
-        result = Curl_client_read(data, k->upload_fromhere,
-                                  data->set.upload_buffer_size-offset,
-                                  &fillcount, &eos);
-        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;
-      }
+  CURLcode result = CURLE_OK;
 
-      /* store number of bytes available for upload */
-      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_xfer_send(data,
-                            k->upload_fromhere, /* buffer pointer */
-                            k->upload_present,  /* buffer size */
-                            &bytes_written);    /* actually sent */
+  if(!Curl_req_done_sending(data)) {
+    *didwhat |= KEEP_SEND;
+    result = Curl_req_send_more(data);
     if(result)
       return result;
 
 #if defined(_WIN32) && defined(USE_WINSOCK)
+    /* FIXME: this looks like it would fit better into cf-socket.c
+     * but then I do not know enough Windows to say... */
     {
       struct curltime n = Curl_now();
-      if(Curl_timediff(n, conn->last_sndbuf_update) > 1000) {
-        win_update_buffer_size(conn->writesockfd);
-        conn->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
-
-    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)
@@ -626,7 +468,7 @@ CURLcode Curl_readwrite(struct Curl_easy *data,
   if((k->keepon & KEEP_SEND) && (select_bits & CURL_CSELECT_OUT)) {
     /* write */
 
-    result = readwrite_upload(data, conn, &didwhat);
+    result = readwrite_upload(data, &didwhat);
     if(result)
       goto out;
   }
@@ -1458,3 +1300,9 @@ CURLcode Curl_xfer_recv(struct Curl_easy *data,
     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;
+}
index a4af1899e51a58674f0bff685eac3f4af89f11b7..96e69988ad93f1191cb69d72743583d4e1f0f196 100644 (file)
@@ -50,10 +50,6 @@ int Curl_single_getsock(struct Curl_easy *data,
                         struct connectdata *conn, curl_socket_t *socks);
 CURLcode Curl_retry_request(struct Curl_easy *data, char **url);
 bool Curl_meets_timecondition(struct Curl_easy *data, time_t timeofdoc);
-CURLcode Curl_get_upload_buffer(struct Curl_easy *data);
-
-CURLcode Curl_done_sending(struct Curl_easy *data,
-                           struct SingleRequest *k);
 
 /**
  * Write the transfer raw response bytes, as received from the connection.
@@ -106,4 +102,6 @@ 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 */
index d72dfc773bbd1e827b5c09813afeb47e76ad3500..b30576597642d4dea149343a9abbec38d4432a62 100644 (file)
--- a/lib/url.c
+++ b/lib/url.c
@@ -277,7 +277,6 @@ CURLcode Curl_close(struct Curl_easy **datap)
 
   up_free(data);
   Curl_dyn_free(&data->state.headerb);
-  Curl_safefree(data->state.ulbuf);
   Curl_flush_cookies(data, TRUE);
   Curl_altsvc_save(data, data->asi, data->set.str[STRING_ALTSVC]);
   Curl_altsvc_cleanup(&data->asi);
index 3d68a60563aec5f073b6b59c931e68f30501ac4c..0871e1348d5f77b8019b5276a873c1323733ea8f 100644 (file)
@@ -1215,7 +1215,6 @@ struct UrlState {
   struct dynbuf headerb; /* buffer to store headers in */
   struct curl_slist *hstslist; /* list of HSTS files set by
                                   curl_easy_setopt(HSTS) calls */
-  char *ulbuf; /* allocated upload buffer or NULL */
   curl_off_t current_speed;  /* the ProgressShow() function sets this,
                                 bytes / second */