cURL / Mailing Lists / curl-library / Single Mail

curl-library

Re: [PATCH] Addition of trailer headers in HTTP requests generated by libcurl

From: Chrysovaladis Datsios <cdatsios_at_gmail.com>
Date: Fri, 18 Jan 2013 15:51:42 +0200

Thank you both for your immediate response and comments.
Every comment was a great suggestion towards the correct
implementation of the feature. Please do review the following sections
as formed after your previous comments.

+------------------------------+
| documentation |
+------------------------------+

curl_easy_setopt() option: CURLOPT_HTTPTRAILERHEADER

        Pass a pointer to a linked list of HTTP trailer headers to pass at
the end of the HTTP request. The HTTP request should be using chunked
Transfer-Encoding. The linked list of HTTP trailer headers should be a
fully valid list of struct curl_slist structs properly filled in. Use
curl_slist_append(3) to create the list and curl_slist_free_all(3) to
clean up an entire list.
        The option is set before the curl_easy_perfom(1). The linked list of
HTTP trailer headers contains some initial values of those headers.
The initial values are not the values that will be sent finally at the
end of the transfer. The final values of the HTTP trailer headers can
be known or calculated after the transfer has been performed. For
this, function curl_slist_replace(3) can be used to alter the initial
values to the real ones. The call of the new function
curl_slist_replace(3) inside a callback function can be used for that.

new function: curl_slist_replace - replace an old string in an slist
with a new string

Synopsis:
    #include <curl/curl.h>
    struct curl_slist *curl_slist_replace(struct curl_slist *list,
const char *old_data, const char *new_data);
Description:
    curl_slist_replace(3): replaces an old string in the linked list
with an new string. It returns the the address of the first record in
the list. If no list exists NULL is returned.
        
+----------------------------------------------+
| code example: |
| PUT request with trailer header |
+----------------------------------------------+

#include <stdio.h>
#include <curl/curl.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(void)
{
  CURL *curl;
  CURLcode res;
  double speed_upload, total_time;
  FILE *fd;
  struct curl_slist *custom_http_hdrs=NULL;
  struct curl_slist *trailer_http_hdrs=NULL;

  size_t read_callback(void *ptr, size_t size, size_t nmemb, void *stream) {
    size_t retcode;

    retcode = fread(ptr, size, nmemb, stream);
    if(ferror(stream)) {
      trailer_http_hdrs = curl_slist_replace(trailer_http_hdrs,
                               "mytrailer: default", "mytrailer: error");
    }
    if(feof(stream)) {
      trailer_http_hdrs = curl_slist_replace(trailer_http_hdrs,
                                 "mytrailer: default", "mytrailer: EOF");
    }
    return retcode;
  }

  fd = fopen("video_0001", "rb"); /* open file to upload */
  if(!fd) {

    return 1; /* can't continue */
  }

  curl = curl_easy_init();
  if(curl) {

    curl_easy_setopt(curl, CURLOPT_URL, "http://10.8.60.209/myfile");

    curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
    curl_easy_setopt(curl, CURLOPT_READDATA, fd);
    curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);

    custom_http_hdrs = curl_slist_append(custom_http_hdrs,
                                        "Transfer-Encoding: chunked");
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, custom_http_hdrs);

        /* give an initial value at the trailer header. Initial value
        * will be altered from inside read_callback() function.
        */
    trailer_http_hdrs = curl_slist_append(trailer_http_hdrs,
"mytrailer: default");
    curl_easy_setopt(curl, CURLOPT_HTTPTRAILERHEADER, trailer_http_hdrs);

    res = curl_easy_perform(curl);

    if(res != CURLE_OK) {
      fprintf(stderr, "curl_easy_perform() failed: %s\n",
              curl_easy_strerror(res));

    }

        fclose(fd);
    curl_slist_free_all(custom_http_hdrs);
        curl_slist_free_all(trailer_http_hdrs);
    curl_easy_cleanup(curl);
  }
  return 0;
}

+----------------------------------------------------------------------------+
| code diff: |
| This are the diffs from the original library: curl-7.28.1 |
+----------------------------------------------------------------------------+

