commit c4b61b00173bb29c7a6d90e695dd3ebf7b66c0f8
Author: Erik Agsjö <erik.agsjo@gmail.com>
Date: Wed, 10 Nov 2021 09:29:05 +0100
First!
Diffstat:
| A | Makefile | | | 16 | ++++++++++++++++ |
| A | main.c | | | 10 | ++++++++++ |
| A | naett.c | | | 158 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | naett.h | | | 28 | ++++++++++++++++++++++++++++ |
| A | naett_internal.h | | | 65 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | naett_objc.h | | | 60 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | naett_osx.c | | | 73 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
7 files changed, 410 insertions(+), 0 deletions(-)
diff --git a/Makefile b/Makefile
@@ -0,0 +1,16 @@
+CFLAGS = -g
+#CFLAGS = -O2
+
+ifeq ($(OS),Windows_NT)
+
+else
+ UNAME_S := $(shell uname -s)
+ ifeq ($(UNAME_S),Darwin)
+ LDFLAGS = -framework Cocoa
+ else ifeq ($(UNAME_S),Linux)
+ #LDFLAGS = -s -lGLU -lGL -lX11
+ endif
+endif
+
+naett: main.c naett.c naett_osx.c
+ gcc $^ -o $@ $(CFLAGS) $(LDFLAGS)
diff --git a/main.c b/main.c
@@ -0,0 +1,10 @@
+#include "naett.h"
+#include <unistd.h>
+
+int main(int argc, char** argv) {
+ naettReq* req = naettRequest("http://www.dn.se", naettMethod("GET"), naettHeader("content-type", "application/json"));
+ naettRes* res = naettMake(req);
+ while (!naettComplete(res)) {
+ usleep(100*1000);
+ }
+}
diff --git a/naett.c b/naett.c
@@ -0,0 +1,158 @@
+#include "naett_internal.h"
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+
+typedef struct InternalOption* InternalOptionPtr;
+typedef void (*OptionSetter)(InternalOptionPtr option, InternalRequest* req);
+
+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;
+ };
+} InternalOption;
+
+void stringSetter(InternalOptionPtr option, InternalRequest* req) {
+ char* stringCopy = strdup(option->string);
+ char* opaque = (char*) &req->options;
+ char** stringField = (char**) (opaque + option->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;
+}
+
+void ptrSetter(InternalOptionPtr option, InternalRequest* req) {
+ char* opaque = (char*) &req->options;
+ void** ptrField = (void**) (opaque + option->offset);
+ *ptrField = option->ptr;
+}
+
+void kvSetter(InternalOptionPtr option, InternalRequest* req) {
+ char* opaque = (char*) &req->options;
+ KVLink** kvField = (KVLink**) (opaque + option->offset);
+
+ naettAlloc(KVLink, newNode);
+ newNode->key = strdup(option->kv.key);
+ newNode->value = strdup(option->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;
+}
+
+void initRequest(InternalRequest* req, const char* url) {
+ req->options.method = strdup("GET");
+ req->url = strdup(url);
+}
+
+// Public API
+
+naettOption* naettMethod(const char* method) {
+ naettAlloc(InternalOption, option);
+ option->string = method;
+ option->offset = offsetof(RequestOptions, method);
+ option->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;
+}
+
+naettOption* naettTimeout(int timeoutMS) {
+ naettAlloc(InternalOption, option);
+ option->integer = timeoutMS;
+ option->offset = offsetof(RequestOptions, timeoutMS);
+ option->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;
+}
+
+naettOption* naettBodyReader(naettReadFunc reader) {
+ naettAlloc(InternalOption, option);
+ option->ptr = reader;
+ option->offset = offsetof(RequestOptions, bodyReader);
+ option->setter = ptrSetter;
+ return (naettOption*) option;
+}
+
+naettOption* naettBodyWriter(naettWriteFunc writer) {
+ naettAlloc(InternalOption, option);
+ option->ptr = writer;
+ option->offset = offsetof(RequestOptions, bodyWriter);
+ option->setter = ptrSetter;
+ return (naettOption*) option;
+}
+
+naettReq* naettRequest_va(const char* url, int numArgs, ...) {
+ va_list args;
+ InternalOption* option;
+ naettAlloc(InternalRequest, req);
+ initRequest(req, url);
+
+ va_start(args, numArgs);
+ for (int i = 0; i < numArgs; i++) {
+ option = va_arg(args, InternalOption*);
+ option->setter(option, req);
+ free(option);
+ }
+ va_end(args);
+
+ naettPlatformInitRequest(req);
+ return (naettReq*) req;
+}
+
+naettReq* naettRequestWithOptions(const char* url, int numOptions, const naettOption** options) {
+ naettAlloc(InternalRequest, req);
+ initRequest(req, url);
+
+ for (int i = 0; i < numOptions; i++) {
+ InternalOption* option = (InternalOption*) options[i];
+ option->setter(option, req);
+ free(option);
+ }
+
+ naettPlatformInitRequest(req);
+ return (naettReq*) req;
+}
+
+int naettComplete(const naettRes* response) {
+ InternalResponse* res = (InternalResponse*) response;
+ return res->complete;
+}
diff --git a/naett.h b/naett.h
@@ -0,0 +1,28 @@
+#ifndef LIBNAETT_H
+#define LIBNAETT_H
+
+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);
+
+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* 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);
+naettRes* naettMake(naettReq* request);
+
+int naettComplete(const naettRes* response);
+void naettClose(naettRes* response);
+
+naettReq* naettRequest_va(const char* url, int numOptions, ...);
+#define countOptions(...) ((sizeof((void*[]){ __VA_ARGS__ }) / sizeof(void*)))
+
+#endif // LIBNAETT_H
diff --git a/naett_internal.h b/naett_internal.h
@@ -0,0 +1,65 @@
+#ifndef NAETT_INTERNAL_H
+#define NAETT_INTERNAL_H
+
+#include "naett.h"
+
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#define __WINDOWS__ 1
+#endif
+
+#if __linux__ && !__ANDROID__
+#define __LINUX__ 1
+#endif
+
+#ifdef __APPLE__
+#include "TargetConditionals.h"
+#include <objc/objc.h>
+#if TARGET_OS_IPHONE
+#define __IOS__ 1
+#else
+#define __MACOS__ 1
+#endif
+#endif
+
+#define naettAlloc(TYPE, VAR) TYPE* VAR = (TYPE*)calloc(1, sizeof(TYPE))
+
+typedef struct KVLink {
+ const char* key;
+ const char* value;
+ struct KVLink* next;
+} KVLink;
+
+typedef struct Buffer {
+ void* data;
+ int size;
+} Buffer;
+
+typedef struct {
+ const char* method;
+ int timeoutMS;
+ naettReadFunc bodyReader;
+ naettWriteFunc bodyWriter;
+ KVLink* headers;
+ Buffer body;
+} RequestOptions;
+
+typedef struct {
+ RequestOptions options;
+ const char* url;
+#if __APPLE__
+ id urlRequest;
+#endif
+} InternalRequest;
+
+typedef struct {
+ InternalRequest* request;
+ int complete;
+ int code;
+ Buffer body;
+} InternalResponse;
+
+void naettPlatformInitRequest(InternalRequest* req);
+
+#endif // NAETT_INTERNAL_H
diff --git a/naett_objc.h b/naett_objc.h
@@ -0,0 +1,60 @@
+#ifndef NAETT_OBJC_H
+#define NAETT_OBJC_H
+
+#if defined(__IOS__) || defined (__MACOS__)
+#include <assert.h>
+#include <math.h>
+
+#include <objc/NSObjCRuntime.h>
+#include <objc/message.h>
+#include <objc/objc.h>
+#include <objc/runtime.h>
+
+#if defined(__OBJC__) && __has_feature(objc_arc)
+#error "ARC is not supported"
+#endif
+
+// ABI is a bit different between platforms
+#ifdef __arm64__
+#define abi_objc_msgSend_stret objc_msgSend
+#else
+#define abi_objc_msgSend_stret objc_msgSend_stret
+#endif
+#ifdef __i386__
+#define abi_objc_msgSend_fpret objc_msgSend_fpret
+#else
+#define abi_objc_msgSend_fpret objc_msgSend
+#endif
+
+#define objc_msgSendSuper_t(RET, ...) ((RET(*)(struct objc_super*, SEL, ##__VA_ARGS__))objc_msgSendSuper)
+#define objc_msgSend_t(RET, ...) ((RET(*)(id, SEL, ##__VA_ARGS__))objc_msgSend)
+#define objc_msgSend_stret_t(RET, ...) ((RET(*)(id, SEL, ##__VA_ARGS__))abi_objc_msgSend_stret)
+#define objc_msgSend_id objc_msgSend_t(id)
+#define objc_msgSend_void objc_msgSend_t(void)
+#define objc_msgSend_void_id objc_msgSend_t(void, id)
+#define objc_msgSend_void_bool objc_msgSend_t(void, bool)
+
+#define sel(NAME) sel_registerName(NAME)
+#define class(NAME) ((id)objc_getClass(NAME))
+#define makeClass(NAME, SUPER) \
+ objc_allocateClassPair((Class)objc_getClass(SUPER), NAME, 0)
+
+// Check here to get the signature right: https://nshipster.com/type-encodings/
+#define addMethod(CLASS, NAME, IMPL, SIGNATURE) \
+ if (!class_addMethod(CLASS, sel(NAME), (IMP) (IMPL), (SIGNATURE))) assert(false)
+
+#define addIvar(CLASS, NAME, SIZE, SIGNATURE) \
+ if (!class_addIvar(CLASS, NAME, SIZE, rint(log2(SIZE)), SIGNATURE)) assert(false)
+
+#define objc_alloc(CLASS) objc_msgSend_id(class(CLASS), sel("alloc"))
+
+#if __LP64__ || NS_BUILD_32_LIKE_64
+#define NSIntegerEncoding "q"
+#define NSUIntegerEncoding "L"
+#else
+#define NSIntegerEncoding "i"
+#define NSUIntegerEncoding "I"
+#endif
+
+#endif // defined(__IOS__) || defined (__MACOS__)
+#endif // NAETT_OBJC_H
diff --git a/naett_osx.c b/naett_osx.c
@@ -0,0 +1,73 @@
+#include "naett_internal.h"
+
+#ifdef __MACOS__
+
+#include "naett_objc.h"
+#include <stdlib.h>
+
+void naettPlatformInitRequest(InternalRequest* req) {
+ id urlString = objc_msgSend_t(id, const char*)(class("NSString"), sel("stringWithUTF8String:"), req->url);
+ id url = objc_msgSend_t(id, id)(class("NSURL"), sel("URLWithString:"), urlString);
+ id request = objc_msgSend_t(id, id)(class("NSMutableURLRequest"), sel("requestWithURL:"), url);
+ objc_msgSend_t(void, double)(request, sel("setTimeoutInterval:"), (double)(req->options.timeoutMS) / 1000.0);
+ id methodString =
+ objc_msgSend_t(id, const char*)(class("NSString"), sel("stringWithUTF8String:"), req->options.method);
+ objc_msgSend_t(void, id)(request, sel("setHTTPMethod:"), methodString);
+
+ KVLink* header = req->options.headers;
+ while (header != NULL) {
+ id name = objc_msgSend_t(id, const char*)(class("NSString"), sel("stringWithUTF8String:"), header->key);
+ id value = objc_msgSend_t(id, const char*)(class("NSString"), sel("stringWithUTF8String:"), header->value);
+ objc_msgSend_t(void, id, id)(request, sel("setValue:forHTTPHeaderField:"), name, value);
+ header = header->next;
+ }
+
+ if (req->options.body.data) {
+ Buffer* body = &req->options.body;
+
+ id bodyData = objc_msgSend_t(id, void*, NSUInteger, BOOL)(
+ class("NSData"), sel("dataWithBytesNoCopy:length:freeWhenDone:"), body->data, body->size, NO);
+ objc_msgSend_t(void, id)(request, sel("setHTTPBody:"), bodyData);
+ }
+
+ req->urlRequest = request;
+}
+
+void didReceiveData(id self, SEL _sel, id session, id dataTask, id data) {
+ InternalResponse* res = NULL;
+ object_getInstanceVariable(self, "response", (void**)&res);
+}
+
+void didComplete(id self, SEL _sel, id session, id dataTask, id error) {
+ InternalResponse* res = NULL;
+ object_getInstanceVariable(self, "response", (void**)&res);
+ res->complete = 1;
+}
+
+naettRes* naettMake(naettReq* request) {
+ InternalRequest* req = (InternalRequest*)request;
+
+ Class TaskDelegateClass = objc_allocateClassPair((Class)objc_getClass("NSObject"), "TaskDelegate", 0);
+ addMethod(TaskDelegateClass, "URLSession:dataTask:didReceiveData:", didReceiveData, "v@:@@@");
+ addMethod(TaskDelegateClass, "URLSession:task:didCompleteWithError:", didComplete, "v@:@@@");
+ addIvar(TaskDelegateClass, "response", sizeof(void*), "^v");
+
+ id config = objc_msgSend_id(class("NSURLSessionConfiguration"), sel("ephemeralSessionConfiguration"));
+
+ id delegate = objc_msgSend_id((id)TaskDelegateClass, sel("alloc"));
+ delegate = objc_msgSend_id(delegate, sel("init"));
+
+ id session = objc_msgSend_t(id, id, id, id)(
+ 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);
+
+ objc_msgSend_void(task, sel("resume"));
+
+ return (naettRes*) response;
+}
+
+#endif // __MACOS__