Objectively 1.0.0
Ultra-lightweight object oriented framework for GNU C.
URLSessionTask.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 <string.h>
26#include <stdlib.h>
27#include <curl/curl.h>
28
29#include "URLRequest.h"
30#include "URLResponse.h"
31#include "URLSession.h"
32#include "URLSessionTask.h"
33
34#define _Class _URLSessionTask
35
36#pragma mark - Object
37
41static Object *copy(const Object *self) {
42
43 return NULL;
44}
45
49static void dealloc(Object *self) {
50
51 URLSessionTask *this = (URLSessionTask *) self;
52
53 if (this->error) {
54 free(this->error);
55 }
56
57 release(this->request);
58 release(this->session);
59 release(this->response);
60
61 super(Object, self, dealloc);
62}
63
64#pragma mark - URLSessionTask
65
70static void cancel(URLSessionTask *self) {
71
72 switch (self->state) {
78 break;
79 default:
80 break;
81 }
82}
83
88static void execute(URLSessionTask *self) {
89
90 $(self, setup);
91
92 curl_easy_perform(self->locals.handle);
93
94 curl_easy_getinfo(self->locals.handle, CURLINFO_RESPONSE_CODE, (long *) &self->response->httpStatusCode);
95
97
98 $(self, teardown);
99}
100
106 struct URLSession *session, URLSessionTaskCompletion completion) {
107
108 assert(request);
109 assert(session);
110
111 self = (URLSessionTask *) super(Object, self, init);
112 if (self) {
113
114 self->error = calloc(1, CURL_ERROR_SIZE);
115 assert(self->error);
116
117 self->request = retain(request);
118 self->session = retain(session);
119
120 self->response = $(alloc(URLResponse), init);
121
122 self->completion = completion;
123
125 }
126
127 return self;
128}
129
134static void resume(URLSessionTask *self) {
135
136 switch (self->state) {
140 break;
141 default:
142 break;
143 }
144}
145
150
151 String *header = $(alloc(String), initWithFormat, "%s: %s", ((String * ) key)->chars, ((String * ) obj)->chars);
152
153 struct curl_slist **headers = (struct curl_slist **) data;
154 *headers = curl_slist_append(*headers, header->chars);
155
156 release(header);
157}
158
162static size_t responseHeader(char *buffer, size_t size, size_t count, void *data) {
163
165
166 char *header = calloc(count + 1, 1);
167 memcpy(header, buffer, count);
168
169 char *delim = strchr(header, ':');
170 if (delim) {
171 *delim = 0;
172
173 char *field = header;
174 char *value = strtrim(delim + 1);
175
176 $(this->response, setValueForHTTPHeaderField, value, field);
177
178 free(value);
179 }
180
181 free(header);
182 return count;
183}
184
190static int progress(ident self, curl_off_t bytesExpectedToReceive, curl_off_t bytesReceived,
191 curl_off_t bytesExpectedToSend, curl_off_t bytesSent) {
192
193 URLSessionTask *this = (URLSessionTask *) self;
194
195 curl_easy_getinfo(this->locals.handle, CURLINFO_RESPONSE_CODE, (long *) &this->response->httpStatusCode);
196
197 this->bytesExpectedToReceive = bytesExpectedToReceive;
198 this->bytesExpectedToSend = bytesExpectedToSend;
199
200 if (this->progress) {
201 this->progress(this);
202 }
203
204 return 0;
205}
206
211static void setup(URLSessionTask *self) {
212
213 self->locals.handle = curl_easy_init();
214 assert(self->locals.handle);
215
216 curl_easy_setopt(self->locals.handle, CURLOPT_ERRORBUFFER, self->error);
217 curl_easy_setopt(self->locals.handle, CURLOPT_FOLLOWLOCATION, true);
218
219 curl_easy_setopt(self->locals.handle, CURLOPT_HEADERFUNCTION, responseHeader);
220 curl_easy_setopt(self->locals.handle, CURLOPT_HEADERDATA, self);
221
222 curl_easy_setopt(self->locals.handle, CURLOPT_XFERINFOFUNCTION, progress);
223 curl_easy_setopt(self->locals.handle, CURLOPT_XFERINFODATA, self);
224
225 Data *body = self->request->httpBody;
226 if (body) {
227 curl_easy_setopt(self->locals.handle, CURLOPT_POSTFIELDS, body->bytes);
228 curl_easy_setopt(self->locals.handle, CURLOPT_POSTFIELDSIZE, body->length);
229 }
230
231 struct curl_slist *httpHeaders = NULL;
232 const Dictionary *headers = NULL;
233
234 headers = self->session->configuration->httpHeaders;
235 if (headers) {
236 $(headers, enumerateObjectsAndKeys, requestHeaders_enumerator, &httpHeaders);
237 }
238
239 headers = self->request->httpHeaders;
240 if (headers) {
241 $(headers, enumerateObjectsAndKeys, requestHeaders_enumerator, &httpHeaders);
242 }
243
244 curl_easy_setopt(self->locals.handle, CURLOPT_HTTPHEADER, httpHeaders);
245
246 self->locals.requestHeaders = httpHeaders;
247
248 switch (self->request->httpMethod) {
249 case HTTP_POST:
250 curl_easy_setopt(self->locals.handle, CURLOPT_POST, true);
251 break;
252 case HTTP_PUT:
253 curl_easy_setopt(self->locals.handle, CURLOPT_PUT, true);
254 break;
255 case HTTP_DELETE:
256 curl_easy_setopt(self->locals.handle, CURLOPT_CUSTOMREQUEST, "DELETE");
257 break;
258 case HTTP_HEAD:
259 curl_easy_setopt(self->locals.handle, CURLOPT_NOBODY, true);
260 break;
261 default:
262 break;
263 }
264
265 curl_easy_setopt(self->locals.handle, CURLOPT_URL, self->request->url->urlString->chars);
266}
267
272static void suspend(URLSessionTask *self) {
273
274 switch (self->state) {
278 break;
279 default:
280 break;
281 }
282}
283
288static void teardown(URLSessionTask *self) {
289
290 if (self->locals.handle) {
291 curl_easy_cleanup(self->locals.handle);
292 self->locals.handle = NULL;
293 }
294
295 if (self->locals.requestHeaders) {
296 curl_slist_free_all(self->locals.requestHeaders);
297 self->locals.requestHeaders = NULL;
298 }
299}
300
301#pragma mark - Class lifecycle
302
306static void initialize(Class *clazz) {
307
308 ((ObjectInterface *) clazz->interface)->copy = copy;
309 ((ObjectInterface *) clazz->interface)->dealloc = dealloc;
310
311 ((URLSessionTaskInterface *) clazz->interface)->cancel = cancel;
312 ((URLSessionTaskInterface *) clazz->interface)->execute = execute;
313 ((URLSessionTaskInterface *) clazz->interface)->initWithRequestInSession = initWithRequestInSession;
314 ((URLSessionTaskInterface *) clazz->interface)->resume = resume;
315 ((URLSessionTaskInterface *) clazz->interface)->setup = setup;
316 ((URLSessionTaskInterface *) clazz->interface)->suspend = suspend;
317 ((URLSessionTaskInterface *) clazz->interface)->teardown = teardown;
318}
319
325 static Class *clazz;
326 static Once once;
327
328 do_once(&once, {
329 clazz = _initialize(&(const ClassDef) {
330 .name = "URLSessionTask",
331 .superclass = _Object(),
332 .instanceSize = sizeof(URLSessionTask),
333 .interfaceOffset = offsetof(URLSessionTask, interface),
334 .interfaceSize = sizeof(URLSessionTaskInterface),
336 });
337 });
338
339 return clazz;
340}
341
342#undef _Class
ident release(ident obj)
Atomically decrement the given Object's reference count. If the resulting reference count is 0,...
Definition: Class.c:196
Class * _initialize(const ClassDef *def)
Initializes the given Class.
Definition: Class.c:91
ident retain(ident obj)
Atomically increment the given Object's reference count.
Definition: Class.c:211
#define obj
#define alloc(type)
Allocate and initialize and instance of type.
Definition: Class.h:159
#define super(type, obj, method,...)
char * strtrim(const char *s)
Copies the given null-terminated C string, trimming leading and trailing whitespace.
Definition: String.c:752
Immutable UTF-8 strings.
void * ident
The identity type, similar to Objective-C id.
Definition: Types.h:49
A protocol-agnostic abstraction for requesting resources via URLs.
@ HTTP_PUT
Definition: URLRequest.h:46
@ HTTP_DELETE
Definition: URLRequest.h:47
@ HTTP_POST
Definition: URLRequest.h:45
@ HTTP_HEAD
Definition: URLRequest.h:48
A protocol-agnostic abstraction for receiving resources via URLs.
A management context for loading resources via URLs.
static void requestHeaders_enumerator(const Dictionary *dictionary, ident obj, ident key, ident data)
A helper to populate the request headers list for CURL.
static int progress(ident self, curl_off_t bytesExpectedToReceive, curl_off_t bytesReceived, curl_off_t bytesExpectedToSend, curl_off_t bytesSent)
The CURLOPT_XFERINFOFUNCTION, which updates internal state and dispatches the task's progress functio...
static void initialize(Class *clazz)
static size_t responseHeader(char *buffer, size_t size, size_t count, void *data)
The CURLOPT_HEADERFUNCTION for parsing response headers.
URL session tasks are handles to pending URL operations.
@ URLSESSIONTASK_RESUMING
@ URLSESSIONTASK_SUSPENDING
@ URLSESSIONTASK_SUSPENDED
@ URLSESSIONTASK_COMPLETED
@ URLSESSIONTASK_RESUMED
@ URLSESSIONTASK_CANCELING
void(* URLSessionTaskCompletion)(URLSessionTask *task, _Bool success)
A function pointer for URLSessionTask completion.
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
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
Condition * init(Condition *self)
Initializes this Condition.
Definition: Condition.c:67
Immutable data buffers.
Definition: Data.h:50
size_t length
The length of bytes.
Definition: Data.h:76
uint8_t * bytes
The bytes.
Definition: Data.h:66
DateFormatter * initWithFormat(DateFormatter *self, const char *fmt)
Initializes a DateFormatter with the specified format string.
Definition: DateFormatter.c:76
Immutable key-value stores.
Definition: Dictionary.h:60
void enumerateObjectsAndKeys(const Dictionary *self, DictionaryEnumerator enumerator, ident data)
Enumerate the pairs of this Dictionary with the given function.
Definition: Dictionary.c:253
void error(const Log *self, const char *fmt,...)
Log an error message.
Definition: Log.c:80
MutableData * data(void)
Returns a new MutableData.
Definition: MutableData.c:75
MutableDictionary * dictionary(void)
Returns a new MutableDictionary.
Object is the root Class of The Objectively Class hierarchy.
Definition: Object.h:46
Class * _Object(void)
The Object archetype.
Definition: Object.c:136
Object * copy(const Object *self)
Creates a shallow copy of this Object.
Definition: Array.c:40
void dealloc(Object *self)
Frees all resources held by this Object.
Definition: Array.c:50
void cancel(Operation *self)
Cancels this Operation, allowing it to complete immediately.
Definition: Operation.c:74
Immutable UTF-8 strings.
Definition: String.h:69
char * chars
The backing null-terminated UTF-8 encoded character array.
Definition: String.h:85
String * urlString
The URL String.
Definition: URL.h:90
A protocol-agnostic abstraction for requesting resources via URLs.
Definition: URLRequest.h:56
HTTPMethod httpMethod
The HTTP request method.
Definition: URLRequest.h:82
Dictionary * httpHeaders
The HTTP request headers.
Definition: URLRequest.h:77
URL * url
The URL.
Definition: URLRequest.h:87
void setValueForHTTPHeaderField(URLREquest *self, const char *value, const char *field)
Data * httpBody
The HTTP request body, sent as POST or PUT data.
Definition: URLRequest.h:72
A protocol-agnostic abstraction for URLSessionTask responses.
Definition: URLResponse.h:42
int httpStatusCode
The HTTP response status code.
Definition: URLResponse.h:63
Dictionary * httpHeaders
The HTTP headers added to every HTTP URLRequest.
A management context for loading resources via URLs.
Definition: URLSession.h:57
URLSessionConfiguration * configuration
The session configuration.
Definition: URLSession.h:103
URL session tasks are handles to pending URL operations.
URLSessionTask * initWithRequestInSession(URLSessionTask *, struct URLRequest *, struct URLSession *, URLSessionTaskCompletion)
Initializes this task with the given URLRequest.
struct URLResponse * response
The response.
ident requestHeaders
HTTP headers, in libcurl list structure.
ident handle
The backing libcurl handle.
void setup(URLSessionTask *)
Sets up this task.
void resume(URLSessionTask *)
Starts or resumes this task.
void suspend(URLSessionTask *)
Suspends this task.
void execute(URLSessionTask *)
Executes this task synchronously, on the calling thread.
char * error
The error buffer.
Class * _URLSessionTask(void)
The URLSessionTask archetype.
struct URLRequest * request
The request.
void teardown(URLSessionTask *)
Tears down this task.
struct URLSession * session
The session.
URLSessionTaskState state
The state.
URLSessionTaskCompletion completion
The completion function.