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: Thu, 14 Feb 2013 15:24:10 +0200

Hi, I made some changes base on your suggestions:

> I would prefer if we cut out the 'HTTP' from the name, and try to keep it
> otherwise protocol agnostic. If we pretend this isn't HTTP specific, there's
> no reason we can't use this callback for other protocols in another similar
> scenario if things turn that way in a future.
Now the name is CURLOPT_TRAILERFUNCTION. The callback function returns
a pointer to the trailer headers list in case it is needed somewere else.

> all libcurl callbacks have an associated *DATA option that allows the
> user to pass in a custom pointer to the callback. That's crucial for a
> callback to be able to function without dreaded global variables.
This is done by CURLOPT_TRAILERDATA option.See documentation.

> This seems like a very blunt way to detect the end of an upload. Surely this
> information can be passed on/figured out in a better way!
Now the end of a chunked transfer is detected in a more suitable place
inside the code. See diff.

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

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.

        
+----------------------------------------------+
| code example: |
| PUT request with trailer header |
+----------------------------------------------+
#include <stdio.h>
#include <curl/curl.h>
#include <sys/stat.h>
#include <fcntl.h>

/* trailer header altering callback function */
struct curl_slist * trailerheader_callback(CURL *handle, struct
curl_slist *trailer_headers, void *userdata) {
  int *code = (int *)userdata;
  if(!(*code))
    trailer_headers = curl_slist_append(trailer_headers, "mytrailer: EOF");
  else
    trailer_headers = curl_slist_append(trailer_headers, "mytrailer: error");

  return trailer_headers;
}

int main(void)
{
  CURL *curl;
  CURLcode res;
  int filecode;
  FILE *fd;
  struct curl_slist *custom_http_hdrs=NULL;

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

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

  /* Read callback function */
  size_t read_callback(void *ptr, size_t size, size_t nmemb, void *stream) {
    size_t retcode;

    retcode = fread(ptr, size, nmemb, stream);
    if(!retcode) {
      if(ferror(stream))
        filecode = 1;
      if(feof(stream))
        filecode = 0;
    }
    return retcode;
  }

  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");
    custom_http_hdrs = curl_slist_append(custom_http_hdrs, "Trailer:
mytrailer");
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, custom_http_hdrs);

    /* Initial value will be altered from inside
trailerheader_callback() function */
    curl_easy_setopt(curl, CURLOPT_TRAILERFUNCTION, trailerheader_callback);
    curl_easy_setopt(curl, CURLOPT_TRAILERDATA, &filecode);

    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_easy_cleanup(curl);
  }
  return 0;
}

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

diff -ru 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-02-14 14:28:24.285818444 +0200
@@ -308,6 +308,11 @@
                                       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 +1541,11 @@
   /* 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 -ru 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-02-14 14:28:33.773699165 +0200
@@ -57,6 +57,9 @@
     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_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 @@
    (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 @@
   (__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 -ru 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-02-14 14:29:05.805296473 +0200
@@ -104,6 +104,7 @@
   struct SessionHandle *data = conn->data;
   size_t buffersize = (size_t)bytes;
   int nread;
+ int trlen = 0;
 #ifdef CURL_DOES_CONVERSIONS
   bool sending_http_headers = FALSE;

@@ -193,8 +194,35 @@
     /* 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 += (int)strlen(trailer_headers->data);
+ memcpy(data->req.upload_fromhere + nread + trlen,
+ endofline_network, strlen(endofline_network));
+ trlen += (int)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));

@@ -231,7 +259,7 @@
   }
 #endif /* CURL_DOES_CONVERSIONS */

- *nreadp = nread;
+ *nreadp = nread + trlen;

   return CURLE_OK;
 }
diff -ru 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-02-14 14:29:20.357113531 +0200
@@ -1261,6 +1261,25 @@
     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 -ru 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-02-14 14:29:30.740982988 +0200
@@ -1429,6 +1429,11 @@
   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 @@
                                 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 -ru 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-02-14 14:30:28.108261784 +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;
-------------------------------------------------------------------
List admin: http://cool.haxx.se/list/listinfo/curl-library
Etiquette: http://curl.haxx.se/mail/etiquette.html
Received on 2013-02-14