diff -ur curl_orig/include/curl/curl.h curl_new/include/curl/curl.h
--- curl_orig/include/curl/curl.h 2012-09-26 12:46:15.000000000 +0300
+++ curl_new/include/curl/curl.h 2013-01-18 15:23:44.916347980 +0200
@@ -1536,6 +1536,9 @@
   /* set the SMTP auth originator */
   CINIT(MAIL_AUTH, OBJECTPOINT, 217),

+ /* points to a linked list of trailer headers, struct curl_slist kind */
+ CINIT(HTTPTRAILERHEADER, OBJECTPOINT, 218),
+
   CURLOPT_LASTENTRY /* the last unused */
 } CURLoption;

@@ -1918,6 +1921,17 @@
                                                  const char *);

 /*
+ * NAME curl_slist_replace()
+ *
+ * DESCRIPTION
+ *
+ * Replaces an old string in the linked list with a new string.
+ * Returns the address of the first record in the list.
+ * If no list exists NULL is returned.
+ */
+CURL_EXTERN struct curl_slist *curl_slist_replace(struct curl_slist *,
+ const char *, const char *);
+/*
  * NAME curl_slist_free_all()
  *
  * DESCRIPTION
diff -ur curl_orig/include/curl/typecheck-gcc.h
curl_new/include/curl/typecheck-gcc.h
--- curl_orig/include/curl/typecheck-gcc.h 2012-04-25 18:29:20.000000000 +0300
+++ curl_new/include/curl/typecheck-gcc.h 2013-01-18 15:23:59.652162725 +0200
@@ -311,6 +311,7 @@
    (option) == CURLOPT_PREQUOTE || \
    (option) == CURLOPT_TELNETOPTIONS || \
    (option) == CURLOPT_MAIL_RCPT || \
+ (option) == CURLOPT_HTTPTRAILERHEADER || \
    0)

 /* groups of curl_easy_getinfo infos that take the same type of argument */
diff -ur curl_orig/lib/http.c curl_new/lib/http.c
--- curl_orig/lib/http.c 2012-11-13 23:02:16.000000000 +0200
+++ curl_new/lib/http.c 2013-01-18 15:25:35.758954498 +0200
@@ -1590,6 +1590,34 @@
     }
     headers = headers->next;
   }
+
+ char *tptr;
+ struct curl_slist *trailer_headers=conn->data->set.trailer_headers;
+
+ while(trailer_headers) {
+ ptr = strchr(trailer_headers->data, ':');
+ if(ptr) {
+ tptr = --ptr; /* the point where the trailer header field ends */
+ ptr++; /* pass the colon */
+ while(*ptr && ISSPACE(*ptr))
+ ptr++;
+
+ if(*ptr) {
+ /* only send this if the contents was non-blank */
+
+ char *tfield = (char *)malloc(strlen(trailer_headers->data)+1);
+ strncpy(tfield, trailer_headers->data, tptr-trailer_headers->data+1);
+ tfield[tptr-trailer_headers->data+1] = '\0';
+ CURLcode result = Curl_add_bufferf(req_buffer, "Trailer: %s\r\n",
+ tfield);
+ tptr = NULL;
+ if(result)
+ return result;
+ }
+ }
+ trailer_headers = trailer_headers->next;
+ }
+
   return CURLE_OK;
 }

diff -ur curl_orig/lib/transfer.c curl_new/lib/transfer.c
--- curl_orig/lib/transfer.c 2012-11-13 23:02:16.000000000 +0200
+++ curl_new/lib/transfer.c 2013-01-18 15:26:02.654616374 +0200
@@ -929,6 +929,36 @@
          that instead of reading more data */
     }

