From be865581d4667814ea11d82099519cd2cd0ddfd3 Mon Sep 17 00:00:00 2001
From: Chrysovaladis Datsios <cdatsios@gmail.com>
Date: Thu, 14 Feb 2013 14:24:10 +0100
Subject: [PATCH] CURLOPT_TRAILERFUNCTION: support chunked-encoding request
 trailers

curl_easy_setopt() option: CURLOPT_TRAILERFUNCTION

Pass a pointer to a function that matches the following prototype:
struct curl_slist* function(CURL *handle, struct curl_slist
*trailer_headers, void *userdata); This function gets called by libcurl
when it is to send the last chunk of (zero payload)data to the
peer. Chunked transfer-encoding must be used. The pointer to the
trailer_headers points to a linked list of type struct curl_slist that
must contain the trailer headers. The trailer header names have to be
set in advance as custom headers using the CURLOPT_HTTPHEADER option
with "Trailer" as "header name_field" and the actual header name as
"header value_field". Inside the callback function the trailer_headers
list have to be created using the curl_slist_append(3) function. The
callback function returns a pointer to the trailer_headers list. The
user also can pass in a custom pointer to the callback, this is done by
the associated CURLOPT_TRAILERDATA option.

curl_easy_setopt() option: CURLOPT_TRAILERDATA Data pointer to pass to
the trailer function. If you use the CURLOPT_TRAILERFUNCTION option,
this is the pointer you'll get as input.
---
 include/curl/curl.h          |   13 ++++++++++++-
 include/curl/typecheck-gcc.h |   16 ++++++++++++++++
 lib/transfer.c               |   35 +++++++++++++++++++++++++++++++++--
 lib/url.c                    |   19 +++++++++++++++++++
 lib/urldata.h                |    6 ++++++
 5 files changed, 86 insertions(+), 3 deletions(-)

diff --git a/include/curl/curl.h b/include/curl/curl.h
index 5b39a24..5bc0b55 100644
--- a/include/curl/curl.h
+++ b/include/curl/curl.h
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -308,6 +308,12 @@ typedef size_t (*curl_read_callback)(char *buffer,
                                       size_t nitems,
                                       void *instream);
 
