Objectively
Ultra-lightweight object oriented framework for GNU C.
Loading...
Searching...
No Matches
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 const URLSessionConfiguration *config = self->session->configuration;
220 if (config->connectTimeout) {
221 curl_easy_setopt(self->locals.handle, CURLOPT_CONNECTTIMEOUT, config->connectTimeout);
222 }
223 if (config->timeout) {
224 curl_easy_setopt(self->locals.handle, CURLOPT_TIMEOUT, config->timeout);
225 }
226 if (!config->longPolling) {
227 curl_easy_setopt(self->locals.handle, CURLOPT_LOW_SPEED_LIMIT, 1L);
228 curl_easy_setopt(self->locals.handle, CURLOPT_LOW_SPEED_TIME, 10L);
229 }
230
231 curl_easy_setopt(self->locals.handle, CURLOPT_HEADERFUNCTION, responseHeader);
232 curl_easy_setopt(self->locals.handle, CURLOPT_HEADERDATA, self);
233
234 curl_easy_setopt(self->locals.handle, CURLOPT_XFERINFOFUNCTION, progress);
235 curl_easy_setopt(self->locals.handle, CURLOPT_XFERINFODATA, self);
236
237 Data *body = self->request->httpBody;
238 if (body) {
239 curl_easy_setopt(self->locals.handle, CURLOPT_POSTFIELDS, body->bytes);
240 curl_easy_setopt(self->locals.handle, CURLOPT_POSTFIELDSIZE, body->length);
241 }
242
243 struct curl_slist *httpHeaders = NULL;
244 const Dictionary *headers = NULL;
245
246 headers = self->session->configuration->httpHeaders;
247 if (headers) {
248 $(headers, enumerateObjectsAndKeys, requestHeaders_enumerator, &httpHeaders);
249 }
250
251 headers = self->request->httpHeaders;
252 if (headers) {
253 $(headers, enumerateObjectsAndKeys, requestHeaders_enumerator, &httpHeaders);
254 }
255
256 curl_easy_setopt(self->locals.handle, CURLOPT_HTTPHEADER, httpHeaders);
257
258 self->locals.requestHeaders = httpHeaders;
259
260 switch (self->request->httpMethod) {
261 case HTTP_POST:
262 curl_easy_setopt(self->locals.handle, CURLOPT_POST, true);
263 break;
264 case HTTP_PUT:
265 curl_easy_setopt(self->locals.handle, CURLOPT_PUT, true);
266 break;
267 case HTTP_DELETE:
268 curl_easy_setopt(self->locals.handle, CURLOPT_CUSTOMREQUEST, "DELETE");
269 break;
270 case HTTP_PATCH:
271 curl_easy_setopt(self->locals.handle, CURLOPT_CUSTOMREQUEST, "PATCH");
272 break;
273 case HTTP_HEAD:
274 curl_easy_setopt(self->locals.handle, CURLOPT_NOBODY, true);
275 break;
276 case HTTP_OPTIONS:
277 curl_easy_setopt(self->locals.handle, CURLOPT_CUSTOMREQUEST, "OPTIONS");
278 break;
279 default:
280 break;
281 }
282
283 curl_easy_setopt(self->locals.handle, CURLOPT_URL, self->request->url->urlString->chars);
284}
285
290static void suspend(URLSessionTask *self) {
291
292 switch (self->state) {
296 break;
297 default:
298 break;
299 }
300}
301
306static void teardown(URLSessionTask *self) {
307
308 if (self->locals.handle) {
309 curl_easy_cleanup(self->locals.handle);
310 self->locals.handle = NULL;
311 }
312
313 if (self->locals.requestHeaders) {
314 curl_slist_free_all(self->locals.requestHeaders);
315 self->locals.requestHeaders = NULL;
316 }
317}
318
319#pragma mark - Class lifecycle
320
324static void initialize(Class *clazz) {
325
326 ((ObjectInterface *) clazz->interface)->copy = copy;
327 ((ObjectInterface *) clazz->interface)->dealloc = dealloc;
328
329 ((URLSessionTaskInterface *) clazz->interface)->cancel = cancel;
330 ((URLSessionTaskInterface *) clazz->interface)->execute = execute;
331 ((URLSessionTaskInterface *) clazz->interface)->initWithRequestInSession = initWithRequestInSession;
332 ((URLSessionTaskInterface *) clazz->interface)->resume = resume;
333 ((URLSessionTaskInterface *) clazz->interface)->setup = setup;
334 ((URLSessionTaskInterface *) clazz->interface)->suspend = suspend;
335 ((URLSessionTaskInterface *) clazz->interface)->teardown = teardown;
336}
337
343 static Class *clazz;
344 static Once once;
345
346 do_once(&once, {
347 clazz = _initialize(&(const ClassDef) {
348 .name = "URLSessionTask",
349 .superclass = _Object(),
350 .instanceSize = sizeof(URLSessionTask),
351 .interfaceOffset = offsetof(URLSessionTask, interface),
352 .interfaceSize = sizeof(URLSessionTaskInterface),
354 });
355 });
356
357 return clazz;
358}
359
360#undef _Class
static Array * init(Array *self)
Definition Array.c:420
static void teardown(void)
Called atexit to teardown Objectively.
Definition Class.c:46
static void setup(void)
Called when initializing Object to setup Objectively.
Definition Class.c:73
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 obj
#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
static DateFormatter * initWithFormat(DateFormatter *self, const char *fmt)
static void enumerateObjectsAndKeys(const Dictionary *self, DictionaryEnumerator enumerator, ident data)
Definition Dictionary.c:295
static Dictionary * dictionary(void)
Definition Dictionary.c:242
static void error(const Log *self, const char *fmt,...)
Definition Log.c:80
Class * _Object(void)
Definition Object.c:136
static int request(RESTClient *self, HTTPMethod method, const char *url_string, const Data *body, Data **out_data)
Definition RESTClient.c:57
char * strtrim(const char *s)
Copies the given null-terminated C string, trimming leading and trailing whitespace.
Definition String.c:1097
UTF-8 strings.
void * ident
The identity type, similar to Objective-C id.
Definition Types.h:49
static void setValueForHTTPHeaderField(URLRequest *self, const char *value, const char *field)
Definition URLRequest.c:169
A protocol-agnostic abstraction for requesting resources via URLs.
@ HTTP_PUT
Definition URLRequest.h:48
@ HTTP_DELETE
Definition URLRequest.h:50
@ HTTP_OPTIONS
Definition URLRequest.h:45
@ HTTP_POST
Definition URLRequest.h:47
@ HTTP_HEAD
Definition URLRequest.h:44
@ HTTP_PATCH
Definition URLRequest.h:49
A protocol-agnostic abstraction for receiving resources via URLs.
A management context for loading resources via URLs.
static URLSessionTask * initWithRequestInSession(URLSessionTask *self, struct URLRequest *request, struct URLSession *session, URLSessionTaskCompletion completion)
static void requestHeaders_enumerator(const Dictionary *dictionary, ident obj, ident key, ident data)
A helper to populate the request headers list for CURL.
static void resume(URLSessionTask *self)
static void cancel(URLSessionTask *self)
static void execute(URLSessionTask *self)
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...
Class * _URLSessionTask(void)
static void dealloc(Object *self)
static Object * copy(const Object *self)
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.
static void suspend(URLSessionTask *self)
URL session tasks are handles to pending URL operations.
void(* URLSessionTaskCompletion)(URLSessionTask *task, bool success)
A function pointer for URLSessionTask completion.
@ URLSESSIONTASK_RESUMING
@ URLSESSIONTASK_SUSPENDING
@ URLSESSIONTASK_SUSPENDED
@ URLSESSIONTASK_COMPLETED
@ URLSESSIONTASK_RESUMED
@ URLSESSIONTASK_CANCELING
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
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
Key-value stores.
Definition Dictionary.h:60
Object is the root Class of The Objectively Class hierarchy.
Definition Object.h:46
void dealloc(Object *self)
Frees all resources held by this Object.
Definition Array.c:99
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:58
HTTPMethod httpMethod
The HTTP request method.
Definition URLRequest.h:84
Dictionary * httpHeaders
The HTTP request headers.
Definition URLRequest.h:79
URL * url
The URL.
Definition URLRequest.h:89
Data * httpBody
The HTTP request body, sent as POST or PUT data.
Definition URLRequest.h:74
A protocol-agnostic abstraction for URLSessionTask responses.
Definition URLResponse.h:42
int httpStatusCode
The HTTP response status code.
Definition URLResponse.h:63
Configuration bundle for URLSession.
long connectTimeout
The timeout interval for establishing a connection, in seconds. 0 means no limit.
Dictionary * httpHeaders
The HTTP headers added to every HTTP URLRequest.
bool longPolling
If true, disables stall detection (LOW_SPEED_LIMIT / LOW_SPEED_TIME). Use for requests that legitimat...
long timeout
The timeout for the entire transfer, in seconds. 0 means no limit.
A management context for loading resources via URLs.
Definition URLSession.h:57
URLSessionConfiguration * configuration
The session configuration.
Definition URLSession.h:98
URL session tasks are handles to pending URL operations.
struct URLResponse * response
The response.
ident requestHeaders
HTTP headers, in libcurl list structure.
ident handle
The backing libcurl handle.
void resume(URLSessionTask *)
Starts or resumes this task.
char * error
The error buffer.
struct URLRequest * request
The request.
struct URLSession * session
The session.
URLSessionTaskState state
The state.
URLSessionTaskCompletion completion
The completion function.