Objectively
Object oriented framework for C.
Loading...
Searching...
No Matches
RESTClient.c
Go to the documentation of this file.
1/*
2 * Objectively: Ultra-lightweight object oriented framework for GNU C.
3 * Copyright (C) 2014 Jay Dolan <jay@jaydolan.com>
4 *
5 * This software is provided 'as-is', without any express or implied
6 * warranty. In no event will the authors be held liable for any damages
7 * arising from the use of this software.
8 *
9 * Permission is granted to anyone to use this software for any purpose,
10 * including commercial applications, and to alter it and redistribute it
11 * freely, subject to the following restrictions:
12 *
13 * 1. The origin of this software must not be misrepresented; you must not
14 * claim that you wrote the original software. If you use this software
15 * in a product, an acknowledgment in the product documentation would be
16 * appreciated but is not required.
17 *
18 * 2. Altered source versions must be plainly marked as such, and must not be
19 * misrepresented as being the original software.
20 *
21 * 3. This notice may not be removed or altered from any source distribution.
22 */
23
24#include <assert.h>
25#include <stdlib.h>
26
27#include "RESTClient.h"
28#include "URL.h"
29#include "URLRequest.h"
30#include "URLSessionDataTask.h"
31
32#define _Class _RESTClient
33
34#pragma mark - Async internals
35
40
41static void RESTClient_AsyncCompletion(URLSessionTask *task, bool success) {
42
43 RESTClient_AsyncState *state = task->data;
44 URLSessionDataTask *dataTask = (URLSessionDataTask *) task;
45 const int status = task->response ? task->response->httpStatusCode : 0;
46
47 if (state->completion) {
48 state->completion(status, dataTask->data, state->userData);
49 }
50
51 free(state);
52 release(task);
53}
54
55#pragma mark - Request helpers
56
57static int request(RESTClient *self, HTTPMethod method, const char *url_string,
58 const Data *body, const char **headers, Data **out_data) {
59
60 if (out_data) {
61 *out_data = NULL;
62 }
63
65
66 if (method == HTTP_GET && !headers) {
67 URL *url = $(alloc(URL), initWithCharacters, url_string);
68 task = $(self->session, dataTaskWithURL, url, NULL);
69 release(url);
70 } else {
71 URL *url = $(alloc(URL), initWithCharacters, url_string);
73 release(url);
74 request->httpMethod = method;
75 if (body) {
76 request->httpBody = retain((Data *) body);
77 $(request, setValueForHTTPHeaderField, "application/json", "Content-Type");
78 }
79 if (headers) {
80 for (size_t i = 0; headers[i] && headers[i + 1]; i += 2) {
81 $(request, setValueForHTTPHeaderField, headers[i + 1], headers[i]);
82 }
83 }
84 task = $(self->session, dataTaskWithRequest, request, NULL);
86 }
87
88 $((URLSessionTask *) task, execute);
89
90 const int status = task->urlSessionTask.response->httpStatusCode;
91 if (out_data && task->data) {
92 *out_data = retain(task->data);
93 }
94
95 release(task);
96 return status;
97}
98
99static void requestAsync(RESTClient *self, HTTPMethod method, const char *url_string,
100 const Data *body, const char **headers, RESTClientCompletion completion, void *userData) {
101
102 URLSessionDataTask *task;
103
104 if (method == HTTP_GET && !headers) {
105 URL *url = $(alloc(URL), initWithCharacters, url_string);
107 release(url);
108 } else {
109 URL *url = $(alloc(URL), initWithCharacters, url_string);
111 release(url);
112 request->httpMethod = method;
113 if (body) {
114 request->httpBody = retain((Data *) body);
115 $(request, setValueForHTTPHeaderField, "application/json", "Content-Type");
116 }
117 if (headers) {
118 for (size_t i = 0; headers[i] && headers[i + 1]; i += 2) {
119 $(request, setValueForHTTPHeaderField, headers[i + 1], headers[i]);
120 }
121 }
124 }
125
126 RESTClient_AsyncState *state = calloc(1, sizeof(RESTClient_AsyncState));
127 state->completion = completion;
128 state->userData = userData;
129 task->urlSessionTask.data = state;
130
131 $((URLSessionTask *) task, resume);
132}
133
134#pragma mark - Object
135
139static void dealloc(Object *self) {
140
141 RESTClient *this = (RESTClient *) self;
142
143 release(this->session);
144
145 super(Object, self, dealloc);
146}
147
148#pragma mark - RESTClient
149
154static int del(RESTClient *self, const char *url, const char **headers, Data **data) {
155 return request(self, HTTP_DELETE, url, NULL, headers, data);
156}
157
162static void delAsync(RESTClient *self, const char *url, const char **headers,
163 RESTClientCompletion completion, void *userData) {
164 requestAsync(self, HTTP_DELETE, url, NULL, headers, completion, userData);
165}
166
171static int get(RESTClient *self, const char *url, const char **headers, Data **data) {
172 return request(self, HTTP_GET, url, NULL, headers, data);
173}
174
179static void getAsync(RESTClient *self, const char *url, const char **headers,
180 RESTClientCompletion completion, void *userData) {
181 requestAsync(self, HTTP_GET, url, NULL, headers, completion, userData);
182}
183
188static int head(RESTClient *self, const char *url, const char **headers) {
189 return request(self, HTTP_HEAD, url, NULL, headers, NULL);
190}
191
196static void headAsync(RESTClient *self, const char *url, const char **headers,
197 RESTClientCompletion completion, void *userData) {
198 requestAsync(self, HTTP_HEAD, url, NULL, headers, completion, userData);
199}
200
205static RESTClient *init(RESTClient *self) {
206 return $(self, initWithSession, $$(URLSession, sharedInstance));
207}
208
214
215 self = (RESTClient *) super(Object, self, init);
216 if (self) {
217 assert(session);
218 self->session = retain(session);
219 }
220 return self;
221}
222
227static int options(RESTClient *self, const char *url, const char **headers, Data **data) {
228 return request(self, HTTP_OPTIONS, url, NULL, headers, data);
229}
230
235static void optionsAsync(RESTClient *self, const char *url, const char **headers,
236 RESTClientCompletion completion, void *userData) {
237 requestAsync(self, HTTP_OPTIONS, url, NULL, headers, completion, userData);
238}
239
244static int patch(RESTClient *self, const char *url, const Data *body, const char **headers, Data **data) {
245 return request(self, HTTP_PATCH, url, body, headers, data);
246}
247
252static void patchAsync(RESTClient *self, const char *url, const Data *body, const char **headers,
253 RESTClientCompletion completion, void *userData) {
254 requestAsync(self, HTTP_PATCH, url, body, headers, completion, userData);
255}
256
261static int post(RESTClient *self, const char *url, const Data *body, const char **headers, Data **data) {
262 return request(self, HTTP_POST, url, body, headers, data);
263}
264
269static void postAsync(RESTClient *self, const char *url, const Data *body, const char **headers,
270 RESTClientCompletion completion, void *userData) {
271 requestAsync(self, HTTP_POST, url, body, headers, completion, userData);
272}
273
278static int put(RESTClient *self, const char *url, const Data *body, const char **headers, Data **data) {
279 return request(self, HTTP_PUT, url, body, headers, data);
280}
281
286static void putAsync(RESTClient *self, const char *url, const Data *body, const char **headers,
287 RESTClientCompletion completion, void *userData) {
288 requestAsync(self, HTTP_PUT, url, body, headers, completion, userData);
289}
290
292
298
299 static Once once;
300
301 do_once(&once, {
303 });
304
305 return _sharedInstance;
306}
307
308#pragma mark - Class lifecycle
309
310static void destroy(Class *clazz) {
312}
313
314static void initialize(Class *clazz) {
315
316 ((ObjectInterface *) clazz->interface)->dealloc = dealloc;
317
318 RESTClientInterface *rest = (RESTClientInterface *) clazz->interface;
319
320 rest->init = init;
322
323 rest->del = del;
324 rest->get = get;
325 rest->head = head;
326 rest->options = options;
327 rest->patch = patch;
328 rest->post = post;
329 rest->put = put;
330
331 rest->delAsync = delAsync;
332 rest->getAsync = getAsync;
333 rest->headAsync = headAsync;
334 rest->optionsAsync = optionsAsync;
335 rest->patchAsync = patchAsync;
336 rest->postAsync = postAsync;
337 rest->putAsync = putAsync;
338
339 rest->sharedInstance = sharedInstance;
340}
341
343
344 static Class *clazz;
345 static Once once;
346
347 do_once(&once, {
348 clazz = _initialize(&(const ClassDef) {
349 .name = "RESTClient",
350 .superclass = _Object(),
351 .instanceSize = sizeof(RESTClient),
352 .interfaceSize = sizeof(RESTClientInterface),
353 .initialize = initialize,
354 .destroy = destroy,
355 });
356 });
357
358 return clazz;
359}
360
361#undef _Class
ident release(ident obj)
Atomically decrement the given Object's reference count. If the resulting reference count is 0,...
Definition Class.c:356
Class * _initialize(const ClassDef *def)
Initializes the given Class.
Definition Class.c:151
ident retain(ident obj)
Atomically increment the given Object's reference count.
Definition Class.c:372
#define alloc(type)
Allocate and initialize and instance of type.
Definition Class.h:226
#define super(type, obj, method,...)
static Data * data(void)
Definition Data.c:286
Class * _Object(void)
Definition Object.c:136
static void resume(OperationQueue *self)
static int head(RESTClient *self, const char *url, const char **headers)
Definition RESTClient.c:188
static void destroy(Class *clazz)
Definition RESTClient.c:310
static void getAsync(RESTClient *self, const char *url, const char **headers, RESTClientCompletion completion, void *userData)
Definition RESTClient.c:179
static void putAsync(RESTClient *self, const char *url, const Data *body, const char **headers, RESTClientCompletion completion, void *userData)
Definition RESTClient.c:286
static void requestAsync(RESTClient *self, HTTPMethod method, const char *url_string, const Data *body, const char **headers, RESTClientCompletion completion, void *userData)
Definition RESTClient.c:99
static void optionsAsync(RESTClient *self, const char *url, const char **headers, RESTClientCompletion completion, void *userData)
Definition RESTClient.c:235
static void delAsync(RESTClient *self, const char *url, const char **headers, RESTClientCompletion completion, void *userData)
Definition RESTClient.c:162
static int del(RESTClient *self, const char *url, const char **headers, Data **data)
Definition RESTClient.c:154
static RESTClient * _sharedInstance
Definition RESTClient.c:291
static void patchAsync(RESTClient *self, const char *url, const Data *body, const char **headers, RESTClientCompletion completion, void *userData)
Definition RESTClient.c:252
static int put(RESTClient *self, const char *url, const Data *body, const char **headers, Data **data)
Definition RESTClient.c:278
static int request(RESTClient *self, HTTPMethod method, const char *url_string, const Data *body, const char **headers, Data **out_data)
Definition RESTClient.c:57
static RESTClient * init(RESTClient *self)
Definition RESTClient.c:205
static void postAsync(RESTClient *self, const char *url, const Data *body, const char **headers, RESTClientCompletion completion, void *userData)
Definition RESTClient.c:269
Class * _RESTClient(void)
Definition RESTClient.c:342
static void dealloc(Object *self)
Definition RESTClient.c:139
static RESTClient * initWithSession(RESTClient *self, URLSession *session)
Definition RESTClient.c:213
static int post(RESTClient *self, const char *url, const Data *body, const char **headers, Data **data)
Definition RESTClient.c:261
static int get(RESTClient *self, const char *url, const char **headers, Data **data)
Definition RESTClient.c:171
static int patch(RESTClient *self, const char *url, const Data *body, const char **headers, Data **data)
Definition RESTClient.c:244
static void initialize(Class *clazz)
Definition RESTClient.c:314
static void RESTClient_AsyncCompletion(URLSessionTask *task, bool success)
Definition RESTClient.c:41
static RESTClient * sharedInstance(void)
Definition RESTClient.c:297
static void headAsync(RESTClient *self, const char *url, const char **headers, RESTClientCompletion completion, void *userData)
Definition RESTClient.c:196
static int options(RESTClient *self, const char *url, const char **headers, Data **data)
Definition RESTClient.c:227
An HTTP REST client backed by URLSession.
static String * initWithCharacters(String *self, const char *chars)
Definition String.c:633
Uniform Resource Locators (RFC 3986).
static URLRequest * initWithURL(URLRequest *self, URL *url)
Definition URLRequest.c:150
static void setValueForHTTPHeaderField(URLRequest *self, const char *value, const char *field)
Definition URLRequest.c:169
A protocol-agnostic abstraction for requesting resources via URLs.
HTTPMethod
Definition URLRequest.h:42
@ HTTP_PUT
Definition URLRequest.h:48
@ HTTP_DELETE
Definition URLRequest.h:50
@ HTTP_GET
Definition URLRequest.h:46
@ HTTP_OPTIONS
Definition URLRequest.h:45
@ HTTP_POST
Definition URLRequest.h:47
@ HTTP_HEAD
Definition URLRequest.h:44
@ HTTP_PATCH
Definition URLRequest.h:49
static URLSessionDataTask * dataTaskWithRequest(URLSession *self, URLRequest *request, URLSessionTaskCompletion completion)
Definition URLSession.c:76
static URLSessionDataTask * dataTaskWithURL(URLSession *self, URL *url, URLSessionTaskCompletion completion)
Definition URLSession.c:103
static void execute(URLSessionTask *self)
Use data tasks to send and receive Data in-memory.
long Once
The Once type.
Definition Once.h:37
#define do_once(once, block)
Executes the given block at most one time.
Definition Once.h:43
void(* RESTClientCompletion)(int status, Data *data, void *userData)
A completion handler for asynchronous RESTClient requests.
Definition RESTClient.h:50
ClassDefs are passed to _initialize via an archetype to initialize a Class.
Definition Class.h:41
The runtime representation of a Class.
Definition Class.h:90
ident interface
The interface of the Class.
Definition Class.h:100
Data buffers.
Definition Data.h:50
Object is the root Class of The Objectively Class hierarchy.
Definition Object.h:46
RESTClientCompletion completion
Definition RESTClient.c:37
An HTTP REST client backed by URLSession.
Definition RESTClient.h:62
RESTClient * initWithSession(RESTClient *, URLSession *)
Initializes this RESTClient with the specified URLSession.
Definition RESTClient.c:213
URLSession * session
The URLSession backing this client.
Definition RESTClient.h:78
int del(RESTClient *, const char *, const char **, Data **)
Synchronously performs an HTTP DELETE request.
Definition RESTClient.c:154
Uniform Resource Locators (RFC 3986).
Definition URL.h:44
A protocol-agnostic abstraction for requesting resources via URLs.
Definition URLRequest.h:58
int httpStatusCode
The HTTP response status code.
Definition URLResponse.h:63
Use data tasks to send and receive Data in-memory.
Data * data
The data received.
URLSessionTask urlSessionTask
The superclass.
A management context for loading resources via URLs.
Definition URLSession.h:57
URL session tasks are handles to pending URL operations.
struct URLResponse * response
The response.
ident data
User data.