+/* pointer to function curl_trailerheaders_callback */
+typedef struct curl_slist *
+  (*curl_trailerheaders_callback)(CURL *handle,
+                                  struct curl_slist *trailer_headers,
+                                  void *userdata);
+
 typedef enum  {
   CURLSOCKTYPE_IPCXN,  /* socket created for a specific IP connection */
   CURLSOCKTYPE_ACCEPT, /* socket created by accept() call */
@@ -1536,6 +1542,11 @@ typedef enum {
   /* set the SMTP auth originator */
   CINIT(MAIL_AUTH, OBJECTPOINT, 217),
 
+  /* Function that will be called to set the final values to trailer headers */
+  CINIT(TRAILERFUNCTION, FUNCTIONPOINT, 218),
+  /* Data passed to the trailer headers callback function */
+  CINIT(TRAILERDATA, OBJECTPOINT, 219),
+
   CURLOPT_LASTENTRY /* the last unused */
 } CURLoption;
 
diff --git a/include/curl/typecheck-gcc.h b/include/curl/typecheck-gcc.h
index f8917e8..811e40d 100644
--- a/include/curl/typecheck-gcc.h
+++ b/include/curl/typecheck-gcc.h
@@ -57,6 +57,9 @@ __extension__ ({                                                              \
     if((_curl_opt) == CURLOPT_READFUNCTION)                                   \
       if(!_curl_is_read_cb(value))                                            \
         _curl_easy_setopt_err_read_cb();                                      \
+    if((_curl_opt) == CURLOPT_TRAILERFUNCTION)                                \
+      if(!_curl_is_trailerheaders_cb(value))                                  \
+        _curl_easy_setopt_err_trailerheaders_cb();                            \
     if((_curl_opt) == CURLOPT_IOCTLFUNCTION)                                  \
       if(!_curl_is_ioctl_cb(value))                                           \
         _curl_easy_setopt_err_ioctl_cb();                                     \
@@ -157,6 +160,9 @@ _CURL_WARNING(_curl_easy_setopt_err_write_callback,
   "curl_easy_setopt expects a curl_write_callback argument for this option")
 _CURL_WARNING(_curl_easy_setopt_err_read_cb,
   "curl_easy_setopt expects a curl_read_callback argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_trailerheaders_cb,
+              "curl_easy_setopt expects a "
+              "curl_trailerheaders_callback argument for this option")
 _CURL_WARNING(_curl_easy_setopt_err_ioctl_cb,
   "curl_easy_setopt expects a curl_ioctl_callback argument for this option")
 _CURL_WARNING(_curl_easy_setopt_err_sockopt_cb,
@@ -294,6 +300,7 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_slist,
    (option) == CURLOPT_INTERLEAVEDATA ||                                      \
    (option) == CURLOPT_CHUNK_DATA ||                                          \
    (option) == CURLOPT_FNMATCH_DATA ||                                        \
+   (option) == CURLOPT_TRAILERDATA ||                                         \
    0)
 
 /* evaluates to true if option takes a POST data argument (void* or char*) */
@@ -426,6 +433,15 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_slist,
   (__builtin_types_compatible_p(__typeof__(func), type) ||                    \
    __builtin_types_compatible_p(__typeof__(func), type*))
 
+/* evaluates to true if expr is of type curl_trailerheaders_callback */
+#define _curl_is_trailerheaders_cb(expr)                                      \
+  (_curl_callback_compatible(expr, _curl_trailerheaders_callback1) ||         \
+   _curl_callback_compatible(expr, _curl_trailerheaders_callback2))
+typedef struct curl_slist * (_curl_trailerheaders_callback1)(CURL *,
+                                            struct curl_slist *, void*);
+typedef struct curl_slist * (_curl_trailerheaders_callback2)(CURL *,
+                                            struct curl_slist *, const void*);
+
 /* evaluates to true if expr is of type curl_read_callback or "similar" */
 #define _curl_is_read_cb(expr)                                          \
   (_curl_is_NULL(expr) ||                                                     \
diff --git a/lib/transfer.c b/lib/transfer.c
index 330b37a..2b4501a 100644
--- a/lib/transfer.c
+++ b/lib/transfer.c
@@ -98,6 +98,7 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp)
   struct SessionHandle *data = conn->data;
   size_t buffersize = (size_t)bytes;
   int nread;
+  size_t trlen = 0;
 #ifdef CURL_DOES_CONVERSIONS
   bool sending_http_headers = FALSE;
 
@@ -187,8 +188,38 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp)
     /* copy the prefix to the buffer, leaving out the NUL */
     memcpy(data->req.upload_fromhere, hexbuffer, hexlen);
 
+    /* If is the last chunk, here put the trailer headers if any */
+    if((nread - hexlen) == 0) {
+      if(data->set.is_trailerheaders_set == 1) {
+        /* The calback function that adds trailer header values */
+        data->set.trailer_headers =
+          (data->set.trailerheaders_func)(data,
+                                          data->set.trailer_headers,
+                                          data->set.trailer_udata);
+        if(data->set.trailer_headers) {
+          char *ptr;
+          struct curl_slist *trailer_headers=data->set.trailer_headers;
+          trlen = 0;
+          while(trailer_headers) {
+            /* header name_field : header value_field */
+            ptr = strchr(trailer_headers->data, ':');
+            if(ptr) {
+              memcpy(data->req.upload_fromhere + nread + trlen,
+                     trailer_headers->data, strlen(trailer_headers->data));
+              trlen += strlen(trailer_headers->data);
+
+              memcpy(data->req.upload_fromhere + nread + trlen,
+                     endofline_network, strlen(endofline_network));
+              trlen += strlen(endofline_native);
+            }
+            trailer_headers = trailer_headers->next;
+          }
+        }
+      }
+    }
+
     /* always append ASCII CRLF to the data */
-    memcpy(data->req.upload_fromhere + nread,
+    memcpy(data->req.upload_fromhere + nread + trlen,
            endofline_network,
            strlen(endofline_network));
 
@@ -225,7 +256,7 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp)
   }
 #endif /* CURL_DOES_CONVERSIONS */
 
-  *nreadp = nread;
+  *nreadp = nread + (int)trlen;
 
   return CURLE_OK;
 }
diff --git a/lib/url.c b/lib/url.c
index f9ce3ce..e68bf7e 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -1059,6 +1059,25 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
     data->set.headers = va_arg(param, struct curl_slist *);
     break;
 
+  case CURLOPT_TRAILERFUNCTION:
+    /*
+     * Set final values of trailer headers callback
+     */
+    data->set.trailerheaders_func = va_arg(param,
+                                           curl_trailerheaders_callback);
+    if(!data->set.trailerheaders_func)
+      data->set.is_trailerheaders_set = 0;
+    else
+      data->set.is_trailerheaders_set = 1;
+    break;
+
+  case CURLOPT_TRAILERDATA:
+    /*
+     * Custom data to pass to the trailer headers callback
+     */
+    data->set.trailer_udata = va_arg(param, void *);
+    break;
+
   case CURLOPT_HTTP200ALIASES:
     /*
      * Set a list of aliases for HTTP 200 in response header
diff --git a/lib/urldata.h b/lib/urldata.h
index d564ae1..3e40055 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -1429,6 +1429,11 @@ struct UserDefined {
   curl_read_callback fread_func;     /* function that reads the input */
   int is_fread_set; /* boolean, has read callback been set to non-NULL? */
   int is_fwrite_set; /* boolean, has write callback been set to non-NULL? */
+  curl_trailerheaders_callback trailerheaders_func; /* function that sets
+                                        the final values at trailer headers */
+  void* trailer_udata; /* pointer, pass user data to trailerheaders callback */
+  int is_trailerheaders_set; /* boolean, has trailerheaders callback
+                                set to non-NULL? */
   curl_progress_callback fprogress;  /* function for progress information */
   curl_debug_callback fdebug;      /* function that write informational data */
   curl_ioctl_callback ioctl_func;  /* function for I/O control */
@@ -1466,6 +1471,7 @@ struct UserDefined {
                                 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(?) */
-- 
1.7.10.4