+ /* The last chunk has zero size of data i.e. 0\r\n
+ * If this is the last chunk and trailer headers do exist*/
+ if(k->upload_chunky == true && data->req.upload_present == 5 &&
+ !strncmp(data->req.upload_fromhere, "0\r\n", 3) &&
+ conn->data->set.trailer_headers ) {
+
+ Curl_send_buffer *trailer_buffer = Curl_add_buffer_init();
+ result = Curl_add_bufferf(trailer_buffer, "0\r\n");
+ if(result)
+ return result;
+
+ char *ptr;
+ struct curl_slist *trailer_headers=conn->data->set.trailer_headers;
+ while(trailer_headers) {
+ ptr = strchr(trailer_headers->data, ':');
+ if(ptr) {
+ result = Curl_add_bufferf(trailer_buffer, "%s\r\n",
+ trailer_headers->data);
+ if(result)
+ return result;
+ }
+ trailer_headers = trailer_headers->next;
+ }
+ result = Curl_add_bufferf(trailer_buffer, "\r\n");
+ if(result)
+ return result;
+ data->req.upload_fromhere = trailer_buffer->buffer;
+ data->req.upload_present = trailer_buffer->size_used;
+ }
+
     /* write to socket (send away data) */
     result = Curl_write(conn,
                         conn->writesockfd, /* socket to send to */
diff -ur curl_orig/lib/url.c curl_new/lib/url.c
--- curl_orig/lib/url.c 2012-11-18 16:08:45.000000000 +0200
+++ curl_new/lib/url.c 2013-01-18 15:25:13.819230320 +0200
@@ -1261,6 +1261,13 @@
     data->set.headers = va_arg(param, struct curl_slist *);
     break;

+ case CURLOPT_HTTPTRAILERHEADER:
+ /*
+ * Set a list with HTTP trailer headers to use
+ */
+ data->set.trailer_headers = va_arg(param, struct curl_slist *);
+ break;
+
   case CURLOPT_HTTP200ALIASES:
     /*
      * Set a list of aliases for HTTP 200 in response header
diff -ur curl_orig/lib/urldata.h curl_new/lib/urldata.h
--- curl_orig/lib/urldata.h 2012-11-13 23:02:16.000000000 +0200
+++ curl_new/lib/urldata.h 2013-01-18 15:25:21.219137289 +0200
@@ -1466,6 +1466,7 @@
                                 download */
   curl_off_t set_resume_from; /* continue [ftp] transfer from here */
   struct curl_slist *headers; /* linked list of extra headers */
+ struct curl_slist *trailer_headers; /* linked list of trailer headers */
   struct curl_httppost *httppost; /* linked list of POST data */
   bool cookiesession; /* new cookie session? */
   bool crlf; /* convert crlf on ftp upload(?) */
diff -ur curl_orig/src/tool_cfgable.h curl_new/src/tool_cfgable.h
--- curl_orig/src/tool_cfgable.h 2012-08-08 23:45:18.000000000 +0300
+++ curl_new/src/tool_cfgable.h 2013-01-18 15:24:32.127754453 +0200
@@ -150,6 +150,7 @@
   curl_TimeCond timecond;
   time_t condtime;
   struct curl_slist *headers;
+ struct curl_slist *trailer_headers;
   struct curl_httppost *httppost;
   struct curl_httppost *last_post;
   struct curl_slist *telnet_options;
diff -ur curl_orig/src/tool_operate.c curl_new/src/tool_operate.c
--- curl_orig/src/tool_operate.c 2012-11-13 23:02:16.000000000 +0200
+++ curl_new/src/tool_operate.c 2013-01-18 15:24:52.059503876 +0200
@@ -978,6 +978,7 @@
           my_setopt(curl, CURLOPT_AUTOREFERER, config->autoreferer);
           my_setopt_str(curl, CURLOPT_USERAGENT, config->useragent);
           my_setopt_slist(curl, CURLOPT_HTTPHEADER, config->headers);
+ my_setopt_slist(curl, CURLOPT_HTTPTRAILERHEADER, config->headers);

           /* new in libcurl 7.5 */
           my_setopt(curl, CURLOPT_MAXREDIRS, config->maxredirs);
-------------------------------------------------------------------
List admin: http://cool.haxx.se/list/listinfo/curl-library
Etiquette: http://curl.haxx.se/mail/etiquette.html
Received on 2013-01-18