kore

Kore is a web application platform for writing scalable, concurrent web based processes in C or Python.
Commits | Files | Refs | README | LICENSE | git clone https://git.kore.io/kore.git

tasks.c (7314B)



      1 /*
      2  * Copyright (c) 2014 Joris Vink <joris@coders.se>
      3  *
      4  * Permission to use, copy, modify, and distribute this software for any
      5  * purpose with or without fee is hereby granted, provided that the above
      6  * copyright notice and this permission notice appear in all copies.
      7  *
      8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     15  */
     16 
     17 /*
     18  * In this example, we use the background tasks available in Kore
     19  * to fire off a POST to our /post_back page handler containing
     20  * the user argument that was passed to us in our GET request to /.
     21  *
     22  * This illustrates how Kore its background tasks in effect work and
     23  * how to operate on the channel in order to pass data back and forth.
     24  *
     25  * You need libcurl installed for this to build (including headers)
     26  *
     27  * Read README.md on how to build and run this example.
     28  */
     29 
     30 #include <curl/curl.h>
     31 
     32 #include <kore/kore.h>
     33 #include <kore/http.h>
     34 #include <kore/tasks.h>
     35 
     36 /* We need to allow some more syscalls on linux. */
     37 #if defined(__linux__)
     38 #include <kore/seccomp.h>
     39 
     40 KORE_SECCOMP_FILTER("tasks",
     41 	/* Allow sockets and libcurl to call connect. */
     42 	KORE_SYSCALL_ALLOW(bind),
     43 	KORE_SYSCALL_ALLOW(ioctl),
     44 	KORE_SYSCALL_ALLOW(connect),
     45 	KORE_SYSCALL_ALLOW(getsockopt),
     46 	KORE_SYSCALL_ALLOW(getsockname),
     47 	KORE_SYSCALL_ALLOW_ARG(socket, 0, AF_INET),
     48 	KORE_SYSCALL_ALLOW_ARG(socket, 0, AF_INET6),
     49 	KORE_SYSCALL_ALLOW_ARG(socket, 0, AF_UNIX),
     50 	KORE_SYSCALL_ALLOW_ARG(socket, 0, AF_NETLINK),
     51 
     52 	/* Other */
     53 	KORE_SYSCALL_ALLOW(ioctl),
     54 	KORE_SYSCALL_ALLOW(madvise),
     55 	KORE_SYSCALL_ALLOW(recvmsg),
     56 	KORE_SYSCALL_ALLOW(sendmmsg),
     57 	KORE_SYSCALL_ALLOW(getpeername),
     58 	KORE_SYSCALL_ALLOW(rseq),
     59 	KORE_SYSCALL_ALLOW(clone3),
     60 );
     61 #endif
     62 
     63 int		run_curl(struct kore_task *);
     64 int		post_back(struct http_request *);
     65 int		page_handler(struct http_request *);
     66 size_t		curl_write_cb(char *, size_t, size_t, void *);
     67 
     68 struct rstate {
     69 	struct kore_task	task;
     70 };
     71 
     72 int
     73 page_handler(struct http_request *req)
     74 {
     75 	u_int32_t	len;
     76 	struct rstate	*state;
     77 	char		*user, result[64];
     78 
     79 	/*
     80 	 * Lets check if a task has been created yet, this is important
     81 	 * as we only want to fire this off once and we will be called
     82 	 * again once it has been created.
     83 	 *
     84 	 * In this example, we'll store our state with our task in hdlr_extra.
     85 	 */
     86 	if (req->hdlr_extra == NULL) {
     87 		/* Grab the user argument */
     88 		http_populate_get(req);
     89 		if (!http_argument_get_string(req, "user", &user)) {
     90 			http_response(req, 500, "ERROR\n", 6);
     91 			return (KORE_RESULT_OK);
     92 		}
     93 
     94 		/*
     95 		 * Allocate rstate and bind it to the hdlr_extra field.
     96 		 * Kore automatically frees this when freeing the result.
     97 		 */
     98 		state = kore_malloc(sizeof(*state));
     99 		req->hdlr_extra = state;
    100 
    101 		/*
    102 		 * Create a new task that will execute the run_curl()
    103 		 * function and bind it to our request.
    104 		 *
    105 		 * Binding a task to a request means Kore will reschedule
    106 		 * the page handler for that request to refire after the
    107 		 * task has completed or when it writes on the task channel.
    108 		 */
    109 		kore_task_create(&state->task, run_curl);
    110 		kore_task_bind_request(&state->task, req);
    111 
    112 		/*
    113 		 * Start the task and write the user we received in our
    114 		 * GET request to its channel.
    115 		 */
    116 		kore_task_run(&state->task);
    117 		kore_task_channel_write(&state->task, user, strlen(user));
    118 
    119 		/*
    120 		 * Tell Kore to retry us later.
    121 		 */
    122 		return (KORE_RESULT_RETRY);
    123 	} else {
    124 		state = req->hdlr_extra;
    125 	}
    126 
    127 	/*
    128 	 * Our page handler is scheduled to be called when either the
    129 	 * task finishes or has written data onto the channel.
    130 	 *
    131 	 * In order to distinguish between the two we can inspect the
    132 	 * state of the task.
    133 	 */
    134 	if (kore_task_state(&state->task) != KORE_TASK_STATE_FINISHED) {
    135 		http_request_sleep(req);
    136 		return (KORE_RESULT_RETRY);
    137 	}
    138 
    139 	/*
    140 	 * Task is finished, check the result.
    141 	 */
    142 	if (kore_task_result(&state->task) != KORE_RESULT_OK) {
    143 		kore_task_destroy(&state->task);
    144 		http_response(req, 500, NULL, 0);
    145 		return (KORE_RESULT_OK);
    146 	}
    147 
    148 	/*
    149 	 * Lets read what our task has written to the channel.
    150 	 *
    151 	 * kore_task_channel_read() will return the amount of bytes
    152 	 * that it received for that read. If the returned bytes is
    153 	 * larger then the buffer you passed this is a sign of truncation
    154 	 * and should be treated carefully.
    155 	 */
    156 	len = kore_task_channel_read(&state->task, result, sizeof(result));
    157 	if (len > sizeof(result)) {
    158 		http_response(req, 500, NULL, 0);
    159 	} else {
    160 		http_response(req, 200, result, len);
    161 	}
    162 
    163 	/* We good, destroy the task. */
    164 	kore_task_destroy(&state->task);
    165 
    166 	return (KORE_RESULT_OK);
    167 }
    168 
    169 int
    170 post_back(struct http_request *req)
    171 {
    172 	char		*user;
    173 
    174 	if (req->method != HTTP_METHOD_POST) {
    175 		http_response(req, 500, NULL, 0);
    176 		return (KORE_RESULT_OK);
    177 	}
    178 
    179 	http_populate_post(req);
    180 	if (!http_argument_get_string(req, "user", &user)) {
    181 		http_response(req, 500, NULL, 0);
    182 		return (KORE_RESULT_OK);
    183 	}
    184 
    185 	/* Simply echo the supplied user argument back. */
    186 	http_response(req, 200, user, strlen(user));
    187 
    188 	return (KORE_RESULT_OK);
    189 }
    190 
    191 /*
    192  * This is the function that is executed by our task which is created
    193  * in the page_handler() callback.
    194  *
    195  * It sets up a CURL POST request to /post_back passing along the
    196  * user argument which it receives from its channel from page_handler().
    197  */
    198 int
    199 run_curl(struct kore_task *t)
    200 {
    201 	struct kore_buf		*b;
    202 	size_t			len;
    203 	CURLcode		res;
    204 	u_int8_t		*data;
    205 	CURL			*curl;
    206 	char			user[64], fields[128];
    207 
    208 	/*
    209 	 * Read the channel in order to obtain the user argument
    210 	 * that was written to it by page_handler().
    211 	 */
    212 	len = kore_task_channel_read(t, user, sizeof(user));
    213 	if (len > sizeof(user))
    214 		return (KORE_RESULT_ERROR);
    215 
    216 	if (!kore_snprintf(fields, sizeof(fields),
    217 	    NULL, "user=%.*s", len, user))
    218 		return (KORE_RESULT_ERROR);
    219 
    220 	if ((curl = curl_easy_init()) == NULL)
    221 		return (KORE_RESULT_ERROR);
    222 
    223 	b = kore_buf_alloc(128);
    224 
    225 	/* Do CURL magic. */
    226 	curl_easy_setopt(curl, CURLOPT_POST, 1);
    227 	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
    228 	curl_easy_setopt(curl, CURLOPT_WRITEDATA, b);
    229 	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
    230 	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
    231 	curl_easy_setopt(curl, CURLOPT_POSTFIELDS, fields);
    232 	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_cb);
    233 #if !defined(KORE_NO_TLS)
    234 	curl_easy_setopt(curl, CURLOPT_URL, "https://127.0.0.1:8888/post_back");
    235 #else
    236 	curl_easy_setopt(curl, CURLOPT_URL, "http://127.0.0.1:8888/post_back");
    237 #endif
    238 
    239 	res = curl_easy_perform(curl);
    240 	if (res != CURLE_OK) {
    241 		kore_buf_free(b);
    242 		curl_easy_cleanup(curl);
    243 		return (KORE_RESULT_ERROR);
    244 	}
    245 
    246 	/*
    247 	 * Grab the response from the CURL request and write the
    248 	 * result back to the task channel.
    249 	 */
    250 	data = kore_buf_release(b, &len);
    251 	kore_task_channel_write(t, data, len);
    252 	kore_free(data);
    253 
    254 	return (KORE_RESULT_OK);
    255 }
    256 
    257 size_t
    258 curl_write_cb(char *ptr, size_t size, size_t nmemb, void *udata)
    259 {
    260 	struct kore_buf		*b = udata;
    261 
    262 	kore_buf_append(b, ptr, size * nmemb);
    263 	return (size * nmemb);
    264 }