commit ec312d7722d9094e552f1c07b2a5baf6a2f4f366
parent b298892985d59c713a4593847c367d902232304f
Author: Erik Agsjö <erik.agsjo@gmail.com>
Date: Sun, 14 Nov 2021 20:01:41 +0100
Default body reader, better free, et.c.
Diffstat:
6 files changed, 213 insertions(+), 27 deletions(-)
diff --git a/naett.c b/naett.c
@@ -42,6 +42,7 @@ typedef struct Buffer {
void* data;
int size;
int capacity;
+ int position;
} Buffer;
typedef struct {
@@ -69,6 +70,7 @@ typedef struct {
typedef struct {
InternalRequest* request;
int code;
+ int complete;
KVLink* headers;
Buffer body;
#if __APPLE__
@@ -80,7 +82,7 @@ typedef struct {
#endif
} InternalResponse;
-void naettPlatformInit(void* initThing);
+void naettPlatformInit(naettInitData initData);
int naettPlatformInitRequest(InternalRequest* req);
void naettPlatformMakeRequest(InternalResponse* res);
void naettPlatformFreeRequest(InternalRequest* req);
@@ -151,6 +153,18 @@ static void kvSetter(InternalParamPtr param, InternalRequest* req) {
*kvField = newNode;
}
+static int defaultBodyReader(void* dest, int bufferSize, void* userData) {
+ Buffer* buffer = (Buffer*) userData;
+ int bytesToRead = buffer->size - buffer->position;
+ if (bytesToRead > bufferSize) {
+ bytesToRead = bufferSize;
+ }
+
+ memcpy(dest, buffer->data + buffer->position, bytesToRead);
+ buffer->position += bytesToRead;
+ return bytesToRead;
+}
+
static int defaultBodyWriter(const void* source, int bytes, void* userData) {
Buffer* buffer = (Buffer*) userData;
int newCapacity = buffer->capacity;
@@ -185,8 +199,8 @@ static void applyOptionParams(InternalRequest* req, InternalOption* option) {
// Public API
-void naettInit(void* initThing) {
- naettPlatformInit(initThing);
+void naettInit(naettInitData initData) {
+ naettPlatformInit(initData);
}
naettOption* naettMethod(const char* method) {
@@ -324,6 +338,13 @@ naettRes* naettMake(naettReq* request) {
InternalRequest* req = (InternalRequest*)request;
naettAlloc(InternalResponse, res);
res->request = req;
+ if (req->options.bodyReader == NULL) {
+ req->options.bodyReader = defaultBodyReader;
+ req->options.bodyReaderData = (void*) &req->options.body;
+ }
+ if (req->options.bodyReader == defaultBodyReader) {
+ req->options.body.position = 0;
+ }
if (req->options.bodyWriter == NULL) {
req->options.bodyWriter = defaultBodyWriter;
req->options.bodyWriterData = (void*) &res->body;
@@ -352,7 +373,7 @@ const char* naettGetHeader(naettRes* response, const char* name) {
int naettComplete(const naettRes* response) {
InternalResponse* res = (InternalResponse*)response;
- return res->code != 0;
+ return res->complete;
}
int naettGetStatus(const naettRes* response) {
@@ -360,9 +381,28 @@ int naettGetStatus(const naettRes* response) {
return res->code;
}
+static void freeKVList(KVLink* node) {
+ while (node != NULL) {
+ free(node->key);
+ free(node->value);
+ KVLink* next = node->next;
+ free(node);
+ node = next;
+ }
+}
+
void naettFree(naettReq* request) {
InternalRequest* req = (InternalRequest*)request;
naettPlatformFreeRequest(req);
+ if (req->options.body.data != NULL) {
+ free(req->options.body.data);
+ }
+ KVLink* node = req->options.headers;
+ freeKVList(node);
+ if (req->options.body.data != NULL) {
+ free(req->options.body.data);
+ }
+ free(req->url);
free(request);
}
@@ -370,6 +410,11 @@ void naettClose(naettRes* response) {
InternalResponse* res = (InternalResponse*)response;
res->request = NULL;
naettPlatformCloseResponse(res);
+ if (res->body.data != NULL) {
+ free(res->body.data);
+ }
+ KVLink* node = res->headers;
+ freeKVList(node);
free(response);
}
// End of inlined naett_core.c //
@@ -445,6 +490,9 @@ void naettClose(naettRes* response) {
#include <stdlib.h>
#include <string.h>
+void naettPlatformInit(naettInitData initData) {
+}
+
int 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);
@@ -469,16 +517,23 @@ int naettPlatformInitRequest(InternalRequest* req) {
header = header->next;
}
- if (req->options.body.data) {
- Buffer* body = &req->options.body;
+ const int bufSize = 10240;
+ char byteBuffer[bufSize];
+ int bytesRead = 0;
+
+ if (req->options.bodyReader != NULL) {
+ id bodyData = objc_msgSend_t(id, NSUInteger)(class("NSMutableData"), sel("dataWithCapacity"), bufSize);
+ do {
+ bytesRead = req->options.bodyReader(byteBuffer, bufSize, req->options.bodyReaderData);
+ objc_msgSend_t(void, const void*, NSUInteger)(bodyData, sel("appendBytes:length:"), byteBuffer, bytesRead);
+ } while (bytesRead > 0);
- 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);
release(bodyData);
}
req->urlRequest = request;
+ return 1;
}
void didReceiveData(id self, SEL _sel, id session, id dataTask, id data) {
@@ -531,7 +586,9 @@ static id createDelegate() {
return delegate;
}
-void naettPlatformMakeRequest(InternalRequest* req, InternalResponse* res) {
+void naettPlatformMakeRequest(InternalResponse* res) {
+ InternalRequest* req = res->request;
+
id config = objc_msgSend_id(class("NSURLSessionConfiguration"), sel("ephemeralSessionConfiguration"));
id delegate = createDelegate();
@@ -668,8 +725,8 @@ static jint intCall(JNIEnv* env, jobject instance, const char* method, const cha
return result;
}
-void naettPlatformInit(void* initThing) {
- globalVM = (JavaVM*)initThing;
+void naettPlatformInit(naettInitData initData) {
+ globalVM = initData.vm;
}
int naettPlatformInitRequest(InternalRequest* req) {
@@ -810,6 +867,7 @@ static void* processRequest(void* data) {
voidCall(env, inputStream, "close", "()V");
res->code = statusCode;
+ res->complete = 1;
finally:
(*env)->PopLocalFrame(env, NULL);
diff --git a/naett.h b/naett.h
@@ -5,7 +5,21 @@
extern "C" {
#endif
-void naettInit(void* initThing);
+#if __ANDROID__
+#include <jni.h>
+#endif
+
+#if __ANDROID__
+typedef JavaVM* naettInitData;
+#else
+typedef void* naettInitData;
+#endif
+
+/**
+ * @brief Global init method.
+ * Call to initialize the library.
+ */
+void naettInit(naettInitData initThing);
typedef struct naettReq naettReq;
typedef struct naettRes naettRes;
@@ -13,30 +27,87 @@ typedef struct naettOption naettOption;
typedef int (*naettReadFunc)(void* dest, int bufferSize, void* userData);
typedef int (*naettWriteFunc)(const void* source, int bytes, void* userData);
+// Sets request method. Defaults to "GET".
naettOption* naettMethod(const char* method);
+// Adds a request header.
naettOption* naettHeader(const char* name, const char* value);
+// Sets the request body. Ignored if a body reader is configured.
naettOption* naettBody(const char* body, int size);
+// Sets a request body reader.
naettOption* naettBodyReader(naettReadFunc reader, void* userData);
+// Sets a response body writer.
naettOption* naettBodyWriter(naettWriteFunc writer, void* userData);
+// Sets timeout, used both for connection and read.
naettOption* naettTimeout(int milliSeconds);
+/**
+ * @brief Creates a new request to the specified url.
+ * Use varargs options to configure the connection and following request.
+ */
#define naettRequest(url, ...) naettRequest_va(url, countOptions(__VA_ARGS__), ##__VA_ARGS__)
+
+/**
+ * @brief Creates a new request to the specified url.
+ * Uses an array of options rather that varargs.
+ */
naettReq* naettRequestWithOptions(const char* url, int numOptions, const naettOption** options);
-void naettFree(naettReq* request);
+
+/**
+ * @brief Makes a request and returns a response object.
+ * The actual request is processed asynchronously, use `naettComplete`
+ * to check if the response is completed.
+ *
+ * A request object can be reused multiple times to make requests, but
+ * there can be only one active request using the same request object.
+ */
naettRes* naettMake(naettReq* request);
+/**
+ * @brief Frees a previously allocated request object.
+ * The request must not have any pending responses.
+ */
+void naettFree(naettReq* request);
+
+/**
+ * @brief Checks if a response is complete, with a result
+ * or with an error.
+ * Use `naettGetStatus` to get the status.
+ */
int naettComplete(const naettRes* response);
-enum {
+
+enum naettStatus {
naettConnectionError = -1,
naettProtocolError = -2,
naettReadError = -3,
- naettConnecting = 0,
+ naettProcessing = 0,
};
+
+/**
+ * @brief Returns the status of a response.
+ * Status codes > 0 are HTTP status codes as returned by the server.
+ * Status codes < 0 are processing errors according to the `naettStatus`
+ * enum values.
+ */
int naettGetStatus(const naettRes* response);
-const void* naettGetBody(naettRes* response, int* size);
+
+/**
+ * @brief Returns the response body.
+ * The body returned by this method is always empty when a custom
+ * body reader has been set up using the `naettBodyReader` option.
+ */
+const void* naettGetBody(naettRes* response, int* outSize);
+
+/**
+ * @brief Returns the HTTP header value for the specified header name.
+ */
const char* naettGetHeader(naettRes* response, const char* name);
+
+/**
+ * @brief Closes a response object.
+ */
void naettClose(naettRes* response);
+// Varargs glue
naettReq* naettRequest_va(const char* url, int numOptions, ...);
#define countOptions(...) ((sizeof((void*[]){ __VA_ARGS__ }) / sizeof(void*)))
diff --git a/src/naett_android.c b/src/naett_android.c
@@ -83,8 +83,8 @@ static jint intCall(JNIEnv* env, jobject instance, const char* method, const cha
return result;
}
-void naettPlatformInit(void* initThing) {
- globalVM = (JavaVM*)initThing;
+void naettPlatformInit(naettInitData initData) {
+ globalVM = initData.vm;
}
int naettPlatformInitRequest(InternalRequest* req) {
@@ -225,6 +225,7 @@ static void* processRequest(void* data) {
voidCall(env, inputStream, "close", "()V");
res->code = statusCode;
+ res->complete = 1;
finally:
(*env)->PopLocalFrame(env, NULL);
diff --git a/src/naett_core.c b/src/naett_core.c
@@ -61,6 +61,18 @@ static void kvSetter(InternalParamPtr param, InternalRequest* req) {
*kvField = newNode;
}
+static int defaultBodyReader(void* dest, int bufferSize, void* userData) {
+ Buffer* buffer = (Buffer*) userData;
+ int bytesToRead = buffer->size - buffer->position;
+ if (bytesToRead > bufferSize) {
+ bytesToRead = bufferSize;
+ }
+
+ memcpy(dest, buffer->data + buffer->position, bytesToRead);
+ buffer->position += bytesToRead;
+ return bytesToRead;
+}
+
static int defaultBodyWriter(const void* source, int bytes, void* userData) {
Buffer* buffer = (Buffer*) userData;
int newCapacity = buffer->capacity;
@@ -95,8 +107,8 @@ static void applyOptionParams(InternalRequest* req, InternalOption* option) {
// Public API
-void naettInit(void* initThing) {
- naettPlatformInit(initThing);
+void naettInit(naettInitData initData) {
+ naettPlatformInit(initData);
}
naettOption* naettMethod(const char* method) {
@@ -234,6 +246,13 @@ naettRes* naettMake(naettReq* request) {
InternalRequest* req = (InternalRequest*)request;
naettAlloc(InternalResponse, res);
res->request = req;
+ if (req->options.bodyReader == NULL) {
+ req->options.bodyReader = defaultBodyReader;
+ req->options.bodyReaderData = (void*) &req->options.body;
+ }
+ if (req->options.bodyReader == defaultBodyReader) {
+ req->options.body.position = 0;
+ }
if (req->options.bodyWriter == NULL) {
req->options.bodyWriter = defaultBodyWriter;
req->options.bodyWriterData = (void*) &res->body;
@@ -262,7 +281,7 @@ const char* naettGetHeader(naettRes* response, const char* name) {
int naettComplete(const naettRes* response) {
InternalResponse* res = (InternalResponse*)response;
- return res->code != 0;
+ return res->complete;
}
int naettGetStatus(const naettRes* response) {
@@ -270,9 +289,28 @@ int naettGetStatus(const naettRes* response) {
return res->code;
}
+static void freeKVList(KVLink* node) {
+ while (node != NULL) {
+ free(node->key);
+ free(node->value);
+ KVLink* next = node->next;
+ free(node);
+ node = next;
+ }
+}
+
void naettFree(naettReq* request) {
InternalRequest* req = (InternalRequest*)request;
naettPlatformFreeRequest(req);
+ if (req->options.body.data != NULL) {
+ free(req->options.body.data);
+ }
+ KVLink* node = req->options.headers;
+ freeKVList(node);
+ if (req->options.body.data != NULL) {
+ free(req->options.body.data);
+ }
+ free(req->url);
free(request);
}
@@ -280,5 +318,10 @@ void naettClose(naettRes* response) {
InternalResponse* res = (InternalResponse*)response;
res->request = NULL;
naettPlatformCloseResponse(res);
+ if (res->body.data != NULL) {
+ free(res->body.data);
+ }
+ KVLink* node = res->headers;
+ freeKVList(node);
free(response);
}
diff --git a/src/naett_internal.h b/src/naett_internal.h
@@ -38,6 +38,7 @@ typedef struct Buffer {
void* data;
int size;
int capacity;
+ int position;
} Buffer;
typedef struct {
@@ -65,6 +66,7 @@ typedef struct {
typedef struct {
InternalRequest* request;
int code;
+ int complete;
KVLink* headers;
Buffer body;
#if __APPLE__
@@ -76,7 +78,7 @@ typedef struct {
#endif
} InternalResponse;
-void naettPlatformInit(void* initThing);
+void naettPlatformInit(naettInitData initData);
int naettPlatformInitRequest(InternalRequest* req);
void naettPlatformMakeRequest(InternalResponse* res);
void naettPlatformFreeRequest(InternalRequest* req);
diff --git a/src/naett_osx.c b/src/naett_osx.c
@@ -6,6 +6,9 @@
#include <stdlib.h>
#include <string.h>
+void naettPlatformInit(naettInitData initData) {
+}
+
int 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);
@@ -30,11 +33,17 @@ int naettPlatformInitRequest(InternalRequest* req) {
header = header->next;
}
- if (req->options.body.data) {
- Buffer* body = &req->options.body;
+ const int bufSize = 10240;
+ char byteBuffer[bufSize];
+ int bytesRead = 0;
+
+ if (req->options.bodyReader != NULL) {
+ id bodyData = objc_msgSend_t(id, NSUInteger)(class("NSMutableData"), sel("dataWithCapacity"), bufSize);
+ do {
+ bytesRead = req->options.bodyReader(byteBuffer, bufSize, req->options.bodyReaderData);
+ objc_msgSend_t(void, const void*, NSUInteger)(bodyData, sel("appendBytes:length:"), byteBuffer, bytesRead);
+ } while (bytesRead > 0);
- 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);
release(bodyData);
}
@@ -93,7 +102,9 @@ static id createDelegate() {
return delegate;
}
-void naettPlatformMakeRequest(InternalRequest* req, InternalResponse* res) {
+void naettPlatformMakeRequest(InternalResponse* res) {
+ InternalRequest* req = res->request;
+
id config = objc_msgSend_id(class("NSURLSessionConfiguration"), sel("ephemeralSessionConfiguration"));
id delegate = createDelegate();