naett.c

Tiny cross-platform HTTP / HTTPS client library in C.
git clone git://git.finwo.net/lib/naett.c
Log | Files | Refs | README | LICENSE

naett_core.c (10940B)


      1 #include "naett_internal.h"
      2 #include <stdarg.h>
      3 #include <stdlib.h>
      4 #include <stddef.h>
      5 #include <string.h>
      6 #include <assert.h>
      7 
      8 typedef struct InternalParam* InternalParamPtr;
      9 typedef void (*ParamSetter)(InternalParamPtr param, InternalRequest* req);
     10 
     11 typedef struct InternalParam {
     12         ParamSetter setter;
     13         int offset;
     14         union {
     15             int integer;
     16             const char* string;
     17             struct {
     18                 const char* key;
     19                 const char* value;
     20             } kv;
     21             void* ptr;
     22             void (*func)(void);
     23         };
     24 } InternalParam;
     25 
     26 typedef struct InternalOption {
     27     #define maxParams 2
     28     int numParams;
     29     InternalParam params[maxParams];
     30 } InternalOption;
     31 
     32 static void stringSetter(InternalParamPtr param, InternalRequest* req) {
     33     char* stringCopy = strdup(param->string);
     34     char* opaque = (char*)&req->options;
     35     char** stringField = (char**)(opaque + param->offset);
     36     if (*stringField) {
     37         free(*stringField);
     38     }
     39     *stringField = stringCopy;
     40 }
     41 
     42 static void intSetter(InternalParamPtr param, InternalRequest* req) {
     43     char* opaque = (char*)&req->options;
     44     int* intField = (int*)(opaque + param->offset);
     45     *intField = param->integer;
     46 }
     47 
     48 static void ptrSetter(InternalParamPtr param, InternalRequest* req) {
     49     char* opaque = (char*)&req->options;
     50     void** ptrField = (void**)(opaque + param->offset);
     51     *ptrField = param->ptr;
     52 }
     53 
     54 static void kvSetter(InternalParamPtr param, InternalRequest* req) {
     55     char* opaque = (char*)&req->options;
     56     KVLink** kvField = (KVLink**)(opaque + param->offset);
     57 
     58     naettAlloc(KVLink, newNode);
     59     newNode->key = strdup(param->kv.key);
     60     newNode->value = strdup(param->kv.value);
     61     newNode->next = *kvField;
     62 
     63     *kvField = newNode;
     64 }
     65 
     66 static int defaultBodyReader(void* dest, int bufferSize, void* userData) {
     67     Buffer* buffer = (Buffer*) userData;
     68 
     69     if (dest == NULL) {
     70         return buffer->size;
     71     }
     72 
     73     int bytesToRead = buffer->size - buffer->position;
     74     if (bytesToRead > bufferSize) {
     75         bytesToRead = bufferSize;
     76     }
     77 
     78     const char* source = ((const char*)buffer->data) + buffer->position;
     79     memcpy(dest, source, bytesToRead);
     80     buffer->position += bytesToRead;
     81     return bytesToRead;
     82 }
     83 
     84 static int defaultBodyWriter(const void* source, int bytes, void* userData) {
     85     Buffer* buffer = (Buffer*) userData;
     86     int newCapacity = buffer->capacity;
     87     if (newCapacity == 0) {
     88         newCapacity = bytes;
     89     }
     90     while (newCapacity - buffer->size < bytes) {
     91         newCapacity *= 2;
     92     }
     93     if (newCapacity != buffer->capacity) {
     94         buffer->data = realloc(buffer->data, newCapacity);
     95         buffer->capacity = newCapacity;
     96     }
     97     char* dest = ((char*)buffer->data) + buffer->size;
     98     memcpy(dest, source, bytes);
     99     buffer->size += bytes;
    100     return bytes;
    101 }
    102 
    103 static int initialized = 0;
    104 
    105 static void initRequest(InternalRequest* req, const char* url) {
    106     assert(initialized);
    107     req->options.method = strdup("GET");
    108     req->options.timeoutMS = 5000;
    109     req->url = strdup(url);
    110 }
    111 
    112 static void applyOptionParams(InternalRequest* req, InternalOption* option) {
    113     for (int j = 0; j < option->numParams; j++) {
    114         InternalParam* param = option->params + j;
    115         param->setter(param, req);
    116     }
    117 }
    118 
    119 // Public API
    120 
    121 void naettInit(naettInitData initData) {
    122     assert(!initialized);
    123     naettPlatformInit(initData);
    124     initialized = 1;
    125 }
    126 
    127 naettOption* naettMethod(const char* method) {
    128     naettAlloc(InternalOption, option);
    129     option->numParams = 1;
    130     InternalParam* param = &option->params[0];
    131 
    132     param->string = method;
    133     param->offset = offsetof(RequestOptions, method);
    134     param->setter = stringSetter;
    135 
    136     return (naettOption*)option;
    137 }
    138 
    139 naettOption* naettHeader(const char* name, const char* value) {
    140     naettAlloc(InternalOption, option);
    141     option->numParams = 1;
    142     InternalParam* param = &option->params[0];
    143 
    144     param->kv.key = name;
    145     param->kv.value = value;
    146     param->offset = offsetof(RequestOptions, headers);
    147     param->setter = kvSetter;
    148 
    149     return (naettOption*)option;
    150 }
    151 
    152 naettOption* naettUserAgent(const char* userAgent) {
    153     naettAlloc(InternalOption, option);
    154     option->numParams = 1;
    155     InternalParam* param = &option->params[0];
    156 
    157     param->string = userAgent;
    158     param->offset = offsetof(RequestOptions, userAgent);
    159     param->setter = stringSetter;
    160 
    161     return (naettOption*)option;
    162 }
    163 
    164 naettOption* naettTimeout(int timeoutMS) {
    165     naettAlloc(InternalOption, option);
    166     option->numParams = 1;
    167     InternalParam* param = &option->params[0];
    168 
    169     param->integer = timeoutMS;
    170     param->offset = offsetof(RequestOptions, timeoutMS);
    171     param->setter = intSetter;
    172 
    173     return (naettOption*)option;
    174 }
    175 
    176 naettOption* naettBody(const char* body, int size) {
    177     naettAlloc(InternalOption, option);
    178     option->numParams = 2;
    179 
    180     InternalParam* bodyParam = &option->params[0];
    181     InternalParam* sizeParam = &option->params[1];
    182 
    183     bodyParam->ptr = (void*)body;
    184     bodyParam->offset = offsetof(RequestOptions, body) + offsetof(Buffer, data);
    185     bodyParam->setter = ptrSetter;
    186 
    187     sizeParam->integer = size;
    188     sizeParam->offset = offsetof(RequestOptions, body) + offsetof(Buffer, size);
    189     sizeParam->setter = intSetter;
    190 
    191     return (naettOption*)option;
    192 }
    193 
    194 naettOption* naettBodyReader(naettReadFunc reader, void* userData) {
    195     naettAlloc(InternalOption, option);
    196     option->numParams = 2;
    197 
    198     InternalParam* readerParam = &option->params[0];
    199     InternalParam* dataParam = &option->params[1];
    200 
    201     readerParam->func = (void (*)(void)) reader;
    202     readerParam->offset = offsetof(RequestOptions, bodyReader);
    203     readerParam->setter = ptrSetter;
    204 
    205     dataParam->ptr = userData;
    206     dataParam->offset = offsetof(RequestOptions, bodyReaderData);
    207     dataParam->setter = ptrSetter;
    208 
    209     return (naettOption*)option;
    210 }
    211 
    212 naettOption* naettBodyWriter(naettWriteFunc writer, void* userData) {
    213     naettAlloc(InternalOption, option);
    214     option->numParams = 2;
    215 
    216     InternalParam* writerParam = &option->params[0];
    217     InternalParam* dataParam = &option->params[1];
    218 
    219     writerParam->func = (void(*)(void)) writer;
    220     writerParam->offset = offsetof(RequestOptions, bodyWriter);
    221     writerParam->setter = ptrSetter;
    222 
    223     dataParam->ptr = userData;
    224     dataParam->offset = offsetof(RequestOptions, bodyWriterData);
    225     dataParam->setter = ptrSetter;
    226 
    227     return (naettOption*)option;
    228 }
    229 
    230 void setupDefaultRW(InternalRequest* req) {
    231     if (req->options.bodyReader == NULL) {
    232         req->options.bodyReader = defaultBodyReader;
    233         req->options.bodyReaderData = (void*) &req->options.body;
    234     }
    235     if (req->options.bodyReader == defaultBodyReader) {
    236         req->options.body.position = 0;
    237     }
    238     if (req->options.bodyWriter == NULL) {
    239         req->options.bodyWriter = defaultBodyWriter;
    240     }
    241 }
    242 
    243 naettReq* naettRequest_va(const char* url, int numArgs, ...) {
    244     assert(url != NULL);
    245 
    246     va_list args;
    247     InternalOption* option;
    248     naettAlloc(InternalRequest, req);
    249     initRequest(req, url);
    250 
    251     va_start(args, numArgs);
    252     for (int i = 0; i < numArgs; i++) {
    253         option = va_arg(args, InternalOption*);
    254         applyOptionParams(req, option);
    255         free(option);
    256     }
    257     va_end(args);
    258 
    259     setupDefaultRW(req);
    260 
    261     if (naettPlatformInitRequest(req)) {
    262         return (naettReq*)req;
    263     }
    264 
    265     naettFree((naettReq*) req);
    266     return NULL;
    267 }
    268 
    269 naettReq* naettRequestWithOptions(const char* url, int numOptions, const naettOption** options) {
    270     assert(url != NULL);
    271     assert(numOptions == 0 || options != NULL);
    272 
    273     naettAlloc(InternalRequest, req);
    274     initRequest(req, url);
    275 
    276     for (int i = 0; i < numOptions; i++) {
    277         InternalOption* option = (InternalOption*)options[i];
    278         applyOptionParams(req, option);
    279         free(option);
    280     }
    281 
    282     setupDefaultRW(req);
    283 
    284     if (naettPlatformInitRequest(req)) {
    285         return (naettReq*)req;
    286     }
    287 
    288     naettFree((naettReq*) req);
    289     return NULL;
    290 }
    291 
    292 naettRes* naettMake(naettReq* request) {
    293     assert(initialized);
    294     assert(request != NULL);
    295 
    296     InternalRequest* req = (InternalRequest*)request;
    297     naettAlloc(InternalResponse, res);
    298     res->request = req;
    299 
    300     if (req->options.bodyWriter == defaultBodyWriter) {
    301         req->options.bodyWriterData = (void*) &res->body;
    302     }
    303 
    304     naettPlatformMakeRequest(res);
    305     return (naettRes*) res;
    306 }
    307 
    308 const void* naettGetBody(naettRes* response, int* size) {
    309     assert(response != NULL);
    310     assert(size != NULL);
    311 
    312     InternalResponse* res = (InternalResponse*)response;
    313     *size = res->body.size;
    314     return res->body.data;
    315 }
    316 
    317 int naettGetTotalBytesRead(naettRes* response, int* totalSize) {
    318     assert(response != NULL);
    319     assert(totalSize != NULL);
    320 
    321     InternalResponse* res = (InternalResponse*)response;
    322     *totalSize = res->contentLength;
    323     return res->totalBytesRead;
    324 }
    325 
    326 const char* naettGetHeader(naettRes* response, const char* name) {
    327     assert(response != NULL);
    328     assert(name != NULL);
    329 
    330     InternalResponse* res = (InternalResponse*)response;
    331     KVLink* node = res->headers;
    332     while (node) {
    333         if (strcasecmp(name, node->key) == 0) {
    334             return node->value;
    335         }
    336         node = node->next;
    337     }
    338     return NULL;
    339 }
    340 
    341 void naettListHeaders(naettRes* response, naettHeaderLister lister, void* userData) {
    342     assert(response != NULL);
    343     assert(lister != NULL);
    344 
    345     InternalResponse* res = (InternalResponse*)response;
    346     KVLink* node = res->headers;
    347     while (node) {
    348         if (!lister(node->key, node->value, userData)) {
    349             return;
    350         }
    351         node = node->next;
    352     }
    353 }
    354 
    355 naettReq* naettGetRequest(naettRes* response) {
    356     assert(response != NULL);
    357     InternalResponse* res = (InternalResponse*)response;
    358     return (naettReq*) res->request;
    359 }
    360 
    361 int naettComplete(const naettRes* response) {
    362     assert(response != NULL);
    363     InternalResponse* res = (InternalResponse*)response;
    364     return res->complete;
    365 }
    366 
    367 int naettGetStatus(const naettRes* response) {
    368     assert(response != NULL);
    369     InternalResponse* res = (InternalResponse*)response;
    370     return res->code;
    371 }
    372 
    373 static void freeKVList(KVLink* node) {
    374     while (node != NULL) {
    375         free((void*) node->key);
    376         free((void*) node->value);
    377         KVLink* next = node->next;
    378         free(node);
    379         node = next;
    380     }
    381 }
    382 
    383 void naettFree(naettReq* request) {
    384     assert(request != NULL);
    385 
    386     InternalRequest* req = (InternalRequest*)request;
    387     naettPlatformFreeRequest(req);
    388     KVLink* node = req->options.headers;
    389     freeKVList(node);
    390     free((void*)req->options.method);
    391     free((void*)req->url);
    392     free(request);
    393 }
    394 
    395 void naettClose(naettRes* response) {
    396     assert(response != NULL);
    397 
    398     InternalResponse* res = (InternalResponse*)response;
    399     res->request = NULL;
    400     naettPlatformCloseResponse(res);
    401     KVLink* node = res->headers;
    402     freeKVList(node);
    403     free(res->body.data);
    404     free(response);
    405 }