commit 539143df7a74c3b27ca5a5646dc00876b37f2bfb
parent c4b61b00173bb29c7a6d90e695dd3ebf7b66c0f8
Author: Erik Agsjö <erik.agsjo@gmail.com>
Date: Wed, 10 Nov 2021 21:32:51 +0100
First Naive OSX implementation works!
Diffstat:
5 files changed, 235 insertions(+), 90 deletions(-)
diff --git a/main.c b/main.c
@@ -1,10 +1,16 @@
#include "naett.h"
#include <unistd.h>
+#include <stdio.h>
int main(int argc, char** argv) {
- naettReq* req = naettRequest("http://www.dn.se", naettMethod("GET"), naettHeader("content-type", "application/json"));
+ naettReq* req =
+ naettRequest("http://www.dn.se", naettMethod("GET"), naettHeader("content-type", "application/json"));
naettRes* res = naettMake(req);
while (!naettComplete(res)) {
- usleep(100*1000);
+ usleep(100 * 1000);
}
+
+ int bodyLength = 0;
+ const char* body = naettGetBody(res, &bodyLength);
+ printf("Got %d bytes of type %s\n", bodyLength, naettGetHeader(res, "content-type"));
}
diff --git a/naett.c b/naett.c
@@ -4,122 +4,186 @@
#include <stddef.h>
#include <string.h>
-typedef struct InternalOption* InternalOptionPtr;
-typedef void (*OptionSetter)(InternalOptionPtr option, InternalRequest* req);
+typedef struct InternalParam* InternalParamPtr;
+typedef void (*ParamSetter)(InternalParamPtr param, InternalRequest* req);
+
+typedef struct InternalParam {
+ ParamSetter setter;
+ int offset;
+ union {
+ int integer;
+ const char* string;
+ struct {
+ const char* key;
+ const char* value;
+ } kv;
+ void* ptr;
+ };
+} InternalParam;
typedef struct InternalOption {
- OptionSetter setter;
- int offset;
- union {
- int integer;
- const char* string;
- struct {
- const char* key;
- const char* value;
- } kv;
- void* ptr;
- Buffer buffer;
- };
+ #define maxParams 2
+ int numParams;
+ InternalParam params[maxParams];
} InternalOption;
-void stringSetter(InternalOptionPtr option, InternalRequest* req) {
- char* stringCopy = strdup(option->string);
- char* opaque = (char*) &req->options;
- char** stringField = (char**) (opaque + option->offset);
+static void stringSetter(InternalParamPtr param, InternalRequest* req) {
+ char* stringCopy = strdup(param->string);
+ char* opaque = (char*)&req->options;
+ char** stringField = (char**)(opaque + param->offset);
if (*stringField) {
free(*stringField);
}
*stringField = stringCopy;
}
-void intSetter(InternalOptionPtr option, InternalRequest* req) {
- char* opaque = (char*) &req->options;
- int* intField = (int*) (opaque + option->offset);
- *intField = option->integer;
+static void intSetter(InternalParamPtr param, InternalRequest* req) {
+ char* opaque = (char*)&req->options;
+ int* intField = (int*)(opaque + param->offset);
+ *intField = param->integer;
}
-void ptrSetter(InternalOptionPtr option, InternalRequest* req) {
- char* opaque = (char*) &req->options;
- void** ptrField = (void**) (opaque + option->offset);
- *ptrField = option->ptr;
+static void ptrSetter(InternalParamPtr param, InternalRequest* req) {
+ char* opaque = (char*)&req->options;
+ void** ptrField = (void**)(opaque + param->offset);
+ *ptrField = param->ptr;
}
-void kvSetter(InternalOptionPtr option, InternalRequest* req) {
- char* opaque = (char*) &req->options;
- KVLink** kvField = (KVLink**) (opaque + option->offset);
-
+static void kvSetter(InternalParamPtr param, InternalRequest* req) {
+ char* opaque = (char*)&req->options;
+ KVLink** kvField = (KVLink**)(opaque + param->offset);
+
naettAlloc(KVLink, newNode);
- newNode->key = strdup(option->kv.key);
- newNode->value = strdup(option->kv.value);
+ newNode->key = strdup(param->kv.key);
+ newNode->value = strdup(param->kv.value);
newNode->next = *kvField;
+
*kvField = newNode;
}
-void bufferSetter(InternalOptionPtr option, InternalRequest* req) {
- char* opaque = (char*) &req->options;
- Buffer* bufferField = (Buffer*) (opaque + option->offset);
-
- bufferField->data = option->buffer.data;
- bufferField->size = option->buffer.size;
+static int defaultBodyWriter(const void* source, int bytes, void* userData) {
+ Buffer* buffer = (Buffer*) userData;
+ int newCapacity = buffer->capacity;
+ if (newCapacity == 0) {
+ newCapacity = bytes;
+ }
+ while (newCapacity - buffer->size < bytes) {
+ newCapacity *= 2;
+ }
+ if (newCapacity != buffer->capacity) {
+ buffer->data = realloc(buffer->data, newCapacity);
+ buffer->capacity = newCapacity;
+ }
+ char* dest = ((char*)buffer->data) + buffer->size;
+ memcpy(dest, source, bytes);
+ buffer->size += bytes;
+ return bytes;
}
-void initRequest(InternalRequest* req, const char* url) {
+static void initRequest(InternalRequest* req, const char* url) {
req->options.method = strdup("GET");
req->url = strdup(url);
}
+static void applyOptionParams(InternalRequest* req, InternalOption* option) {
+ for (int j = 0; j < option->numParams; j++) {
+ InternalParam* param = option->params + j;
+ param->setter(param, req);
+ }
+}
+
// Public API
naettOption* naettMethod(const char* method) {
naettAlloc(InternalOption, option);
- option->string = method;
- option->offset = offsetof(RequestOptions, method);
- option->setter = stringSetter;
- return (naettOption*) option;
+ option->numParams = 1;
+ InternalParam* param = &option->params[0];
+
+ param->string = method;
+ param->offset = offsetof(RequestOptions, method);
+ param->setter = stringSetter;
+
+ return (naettOption*)option;
}
naettOption* naettHeader(const char* name, const char* value) {
naettAlloc(InternalOption, option);
- option->kv.key = name;
- option->kv.value = value;
- option->offset = offsetof(RequestOptions, headers);
- option->setter = kvSetter;
- return (naettOption*) option;
+ option->numParams = 1;
+ InternalParam* param = &option->params[0];
+
+ param->kv.key = name;
+ param->kv.value = value;
+ param->offset = offsetof(RequestOptions, headers);
+ param->setter = kvSetter;
+
+ return (naettOption*)option;
}
naettOption* naettTimeout(int timeoutMS) {
naettAlloc(InternalOption, option);
- option->integer = timeoutMS;
- option->offset = offsetof(RequestOptions, timeoutMS);
- option->setter = intSetter;
- return (naettOption*) option;
+ option->numParams = 1;
+ InternalParam* param = &option->params[0];
+
+ param->integer = timeoutMS;
+ param->offset = offsetof(RequestOptions, timeoutMS);
+ param->setter = intSetter;
+
+ return (naettOption*)option;
}
naettOption* naettBody(const char* body, int size) {
naettAlloc(InternalOption, option);
- option->buffer.data = (void*) body;
- option->buffer.size = size;
- option->offset = offsetof(RequestOptions, body);
- option->setter = bufferSetter;
- return (naettOption*) option;
+ option->numParams = 2;
+
+ InternalParam* bodyParam = &option->params[0];
+ InternalParam* sizeParam = &option->params[1];
+
+ bodyParam->ptr = (void*)body;
+ bodyParam->offset = offsetof(RequestOptions, body) + offsetof(Buffer, data);
+ bodyParam->setter = ptrSetter;
+
+ sizeParam->integer = size;
+ sizeParam->offset = offsetof(RequestOptions, body) + offsetof(Buffer, size);
+ sizeParam->setter = intSetter;
+
+ return (naettOption*)option;
}
-naettOption* naettBodyReader(naettReadFunc reader) {
+naettOption* naettBodyReader(naettReadFunc reader, void* userData) {
naettAlloc(InternalOption, option);
- option->ptr = reader;
- option->offset = offsetof(RequestOptions, bodyReader);
- option->setter = ptrSetter;
- return (naettOption*) option;
+ option->numParams = 2;
+
+ InternalParam* readerParam = &option->params[0];
+ InternalParam* dataParam = &option->params[1];
+
+ readerParam->ptr = reader;
+ readerParam->offset = offsetof(RequestOptions, bodyReader);
+ readerParam->setter = ptrSetter;
+
+ dataParam->ptr = reader;
+ dataParam->offset = offsetof(RequestOptions, bodyReaderData);
+ dataParam->setter = ptrSetter;
+
+ return (naettOption*)option;
}
-naettOption* naettBodyWriter(naettWriteFunc writer) {
+naettOption* naettBodyWriter(naettWriteFunc writer, void* userData) {
naettAlloc(InternalOption, option);
- option->ptr = writer;
- option->offset = offsetof(RequestOptions, bodyWriter);
- option->setter = ptrSetter;
- return (naettOption*) option;
-}
+ option->numParams = 2;
+
+ InternalParam* writerParam = &option->params[0];
+ InternalParam* dataParam = &option->params[1];
+ writerParam->ptr = writer;
+ writerParam->offset = offsetof(RequestOptions, bodyWriter);
+ writerParam->setter = ptrSetter;
+
+ dataParam->ptr = writer;
+ dataParam->offset = offsetof(RequestOptions, bodyWriterData);
+ dataParam->setter = ptrSetter;
+
+ return (naettOption*)option;
+}
naettReq* naettRequest_va(const char* url, int numArgs, ...) {
va_list args;
InternalOption* option;
@@ -129,13 +193,13 @@ naettReq* naettRequest_va(const char* url, int numArgs, ...) {
va_start(args, numArgs);
for (int i = 0; i < numArgs; i++) {
option = va_arg(args, InternalOption*);
- option->setter(option, req);
+ applyOptionParams(req, option);
free(option);
}
va_end(args);
naettPlatformInitRequest(req);
- return (naettReq*) req;
+ return (naettReq*)req;
}
naettReq* naettRequestWithOptions(const char* url, int numOptions, const naettOption** options) {
@@ -143,16 +207,59 @@ naettReq* naettRequestWithOptions(const char* url, int numOptions, const naettOp
initRequest(req, url);
for (int i = 0; i < numOptions; i++) {
- InternalOption* option = (InternalOption*) options[i];
- option->setter(option, req);
+ InternalOption* option = (InternalOption*)options[i];
+ applyOptionParams(req, option);
free(option);
}
naettPlatformInitRequest(req);
- return (naettReq*) req;
+ return (naettReq*)req;
+}
+
+naettRes* naettMake(naettReq* request) {
+ InternalRequest* req = (InternalRequest*)request;
+ naettAlloc(InternalResponse, res);
+ res->request = req;
+ if (req->options.bodyWriter == NULL) {
+ req->options.bodyWriter = defaultBodyWriter;
+ req->options.bodyWriterData = (void*) &res->body;
+ }
+ naettPlatformMakeRequest(req, res);
+ return (naettRes*) res;
+}
+
+const void* naettGetBody(naettRes* response, int* size) {
+ InternalResponse* res = (InternalResponse*)response;
+ *size = res->body.size;
+ return res->body.data;
+}
+
+const char* naettGetHeader(naettRes* response, const char* name) {
+ InternalResponse* res = (InternalResponse*)response;
+ KVLink* node = res->headers;
+ while (node) {
+ if (strcasecmp(name, node->key) == 0) {
+ return node->value;
+ }
+ node = node->next;
+ }
+ return NULL;
}
int naettComplete(const naettRes* response) {
- InternalResponse* res = (InternalResponse*) response;
+ InternalResponse* res = (InternalResponse*)response;
return res->complete;
}
+
+void naettFree(naettReq* request) {
+ InternalRequest* req = (InternalRequest*)request;
+ naettPlatformFreeRequest(req);
+ free(request);
+}
+
+void naettClose(naettRes* response) {
+ InternalResponse* res = (InternalResponse*)response;
+ res->request = NULL;
+ naettPlatformCloseResponse(res);
+ free(response);
+}
diff --git a/naett.h b/naett.h
@@ -4,22 +4,24 @@
typedef struct naettReq naettReq;
typedef struct naettRes naettRes;
typedef struct naettOption naettOption;
-typedef int (*naettReadFunc)(char* dest, int bufferSize);
-typedef int (*naettWriteFunc)(const char* source, int bytes);
+typedef int (*naettReadFunc)(void* dest, int bufferSize, void* userData);
+typedef int (*naettWriteFunc)(const void* source, int bytes, void* userData);
naettOption* naettMethod(const char* method);
naettOption* naettHeader(const char* name, const char* value);
naettOption* naettBody(const char* body, int size);
-naettOption* naettBodyReader(naettReadFunc reader);
-naettOption* naettBodyWriter(naettWriteFunc writer);
+naettOption* naettBodyReader(naettReadFunc reader, void* userData);
+naettOption* naettBodyWriter(naettWriteFunc writer, void* userData);
naettOption* naettTimeout(int milliSeconds);
#define naettRequest(url, ...) naettRequest_va(url, countOptions(__VA_ARGS__), ##__VA_ARGS__)
naettReq* naettRequestWithOptions(const char* url, int numOptions, const naettOption** options);
-void naettFreeRequest(naettReq* request);
+void naettFree(naettReq* request);
naettRes* naettMake(naettReq* request);
int naettComplete(const naettRes* response);
+const void* naettGetBody(naettRes* response, int* size);
+const char* naettGetHeader(naettRes* response, const char* name);
void naettClose(naettRes* response);
naettReq* naettRequest_va(const char* url, int numOptions, ...);
diff --git a/naett_internal.h b/naett_internal.h
@@ -34,13 +34,16 @@ typedef struct KVLink {
typedef struct Buffer {
void* data;
int size;
+ int capacity;
} Buffer;
typedef struct {
const char* method;
int timeoutMS;
naettReadFunc bodyReader;
+ void* bodyReaderData;
naettWriteFunc bodyWriter;
+ void* bodyWriterData;
KVLink* headers;
Buffer body;
} RequestOptions;
@@ -57,9 +60,13 @@ typedef struct {
InternalRequest* request;
int complete;
int code;
+ KVLink* headers;
Buffer body;
} InternalResponse;
void naettPlatformInitRequest(InternalRequest* req);
+void naettPlatformMakeRequest(InternalRequest* req, InternalResponse* res);
+void naettPlatformFreeRequest(InternalRequest* req);
+void naettPlatformCloseResponse(InternalResponse* res);
#endif // NAETT_INTERNAL_H
diff --git a/naett_osx.c b/naett_osx.c
@@ -4,6 +4,7 @@
#include "naett_objc.h"
#include <stdlib.h>
+#include <string.h>
void naettPlatformInitRequest(InternalRequest* req) {
id urlString = objc_msgSend_t(id, const char*)(class("NSString"), sel("stringWithUTF8String:"), req->url);
@@ -35,7 +36,30 @@ void naettPlatformInitRequest(InternalRequest* req) {
void didReceiveData(id self, SEL _sel, id session, id dataTask, id data) {
InternalResponse* res = NULL;
- object_getInstanceVariable(self, "response", (void**)&res);
+ object_getInstanceVariable(self, "response", (void**)&res);
+
+ if (res->headers == NULL) {
+ id response = objc_msgSend_t(id)(dataTask, sel("response"));
+ id allHeaders = objc_msgSend_t(id)(response, sel("allHeaderFields"));
+
+ NSUInteger headerCount = objc_msgSend_t(NSUInteger)(allHeaders, sel("count"));
+ id headerNames[headerCount];
+ id headerValues[headerCount];
+
+ objc_msgSend_t(NSInteger, id*, id*, NSUInteger)(
+ allHeaders, sel("getObjects:andKeys:count:"), headerValues, headerNames, headerCount);
+ for (int i = 0; i < headerCount; i++) {
+ naettAlloc(KVLink, node);
+ node->key = strdup(objc_msgSend_t(const char*)(headerNames[i], sel("UTF8String")));
+ node->value = strdup(objc_msgSend_t(const char*)(headerValues[i], sel("UTF8String")));
+ node->next = res->headers;
+ res->headers = node;
+ }
+ }
+
+ const void* bytes = objc_msgSend_t(const void*)(data, sel("bytes"));
+ NSUInteger length = objc_msgSend_t(NSUInteger)(data, sel("length"));
+ res->request->options.bodyWriter(bytes, length, res->request->options.bodyWriterData);
}
void didComplete(id self, SEL _sel, id session, id dataTask, id error) {
@@ -44,9 +68,7 @@ void didComplete(id self, SEL _sel, id session, id dataTask, id error) {
res->complete = 1;
}
-naettRes* naettMake(naettReq* request) {
- InternalRequest* req = (InternalRequest*)request;
-
+void naettPlatformMakeRequest(InternalRequest* req, InternalResponse* res) {
Class TaskDelegateClass = objc_allocateClassPair((Class)objc_getClass("NSObject"), "TaskDelegate", 0);
addMethod(TaskDelegateClass, "URLSession:dataTask:didReceiveData:", didReceiveData, "v@:@@@");
addMethod(TaskDelegateClass, "URLSession:task:didCompleteWithError:", didComplete, "v@:@@@");
@@ -61,13 +83,14 @@ naettRes* naettMake(naettReq* request) {
class("NSURLSession"), sel("sessionWithConfiguration:delegate:delegateQueue:"), config, delegate, nil);
id task = objc_msgSend_t(id, id)(session, sel("dataTaskWithRequest:"), req->urlRequest);
- naettAlloc(InternalResponse, response);
- response->request = req;
- object_setInstanceVariable(delegate, "response", (void*)response);
-
+ object_setInstanceVariable(delegate, "response", (void*)res);
objc_msgSend_void(task, sel("resume"));
+}
+
+void naettPlatformFreeRequest(InternalRequest* req) {
+}
- return (naettRes*) response;
+void naettPlatformCloseResponse(InternalResponse* res) {
}
#endif // __MACOS__