Skip to content

Instantly share code, notes, and snippets.

@bagder
Last active February 21, 2023 05:24
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bagder/4082d9dc159401741bfdd6d9112c96da to your computer and use it in GitHub Desktop.
Save bagder/4082d9dc159401741bfdd6d9112c96da to your computer and use it in GitHub Desktop.
libcurl multipart formpost without content-length
#include <stdio.h>
#include <string.h>
#include <curl/curl.h>
#define MULTI_PART_CONTENT_TYPE_AUDIO "application/octet-stream"
#define DATA_SIZE 10
char demoBuffer[DATA_SIZE] = {1,2,3,4,5,6,7,8,9,0};
const char *metadata = "I am metadata";
int Curr_index ;
struct data {
char trace_ascii; /* 1 or 0 */
};
static
void dump(const char *text,
FILE *stream, unsigned char *ptr, size_t size,
char nohex)
{
size_t i;
size_t c;
unsigned int width=0x10;
if(nohex)
/* without the hex output, we can fit more on screen */
width = 0x40;
fprintf(stream, "%s, %10.10ld bytes (0x%8.8lx)\n",
text, (long)size, (long)size);
for(i=0; i<size; i+= width) {
fprintf(stream, "%4.4lx: ", (long)i);
if(!nohex) {
/* hex not disabled, show it */
for(c = 0; c < width; c++)
if(i+c < size)
fprintf(stream, "%02x ", ptr[i+c]);
else
fputs(" ", stream);
}
for(c = 0; (c < width) && (i+c < size); c++) {
/* check for 0D0A; if found, skip past and start a new line of output */
if(nohex && (i+c+1 < size) && ptr[i+c]==0x0D && ptr[i+c+1]==0x0A) {
i+=(c+2-width);
break;
}
fprintf(stream, "%c",
(ptr[i+c]>=0x20) && (ptr[i+c]<0x80)?ptr[i+c]:'.');
/* check again for 0D0A, to avoid an extra \n if it's at width */
if(nohex && (i+c+2 < size) && ptr[i+c+1]==0x0D && ptr[i+c+2]==0x0A) {
i+=(c+3-width);
break;
}
}
fputc('\n', stream); /* newline */
}
fflush(stream);
}
static
int my_trace(CURL *handle, curl_infotype type,
char *data, size_t size,
void *userp)
{
struct data *config = (struct data *)userp;
const char *text;
(void)handle; /* prevent compiler warning */
switch (type) {
case CURLINFO_TEXT:
fprintf(stderr, "== Info: %s", data);
default: /* in case a new one is introduced to shock us */
return 0;
case CURLINFO_HEADER_OUT:
text = "=> Send header";
break;
case CURLINFO_DATA_OUT:
text = "=> Send data";
break;
case CURLINFO_SSL_DATA_OUT:
text = "=> Send SSL data";
break;
case CURLINFO_HEADER_IN:
text = "<= Recv header";
break;
case CURLINFO_DATA_IN:
text = "<= Recv data";
break;
case CURLINFO_SSL_DATA_IN:
text = "<= Recv SSL data";
break;
}
dump(text, stderr, (unsigned char *)data, size, config->trace_ascii);
return 0;
}
static int getTransferStatus(CURLM *multiHandleInstance,CURL *currentHandle)
{
//check the status of transfer CURLcode return_code=0;
int msgs_left=0;
CURLMsg *msg=NULL;
CURL *eh=NULL;
CURLcode return_code;
int http_status_code = 0;
while ((msg = curl_multi_info_read(multiHandleInstance, &msgs_left)))
{
if (msg->msg == CURLMSG_DONE) {
eh = msg->easy_handle;
if(currentHandle == eh)
{
return_code = msg->data.result;
if((return_code!=CURLE_OK) &&(return_code != CURLE_RECV_ERROR))
{
fprintf(stderr, "CURL error code: %d\n", msg->data.result);
continue;
}
// Get HTTP status code
http_status_code=0;
curl_easy_getinfo(eh, CURLINFO_RESPONSE_CODE, &http_status_code);
}
}
}
return http_status_code;
}
static size_t processResponse(void *ptr, size_t size, size_t nmemb, void *instance)
{
//write callback
return size * nmemb;
}
static size_t datareader(void *ptr, size_t size, size_t nmemb, void* userp)
{
//read callback
char *pooh = ( char *)userp;
if(size*nmemb < 1)
return 0;
if(Curr_index < DATA_SIZE) {
*(char *)ptr = pooh[Curr_index]; /* copy one single byte */
Curr_index++;
return 1; /* we return 1 byte at a time! */
}
return 0;
}
int main(void)
{
const char *postUrl = "localhost";
CURLM *multi_handle = curl_multi_init();
int still_running = 0;
Curr_index = 0;
/*I want to upload the data using chunked encoding,
which means I dont know the total content size */
struct curl_slist *header = NULL;
header = curl_slist_append(header, "Content-Type: multipart/form-data");
header = curl_slist_append(header, "Transfer-Encoding: chunked");
struct curl_httppost *formpost = NULL;
struct curl_httppost *lastptr = NULL;
curl_formadd(&formpost,
&lastptr,
CURLFORM_COPYNAME, "metadata",
CURLFORM_COPYCONTENTS, metadata,
CURLFORM_CONTENTTYPE, "not/important",
CURLFORM_END);
/*In below code I am commenting out the CONTENTSLENGTH
as i dont know the total size of the buffer,
the behaviour is same when when CONTENTSLENGTH
is commented or set to a false value.The transfer never gets completed. */
/*Curl documentation for CURLFORM_STREAM( [https://curl.haxx.se/libcurl/c/curl_formadd.html] )
tells developers its mandatory to set the CURLFORM_CONTENTSLENGTH,
In cases in "Chunked" transfer encoding the CONTENTSLENGTH is not known*/
curl_formadd(&formpost,
&lastptr,
CURLFORM_COPYNAME, "audio",
CURLFORM_CONTENTTYPE, MULTI_PART_CONTENT_TYPE_AUDIO,
CURLFORM_STREAM, demoBuffer,
//CURLFORM_CONTENTSLENGTH, DATA_SIZE,
CURLFORM_END);
CURL *eventHttp_handle;
CURLcode res;
// init the curl session
eventHttp_handle = curl_easy_init();
if (eventHttp_handle)
{
struct data config;
curl_easy_setopt(eventHttp_handle, CURLOPT_URL, postUrl);
curl_easy_setopt(eventHttp_handle, CURLOPT_HTTP_VERSION,
CURL_HTTP_VERSION_2_0);
/* disable progress meter, set to 0L to enable and disable debug output */
curl_easy_setopt(eventHttp_handle, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(eventHttp_handle, CURLOPT_NOPROGRESS, 0L);
curl_easy_setopt(eventHttp_handle, CURLOPT_DEBUGFUNCTION, my_trace);
config.trace_ascii = 1; /* enable ascii tracing */
curl_easy_setopt(eventHttp_handle, CURLOPT_DEBUGDATA, &config);
//write callback function
curl_easy_setopt(eventHttp_handle, CURLOPT_WRITEFUNCTION, processResponse);
curl_easy_setopt(eventHttp_handle, CURLOPT_WRITEDATA, NULL);
//read callback function for sending audio
curl_easy_setopt(eventHttp_handle, CURLOPT_READFUNCTION, datareader);
curl_easy_setopt(eventHttp_handle, CURLOPT_READDATA,demoBuffer);
curl_easy_setopt(eventHttp_handle, CURLOPT_HTTPPOST, formpost);
curl_easy_setopt(eventHttp_handle, CURLOPT_HTTPHEADER, header);
//adding easy handle to Multihandle
curl_multi_add_handle(multi_handle, eventHttp_handle);
do {
CURLMcode mc;
int numfds;
/* when CURLFORM_CONTENTSLENGTH is set to false value
the "still_running" (active handles) never returns to 0*/
mc = curl_multi_perform(multi_handle, &still_running);
if(mc == CURLM_OK ) {
/* wait for activity, timeout or "nothing" */
mc = curl_multi_wait(multi_handle, NULL, 0, 1000, &numfds);
}
if(mc != CURLM_OK) {
fprintf(stderr, "curl_multi failed, code %d.n", mc);
break;
}
/* 'numfds' being zero means either a timeout or no file descriptors to
wait for. Try timeout on first occurrence, then assume no file
descriptors and no file descriptors to wait for means wait for 100
milliseconds. */
if(!numfds)
{
struct timeval wait = { 0, 100 * 1000 }; // 100ms
select(0, NULL, NULL, NULL, &wait);
}
} while(still_running);
if (header)
curl_slist_free_all(header);
if (formpost)
{
curl_formfree(formpost);
formpost = NULL;
}
int responseCode = getTransferStatus(multi_handle,eventHttp_handle);
if(responseCode == 200)//200 OK!
{
// handle response body
}
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment