curl-library
Re: [PATCH] Addition of trailer headers in HTTP requests generated by libcurl
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