Objectively
Ultra-lightweight object oriented framework for GNU 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->user_data);
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, Data **out_data) {
59
60 if (out_data) {
61 *out_data = NULL;
62 }
63
65
66 if (method == HTTP_GET) {
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 task = $(self->session, dataTaskWithRequest, request, NULL);
81 }
82
83 $((URLSessionTask *) task, execute);
84
85 const int status = task->urlSessionTask.response->httpStatusCode;
86 if (out_data && task->data) {
87 *out_data = retain(task->data);
88 }
89
90 release(task);
91 return status;
92}
93
94static void requestAsync(RESTClient *self, HTTPMethod method, const char *url_string,
95 const Data *body, RESTClientCompletion completion, void *user_data) {
96
98
99 if (method == HTTP_GET) {
100 URL *url = $(alloc(URL), initWithCharacters, url_string);
102 release(url);
103 } else {
104 URL *url = $(alloc(URL), initWithCharacters, url_string);
106 release(url);
107 request->httpMethod = method;
108 if (body) {
109 request->httpBody = retain((Data *) body);
110 $(request, setValueForHTTPHeaderField, "application/json", "Content-Type");
111 }
114 }
115
116 RESTClient_AsyncState *state = calloc(1, sizeof(RESTClient_AsyncState));
117 state->completion = completion;
118 state->user_data = user_data;
119 task->urlSessionTask.data = state;
120
121 $((URLSessionTask *) task, resume);
122}
123
124#pragma mark - Object
125
129static void dealloc(Object *self) {
130
131 RESTClient *this = (RESTClient *) self;
132
133 release(this->session);
134
135 super(Object, self, dealloc);
136}
137
138#pragma mark - RESTClient
139
144static int get(RESTClient *self, const char *url, Data **data) {
145 return request(self, HTTP_GET, url, NULL, data);
146}
147
152static void getAsync(RESTClient *self, const char *url,
153 RESTClientCompletion completion, void *user_data) {
154 requestAsync(self, HTTP_GET, url, NULL, completion, user_data);
155}
156
161static int head(RESTClient *self, const char *url) {
162 return request(self, HTTP_HEAD, url, NULL, NULL);
163}
164
169static void headAsync(RESTClient *self, const char *url,
170 RESTClientCompletion completion, void *user_data) {
171 requestAsync(self, HTTP_HEAD, url, NULL, completion, user_data);
172}
173
178static int httpDelete(RESTClient *self, const char *url, Data **data) {
179 return request(self, HTTP_DELETE, url, NULL, data);
180}
181
186static void httpDeleteAsync(RESTClient *self, const char *url,
187 RESTClientCompletion completion, void *user_data) {
188 requestAsync(self, HTTP_DELETE, url, NULL, completion, user_data);
189}
190
195static RESTClient *init(RESTClient *self) {
196 return $(self, initWithSession, $$(URLSession, sharedInstance));
197}
198
204
205 self = (RESTClient *) super(Object, self, init);
206 if (self) {
207 assert(session);
208 self->session = retain(session);
209 }
210 return self;
211}
212
217static int options(RESTClient *self, const char *url, Data **data) {
218 return request(self, HTTP_OPTIONS, url, NULL, data);
219}
220
225static void optionsAsync(RESTClient *self, const char *url,
226 RESTClientCompletion completion, void *user_data) {
227 requestAsync(self, HTTP_OPTIONS, url, NULL, completion, user_data);
228}
229
234static int patch(RESTClient *self, const char *url, const Data *body, Data **data) {
235 return request(self, HTTP_PATCH, url, body, data);
236}
237
242static void patchAsync(RESTClient *self, const char *url, const Data *body,
243 RESTClientCompletion completion, void *user_data) {
244 requestAsync(self, HTTP_PATCH, url, body, completion, user_data);
245}
246
251static int post(RESTClient *self, const char *url, const Data *body, Data **data) {
252 return request(self, HTTP_POST, url, body, data);
253}
254
259static void postAsync(RESTClient *self, const char *url, const Data *body,
260 RESTClientCompletion completion, void *user_data) {
261 requestAsync(self, HTTP_POST, url, body, completion, user_data);
262}
263
268static int put(RESTClient *self, const char *url, const Data *body, Data **data) {
269 return request(self, HTTP_PUT, url, body, data);
270}
271
276static void putAsync(RESTClient *self, const char *url, const Data *body,
277 RESTClientCompletion completion, void *user_data) {
278 requestAsync(self, HTTP_PUT, url, body, completion, user_data);
279}
280
282
288
289 static Once once;
290
291 do_once(&once, {
293 });
294
295 return _sharedInstance;
296}
297
298#pragma mark - Class lifecycle
299
300static void destroy(Class *clazz) {
302}
303
304static void initialize(Class *clazz) {
305
306 ((ObjectInterface *) clazz->interface)->dealloc = dealloc;
307
308 RESTClientInterface *rest = (RESTClientInterface *) clazz->interface;
309
310 rest->init = init;
312
313 rest->httpDelete = httpDelete;
314 rest->head = head;
315 rest->get = get;
316 rest->patch = patch;
317 rest->post = post;
318 rest->put = put;
319 rest->options = options;
320
321 rest->httpDeleteAsync = httpDeleteAsync;
322 rest->headAsync = headAsync;
323 rest->getAsync = getAsync;
324 rest->patchAsync = patchAsync;
325 rest->postAsync = postAsync;
326 rest->putAsync = putAsync;
327 rest->optionsAsync = optionsAsync;
328
329 rest->sharedInstance = sharedInstance;
330}
331
333
334 static Class *clazz;
335 static Once once;
336
337 do_once(&once, {
338 clazz = _initialize(&(const ClassDef) {
339 .name = "RESTClient",
340 .superclass = _Object(),
341 .instanceSize = sizeof(RESTClient),
342 .interfaceOffset = offsetof(RESTClient, interface),
343 .interfaceSize = sizeof(RESTClientInterface),
345 .destroy = destroy,
346 });
347 });
348
349 return clazz;
350}
351
352#undef _Class
ident release(ident obj)
Atomically decrement the given Object's reference count. If the resulting reference count is 0,...
Definition Class.c:195
Class * _initialize(const ClassDef *def)
Initializes the given Class.
Definition Class.c:86
ident retain(ident obj)
Atomically increment the given Object's reference count.
Definition Class.c:210
#define alloc(type)
Allocate and initialize and instance of type.
Definition Class.h:176
#define super(type, obj, method,...)
static Data * data(void)
Definition Data.c:286
Class * _Object(void)
Definition Object.c:136
static void destroy(Class *clazz)
Definition RESTClient.c:300
static int request(RESTClient *self, HTTPMethod method, const char *url_string, const Data *body, Data **out_data)
Definition RESTClient.c:57
static int put(RESTClient *self, const char *url, const Data *body, Data **data)
Definition RESTClient.c:268
static void headAsync(RESTClient *self, const char *url, RESTClientCompletion completion, void *user_data)
Definition RESTClient.c:169
static void putAsync(RESTClient *self, const char *url, const Data *body, RESTClientCompletion completion, void *user_data)
Definition RESTClient.c:276
static int get(RESTClient *self, const char *url, Data **data)
Definition RESTClient.c:144
static int patch(RESTClient *self, const char *url, const Data *body, Data **data)
Definition RESTClient.c:234
static void patchAsync(RESTClient *self, const char *url, const Data *body, RESTClientCompletion completion, void *user_data)
Definition RESTClient.c:242
static void postAsync(RESTClient *self, const char *url, const Data *body, RESTClientCompletion completion, void *user_data)
Definition RESTClient.c:259
static RESTClient * _sharedInstance
Definition RESTClient.c:281
static int httpDelete(RESTClient *self, const char *url, Data **data)
Definition RESTClient.c:178
static int options(RESTClient *self, const char *url, Data **data)
Definition RESTClient.c:217
static RESTClient * init(RESTClient *self)
Definition RESTClient.c:195
static void getAsync(RESTClient *self, const char *url, RESTClientCompletion completion, void *user_data)
Definition RESTClient.c:152
static int head(RESTClient *self, const char *url)
Definition RESTClient.c:161
static void httpDeleteAsync(RESTClient *self, const char *url, RESTClientCompletion completion, void *user_data)
Definition RESTClient.c:186
static void optionsAsync(RESTClient *self, const char *url, RESTClientCompletion completion, void *user_data)
Definition RESTClient.c:225
Class * _RESTClient(void)
Definition RESTClient.c:332
static void dealloc(Object *self)
Definition RESTClient.c:129
static RESTClient * initWithSession(RESTClient *self, URLSession *session)
Definition RESTClient.c:203
static void initialize(Class *clazz)
Definition RESTClient.c:304
static void RESTClient_AsyncCompletion(URLSessionTask *task, bool success)
Definition RESTClient.c:41
static RESTClient * sharedInstance(void)
Definition RESTClient.c:287
static void requestAsync(RESTClient *self, HTTPMethod method, const char *url_string, const Data *body, RESTClientCompletion completion, void *user_data)
Definition RESTClient.c:94
static int post(RESTClient *self, const char *url, const Data *body, Data **data)
Definition RESTClient.c:251
An HTTP REST client backed by URLSession.
static String * initWithCharacters(String *self, const char *chars)
Definition String.c:638
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 resume(URLSessionTask *self)
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 *user_data)
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:95
ident interface
The interface of the Class.
Definition Class.h:105
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:61
RESTClient * initWithSession(RESTClient *, URLSession *)
Initializes this RESTClient with the specified URLSession.
Definition RESTClient.c:203
URLSession * session
The URLSession backing this client.
Definition RESTClient.h:77
int httpDelete(RESTClient *, const char *, Data **)
Synchronously performs an HTTP DELETE request.
Definition RESTClient.c:178
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.