Skip to content

Instantly share code, notes, and snippets.

@danieleggert
Created April 13, 2016 21:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save danieleggert/3c810e8329d59a2449e1cf8d82292646 to your computer and use it in GitHub Desktop.
Save danieleggert/3c810e8329d59a2449e1cf8d82292646 to your computer and use it in GitHub Desktop.
//
// main.c
// pausehandle
//
// Created by Daniel Eggert on 12/04/16.
//
// Run an HTTP server like so:
// % echo "Hello" > hello.txt
// % python -m SimpleHTTPServer
// The code expects to be able to get a text file at
// http://localhost:8000/hello.txt
// You can verify that this works with
// % curl http://localhost:8000/hello.txt
//
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <curl/curl.h>
#define checkCode(a) \
do { \
CURLcode const _code = a; \
if (_code != CURLE_OK) { \
printf("%s -- %s (%d)\n", #a, curl_easy_strerror(_code), (int) _code); \
abort(); \
} \
} while (0)
#define checkMCode(a) \
do { \
CURLMcode const _code = a; \
if (_code != CURLM_OK) { \
printf("%s -- %s (%d)\n", #a, curl_multi_strerror(_code), (int) _code); \
abort(); \
} \
} while (0)
static CURL *createEasyHandle(void);
int main(int argc, const char * argv[]) {
checkCode(curl_global_init(CURL_GLOBAL_ALL));
CURLM * const multi = curl_multi_init();
CURL * const easy = createEasyHandle();
checkMCode(curl_multi_add_handle(multi, easy));
int repeats = 0;
int running = 1;
while (running) {
// Perform:
while (1) {
int runningHandles = 0;
CURLMcode const result = curl_multi_perform(multi, &runningHandles);
if (result == CURLM_CALL_MULTI_PERFORM) {
// call again
} else if (result != CURLM_OK) {
printf("curl_multi_perform() failed: %s (%d)\n", curl_easy_strerror(result), (int) result);
abort();
} else {
running = (0 < runningHandles);
break;
}
}
// Wait:
{
int numfds = 0;
checkMCode(curl_multi_wait(multi, NULL, 0, 1000, &numfds));
// '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 == 0) {
repeats++; /* count number of repeated zero numfds */
if(repeats > 1) {
// sleep 100 milliseconds
(void) usleep(100 * 1000);
}
} else {
repeats = 0;
}
}
}
checkMCode(curl_multi_remove_handle(multi, easy));
return 0;
}
static size_t writeCallback(char *ptr, size_t size, size_t nmemb, void *userdata) {
size_t const count = size * nmemb;
printf("[write] ");
for (size_t i = 0; i < count; ++i) {
if ((0x20 <= ptr[i]) && (ptr[i] <= 0x7e)) {
printf("%.1s", ptr + i);
} else {
printf("\\%02x", ptr[i]);
}
}
printf(" (%zu bytes)\n", count);
return count;
}
static size_t headerCallback(char *buffer, size_t size, size_t nitems, void *userdata) {
size_t const count = size * nitems;
printf("[header] ");
for (size_t i = 0; i < count; ++i) {
if ((0x20 <= buffer[i]) && (buffer[i] <= 0x7e)) {
printf("%.1s", buffer + i);
} else {
printf("\\%02x", buffer[i]);
}
}
printf(" (%zu bytes)\n", count);
// If we've received the final CR LF CR LF, we'll tell libcurl to pause:
if ((count == 2) && (buffer[0] == 0xd) && (buffer[1] == 0xa)) {
printf("[header] <- returning CURL_WRITEFUNC_PAUSE\n");
return CURL_WRITEFUNC_PAUSE;
} else {
return count;
}
}
static void dump(const char *text, unsigned char *ptr, size_t size) {
unsigned int const width = 0x10;
printf("[debug] %s, %10.10ld bytes (0x%8.8lx)\n", text, (long)size, (long)size);
for (size_t i=0; i<size; i+= width) {
printf(" %4.4lx: ", (long)i);
/* show hex to the left */
for (size_t c = 0; c < width; c++) {
if (i+c < size) {
printf("%02x ", ptr[i+c]);
} else {
printf(" ");
}
}
/* show data on the right */
for (size_t c = 0; (c < width) && (i+c < size); c++) {
char x = (ptr[i+c] >= 0x20 && ptr[i+c] < 0x80) ? ptr[i+c] : '.';
printf("%.1s", &x);
}
printf("\n"); /* newline */
}
}
static int debugCallback(CURL *handle, curl_infotype type, char *data, size_t size, void *userp) {
(void) handle; /* prevent compiler warning */
const char *text;
switch (type) {
case CURLINFO_TEXT:
printf("== 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, (unsigned char *) data, size);
return 0;
}
static CURL *createEasyHandle(void) {
CURL *handle = curl_easy_init();
curl_easy_setopt(handle, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, writeCallback);
curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, headerCallback);
curl_easy_setopt(handle, CURLOPT_DEBUGFUNCTION, debugCallback);
curl_easy_setopt(handle, CURLOPT_URL, "http://localhost:8000/hello.txt");
curl_easy_setopt(handle, CURLOPT_ACCEPT_ENCODING, "");
curl_easy_setopt(handle, CURLOPT_HTTPGET, 1L);
return handle;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment