diff --git a/libs/libblade/libblade.vcxproj b/libs/libblade/libblade.vcxproj
index 5393765671..40baad7dda 100644
--- a/libs/libblade/libblade.vcxproj
+++ b/libs/libblade/libblade.vcxproj
@@ -195,6 +195,7 @@
+
@@ -219,6 +220,7 @@
+
diff --git a/libs/libblade/libblade.vcxproj.filters b/libs/libblade/libblade.vcxproj.filters
index 95d31a3192..2c7dbb8b54 100644
--- a/libs/libblade/libblade.vcxproj.filters
+++ b/libs/libblade/libblade.vcxproj.filters
@@ -78,6 +78,9 @@
Source Files
+
+ Source Files
+
@@ -146,5 +149,8 @@
Header Files
+
+ Header Files
+
\ No newline at end of file
diff --git a/libs/libblade/src/blade_restmgr.c b/libs/libblade/src/blade_restmgr.c
index 34964f43bc..2c2d43a2ef 100644
--- a/libs/libblade/src/blade_restmgr.c
+++ b/libs/libblade/src/blade_restmgr.c
@@ -61,6 +61,7 @@ typedef struct blade_restmgr_service_s {
blade_restmgr_service_callback_t callback;
} blade_restmgr_service_t;
+
static void blade_restmgr_service_cleanup(void *ptr, void *arg, ks_pool_cleanup_action_t action, ks_pool_cleanup_type_t type)
{
blade_restmgr_service_t *brestmgrs = (blade_restmgr_service_t *)ptr;
diff --git a/libs/libblade/src/blade_web.c b/libs/libblade/src/blade_web.c
new file mode 100644
index 0000000000..1bf98dbf70
--- /dev/null
+++ b/libs/libblade/src/blade_web.c
@@ -0,0 +1,674 @@
+/*
+ * Copyright (c) 2017, Shane Bryldt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of the original author; nor the names of any contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "blade.h"
+
+struct blade_webrequest_s {
+ const char *action;
+ const char *path;
+
+ ks_hash_t *query;
+ ks_hash_t *headers;
+
+ ks_sb_t *content;
+};
+
+struct blade_webresponse_s {
+ const char *status_code;
+ const char *status_message;
+
+ ks_hash_t *headers;
+
+ ks_sb_t *content;
+};
+
+static void blade_webrequest_cleanup(void *ptr, void *arg, ks_pool_cleanup_action_t action, ks_pool_cleanup_type_t type)
+{
+ blade_webrequest_t *bwreq = (blade_webrequest_t *)ptr;
+
+ ks_assert(bwreq);
+
+ switch (action) {
+ case KS_MPCL_ANNOUNCE:
+ break;
+ case KS_MPCL_TEARDOWN:
+ break;
+ case KS_MPCL_DESTROY:
+ break;
+ }
+}
+
+static void blade_webresponse_cleanup(void *ptr, void *arg, ks_pool_cleanup_action_t action, ks_pool_cleanup_type_t type)
+{
+ blade_webresponse_t *bwres = (blade_webresponse_t *)ptr;
+
+ ks_assert(bwres);
+
+ switch (action) {
+ case KS_MPCL_ANNOUNCE:
+ break;
+ case KS_MPCL_TEARDOWN:
+ break;
+ case KS_MPCL_DESTROY:
+ break;
+ }
+}
+
+KS_DECLARE(ks_status_t) blade_webrequest_create(blade_webrequest_t **bwreqP, const char *action, const char *path)
+{
+ ks_pool_t *pool = NULL;
+ blade_webrequest_t *bwreq = NULL;
+
+ ks_assert(bwreqP);
+ ks_assert(action);
+ ks_assert(path);
+
+ ks_pool_open(&pool);
+ ks_assert(pool);
+
+ bwreq = ks_pool_alloc(pool, sizeof(blade_webrequest_t));
+
+ bwreq->action = ks_pstrdup(pool, action);
+ bwreq->path = ks_pstrdup(pool, path);
+
+ ks_hash_create(&bwreq->query, KS_HASH_MODE_CASE_INSENSITIVE, KS_HASH_FLAG_NOLOCK | KS_HASH_FLAG_DUP_CHECK | KS_HASH_FLAG_FREE_KEY | KS_HASH_FLAG_FREE_VALUE, pool);
+ ks_assert(bwreq->query);
+
+ ks_hash_create(&bwreq->headers, KS_HASH_MODE_CASE_INSENSITIVE, KS_HASH_FLAG_NOLOCK | KS_HASH_FLAG_DUP_CHECK | KS_HASH_FLAG_FREE_KEY | KS_HASH_FLAG_FREE_VALUE, pool);
+ ks_assert(bwreq->headers);
+
+ ks_sb_create(&bwreq->content, pool, 0);
+ ks_assert(bwreq->content);
+
+ ks_pool_set_cleanup(bwreq, NULL, blade_webrequest_cleanup);
+
+ *bwreqP = bwreq;
+
+ blade_webrequest_header_add(bwreq, "Content-Type", "application/x-www-form-urlencoded");
+
+ return KS_STATUS_SUCCESS;
+}
+
+KS_DECLARE(ks_status_t) blade_webrequest_load(blade_webrequest_t **bwreqP, struct mg_connection *conn)
+{
+ ks_status_t ret = KS_STATUS_SUCCESS;
+ ks_pool_t *pool = NULL;
+ blade_webrequest_t *bwreq = NULL;
+ struct mg_request_info *info = NULL;
+ char buf[1024];
+ int bytes = 0;
+
+ ks_assert(bwreqP);
+ ks_assert(conn);
+
+ info = mg_get_request_info(conn);
+
+ ks_pool_open(&pool);
+ ks_assert(pool);
+
+ bwreq = ks_pool_alloc(pool, sizeof(blade_webrequest_t));
+
+ bwreq->action = ks_pstrdup(pool, info->request_method);
+ bwreq->path = ks_pstrdup(pool, info->request_uri);
+
+ ks_hash_create(&bwreq->query, KS_HASH_MODE_CASE_INSENSITIVE, KS_HASH_FLAG_NOLOCK | KS_HASH_FLAG_DUP_CHECK | KS_HASH_FLAG_FREE_KEY | KS_HASH_FLAG_FREE_VALUE, pool);
+ ks_assert(bwreq->query);
+
+ ks_hash_create(&bwreq->headers, KS_HASH_MODE_CASE_INSENSITIVE, KS_HASH_FLAG_NOLOCK | KS_HASH_FLAG_DUP_CHECK | KS_HASH_FLAG_FREE_KEY | KS_HASH_FLAG_FREE_VALUE, pool);
+ ks_assert(bwreq->headers);
+
+ ks_sb_create(&bwreq->content, pool, 0);
+ ks_assert(bwreq->content);
+
+ ks_pool_set_cleanup(bwreq, NULL, blade_webrequest_cleanup);
+
+ if (info->query_string && info->query_string[0]) {
+ char *query = ks_pstrdup(pool, info->query_string);
+ char *start = query;
+ char *end = NULL;
+
+ do {
+ char *key = start;
+ char *value = NULL;
+
+ end = strchr(start, '&');
+ if (end) *end = '\0';
+
+ value = strchr(start, '=');
+ if (value) {
+ *value = '\0';
+ value++;
+
+ if (*key && *value) {
+ ks_hash_insert(bwreq->query, (void *)ks_pstrdup(pool, key), (void *)ks_pstrdup(pool, value));
+ }
+ }
+
+ if (end) start = ++end;
+ else start = NULL;
+ } while (start);
+
+ ks_pool_free(&query);
+ }
+
+ for (int index = 0; index < info->num_headers; ++index) {
+ struct mg_header *header = &info->http_headers[index];
+ ks_hash_insert(bwreq->headers, (void *)ks_pstrdup(pool, header->name), (void *)ks_pstrdup(pool, header->value));
+ }
+
+ while ((bytes = mg_read(conn, buf, sizeof(buf))) > 0) ks_sb_append_ex(bwreq->content, buf, bytes);
+ if (bytes < 0) {
+ blade_webrequest_destroy(&bwreq);
+ ret = KS_STATUS_FAIL;
+ }
+ else *bwreqP = bwreq;
+
+ return ret;
+}
+
+KS_DECLARE(ks_status_t) blade_webrequest_destroy(blade_webrequest_t **bwreqP)
+{
+ blade_webrequest_t *bwreq = NULL;
+ ks_pool_t *pool;
+
+ ks_assert(bwreqP);
+ ks_assert(*bwreqP);
+
+ bwreq = *bwreqP;
+ *bwreqP = NULL;
+
+ pool = ks_pool_get(bwreq);
+
+ ks_pool_close(&pool);
+
+ return KS_STATUS_SUCCESS;
+}
+
+KS_DECLARE(const char *) blade_webrequest_action_get(blade_webrequest_t *bwreq)
+{
+ ks_assert(bwreq);
+ return bwreq->action;
+}
+
+KS_DECLARE(const char *) blade_webrequest_path_get(blade_webrequest_t *bwreq)
+{
+ ks_assert(bwreq);
+ return bwreq->path;
+}
+
+KS_DECLARE(ks_status_t) blade_webrequest_query_add(blade_webrequest_t *bwreq, const char *name, const char *value)
+{
+ ks_assert(bwreq);
+ ks_assert(name);
+ ks_assert(value);
+
+ ks_hash_insert(bwreq->query, (void *)ks_pstrdup(ks_pool_get(bwreq), name), (void *)ks_pstrdup(ks_pool_get(bwreq), value));
+
+ return KS_STATUS_SUCCESS;
+}
+
+KS_DECLARE(const char *) blade_webrequest_query_get(blade_webrequest_t *bwreq, const char *name)
+{
+ ks_assert(bwreq);
+ ks_assert(name);
+
+ return (const char *)ks_hash_search(bwreq->query, (void *)name, KS_UNLOCKED);
+}
+
+KS_DECLARE(ks_status_t) blade_webrequest_header_add(blade_webrequest_t *bwreq, const char *header, const char *value)
+{
+ ks_assert(bwreq);
+ ks_assert(header);
+ ks_assert(value);
+
+ ks_hash_insert(bwreq->headers, (void *)ks_pstrdup(ks_pool_get(bwreq), header), (void *)ks_pstrdup(ks_pool_get(bwreq), value));
+
+ return KS_STATUS_SUCCESS;
+}
+
+KS_DECLARE(ks_status_t) blade_webrequest_header_printf(blade_webrequest_t *bwreq, const char *header, const char *fmt, ...)
+{
+ va_list ap;
+ char *result = NULL;
+
+ ks_assert(bwreq);
+ ks_assert(header);
+ ks_assert(fmt);
+
+ va_start(ap, fmt);
+ result = ks_vpprintf(ks_pool_get(bwreq), fmt, ap);
+ va_end(ap);
+
+ ks_hash_insert(bwreq->headers, (void *)ks_pstrdup(ks_pool_get(bwreq), header), (void *)result);
+
+ return KS_STATUS_SUCCESS;
+}
+
+KS_DECLARE(const char *) blade_webrequest_header_get(blade_webrequest_t *bwreq, const char *header)
+{
+ ks_assert(bwreq);
+ ks_assert(header);
+
+ return (const char *)ks_hash_search(bwreq->headers, (void *)header, KS_UNLOCKED);
+}
+
+KS_DECLARE(ks_status_t) blade_webrequest_content_json_append(blade_webrequest_t *bwreq, cJSON *json)
+{
+ ks_assert(bwreq);
+ ks_assert(json);
+
+ blade_webrequest_header_add(bwreq, "Content-Type", "application/json");
+
+ ks_sb_json(bwreq->content, json);
+
+ return KS_STATUS_SUCCESS;
+}
+
+KS_DECLARE(ks_status_t) blade_webrequest_content_string_append(blade_webrequest_t *bwreq, const char *str)
+{
+ ks_assert(bwreq);
+ ks_assert(str);
+
+ ks_sb_append(bwreq->content, str);
+
+ return KS_STATUS_SUCCESS;
+}
+
+KS_DECLARE(ks_status_t) blade_webrequest_send(blade_webrequest_t *bwreq, ks_bool_t secure, const char *host, ks_port_t port, blade_webresponse_t **bwresP)
+{
+ ks_status_t ret = KS_STATUS_SUCCESS;
+ char buf[1024];
+ struct mg_connection *conn = NULL;
+ const char *path = NULL;
+ ks_sb_t *pathAndQuery = NULL;
+
+ ks_assert(bwreq);
+ ks_assert(host);
+ ks_assert(bwresP);
+
+ if (port == 0) port = secure ? 443 : 80;
+
+ conn = mg_connect_client(host, port, secure, buf, sizeof(buf));
+ if (!conn) {
+ ret = KS_STATUS_FAIL;
+ goto done;
+ }
+
+ path = bwreq->path;
+ if (ks_hash_count(bwreq->query) > 0) {
+ ks_bool_t firstQuery = KS_TRUE;
+
+ ks_sb_create(&pathAndQuery, NULL, 0);
+ ks_sb_append(pathAndQuery, bwreq->path);
+ for (ks_hash_iterator_t *it = ks_hash_first(bwreq->query, KS_UNLOCKED); it; it = ks_hash_next(&it)) {
+ const char *key;
+ const char *value;
+
+ ks_hash_this(it, (const void **)&key, NULL, (void **)&value);
+
+ // @todo make sure key and value are URL encoded
+ mg_url_encode(key, buf, sizeof(buf));
+ ks_sb_printf(pathAndQuery, "%c%s=", firstQuery ? '?' : '&', buf);
+
+ mg_url_encode(value, buf, sizeof(buf));
+ ks_sb_append(pathAndQuery, buf);
+
+ firstQuery = KS_FALSE;
+ }
+
+ path = ks_sb_cstr(pathAndQuery);
+ }
+
+ mg_printf(conn,
+ "%s %s HTTP/1.1\r\n"
+ "Host: %s\r\n"
+ "Content-Length: %lu\r\n",
+ bwreq->action,
+ path,
+ host,
+ ks_sb_length(bwreq->content));
+
+ if (pathAndQuery) ks_sb_destroy(&pathAndQuery);
+
+ for (ks_hash_iterator_t *it = ks_hash_first(bwreq->headers, KS_UNLOCKED); it; it = ks_hash_next(&it)) {
+ const char *key;
+ const char *value;
+
+ ks_hash_this(it, (const void **)&key, NULL, (void **)&value);
+ mg_printf(conn, "%s: %s\r\n", key, value);
+ }
+
+ mg_write(conn, "\r\n", 2);
+
+ mg_write(conn, ks_sb_cstr(bwreq->content), ks_sb_length(bwreq->content));
+
+ if (mg_get_response(conn, buf, sizeof(buf), 1000) <= 0) {
+ ret = KS_STATUS_FAIL;
+ goto done;
+ }
+
+ ret = blade_webresponse_load(bwresP, conn);
+
+done:
+ if (conn) mg_close_connection(conn);
+ return ret;
+}
+
+KS_DECLARE(ks_status_t) blade_webrequest_oauth2_token_by_credentials_send(ks_bool_t secure, const char *host, ks_port_t port, const char *path, const char *client_id, const char *client_secret, const char **token)
+{
+ ks_status_t ret = KS_STATUS_SUCCESS;
+ blade_webrequest_t *bwreq = NULL;
+ blade_webresponse_t *bwres = NULL;
+ cJSON *json = NULL;
+ char *auth = NULL;
+ char encoded[1024];
+ ks_pool_t *pool = NULL;
+ char *tok = NULL;
+
+ ks_assert(host);
+ ks_assert(path);
+ ks_assert(client_id);
+ ks_assert(client_secret);
+ ks_assert(token);
+
+ blade_webrequest_create(&bwreq, "POST", path);
+
+ auth = ks_psprintf(ks_pool_get(bwreq), "%s:%s", client_id, client_secret);
+ ks_b64_encode((unsigned char *)auth, strlen(auth), (unsigned char *)encoded, sizeof(encoded));
+ ks_pool_free(&auth);
+
+ blade_webrequest_header_printf(bwreq, "Authorization", "Basic %s", encoded);
+
+ json = cJSON_CreateObject();
+ cJSON_AddStringToObject(json, "grant_type", "client_credentials");
+ blade_webrequest_content_json_append(bwreq, json);
+ cJSON_Delete(json);
+
+ if ((ret = blade_webrequest_send(bwreq, secure, host, port, &bwres)) != KS_STATUS_SUCCESS) goto done;
+
+ if ((ret = blade_webresponse_content_json_get(bwres, &json)) != KS_STATUS_SUCCESS) goto done;
+
+ if ((tok = cJSON_GetObjectCstr(json, "access_token")) == NULL) {
+ ret = KS_STATUS_FAIL;
+ goto done;
+ }
+
+ ks_pool_open(&pool);
+ *token = ks_pstrdup(pool, tok);
+
+done:
+ if (json) cJSON_Delete(json);
+ blade_webrequest_destroy(&bwreq);
+ if (bwres) blade_webresponse_destroy(&bwres);
+
+ return ret;
+}
+
+KS_DECLARE(ks_status_t) blade_webrequest_oauth2_token_by_code_send(ks_bool_t secure, const char *host, ks_port_t port, const char *path, const char *client_id, const char *client_secret, const char *code, const char **token)
+{
+ ks_status_t ret = KS_STATUS_SUCCESS;
+ blade_webrequest_t *bwreq = NULL;
+ blade_webresponse_t *bwres = NULL;
+ cJSON *json = NULL;
+ char *auth = NULL;
+ char encoded[1024];
+ ks_pool_t *pool = NULL;
+ char *tok = NULL;
+
+ ks_assert(host);
+ ks_assert(path);
+ ks_assert(client_id);
+ ks_assert(client_secret);
+ ks_assert(code);
+ ks_assert(token);
+
+ blade_webrequest_create(&bwreq, "POST", path);
+
+ auth = ks_psprintf(ks_pool_get(bwreq), "%s:%s", client_id, client_secret);
+ ks_b64_encode((unsigned char *)auth, strlen(auth), (unsigned char *)encoded, sizeof(encoded));
+ ks_pool_free(&auth);
+
+ blade_webrequest_header_printf(bwreq, "Authorization", "Basic %s", encoded);
+
+ json = cJSON_CreateObject();
+ cJSON_AddStringToObject(json, "grant_type", "authorization_code");
+ cJSON_AddStringToObject(json, "code", code);
+ blade_webrequest_content_json_append(bwreq, json);
+ cJSON_Delete(json);
+
+ if ((ret = blade_webrequest_send(bwreq, secure, host, port, &bwres)) != KS_STATUS_SUCCESS) goto done;
+
+ if ((ret = blade_webresponse_content_json_get(bwres, &json)) != KS_STATUS_SUCCESS) goto done;
+
+ if ((tok = cJSON_GetObjectCstr(json, "access_token")) == NULL) {
+ ret = KS_STATUS_FAIL;
+ goto done;
+ }
+
+ ks_pool_open(&pool);
+ *token = ks_pstrdup(pool, tok);
+
+done:
+ if (json) cJSON_Delete(json);
+ blade_webrequest_destroy(&bwreq);
+ if (bwres) blade_webresponse_destroy(&bwres);
+
+ return ret;
+}
+
+
+KS_DECLARE(ks_status_t) blade_webresponse_create(blade_webresponse_t **bwresP, const char *status)
+{
+ ks_pool_t *pool = NULL;
+ blade_webresponse_t *bwres = NULL;
+
+ ks_assert(bwresP);
+ ks_assert(status);
+
+ ks_pool_open(&pool);
+ ks_assert(pool);
+
+ bwres = ks_pool_alloc(pool, sizeof(blade_webresponse_t));
+
+ bwres->status_code = ks_pstrdup(pool, status);
+ bwres->status_message = ks_pstrdup(pool, mg_get_response_code_text(NULL, atoi(status)));
+
+ ks_hash_create(&bwres->headers, KS_HASH_MODE_CASE_INSENSITIVE, KS_HASH_FLAG_NOLOCK | KS_HASH_FLAG_DUP_CHECK | KS_HASH_FLAG_FREE_KEY | KS_HASH_FLAG_FREE_VALUE, pool);
+ ks_assert(bwres->headers);
+
+ ks_sb_create(&bwres->content, pool, 0);
+ ks_assert(bwres->content);
+
+ ks_pool_set_cleanup(bwres, NULL, blade_webresponse_cleanup);
+
+ *bwresP = bwres;
+
+ blade_webresponse_header_add(bwres, "Content-Type", "application/x-www-form-urlencoded");
+
+ return KS_STATUS_SUCCESS;
+}
+
+KS_DECLARE(ks_status_t) blade_webresponse_load(blade_webresponse_t **bwresP, struct mg_connection *conn)
+{
+ ks_status_t ret = KS_STATUS_SUCCESS;
+ ks_pool_t *pool = NULL;
+ blade_webresponse_t *bwres = NULL;
+ struct mg_request_info *info = NULL;
+ char buf[1024];
+ int bytes = 0;
+
+ ks_assert(bwresP);
+ ks_assert(conn);
+
+ info = mg_get_request_info(conn);
+
+ ks_pool_open(&pool);
+ ks_assert(pool);
+
+ bwres = ks_pool_alloc(pool, sizeof(blade_webrequest_t));
+
+ bwres->status_code = ks_pstrdup(pool, info->request_uri);
+ bwres->status_message = ks_pstrdup(pool, info->http_version);
+
+ ks_hash_create(&bwres->headers, KS_HASH_MODE_CASE_INSENSITIVE, KS_HASH_FLAG_NOLOCK | KS_HASH_FLAG_DUP_CHECK | KS_HASH_FLAG_FREE_KEY | KS_HASH_FLAG_FREE_VALUE, pool);
+ ks_assert(bwres->headers);
+
+ ks_sb_create(&bwres->content, pool, 0);
+ ks_assert(bwres->content);
+
+ ks_pool_set_cleanup(bwres, NULL, blade_webresponse_cleanup);
+
+ for (int index = 0; index < info->num_headers; ++index) {
+ struct mg_header *header = &info->http_headers[index];
+ ks_hash_insert(bwres->headers, (void *)ks_pstrdup(pool, header->name), (void *)ks_pstrdup(pool, header->value));
+ }
+
+ while ((bytes = mg_read(conn, buf, sizeof(buf))) > 0) ks_sb_append_ex(bwres->content, buf, bytes);
+ if (bytes < 0) {
+ blade_webresponse_destroy(&bwres);
+ ret = KS_STATUS_FAIL;
+ }
+ else *bwresP = bwres;
+
+ return ret;
+}
+
+KS_DECLARE(ks_status_t) blade_webresponse_destroy(blade_webresponse_t **bwresP)
+{
+ blade_webresponse_t *bwres = NULL;
+ ks_pool_t *pool;
+
+ ks_assert(bwresP);
+ ks_assert(*bwresP);
+
+ bwres = *bwresP;
+ *bwresP = NULL;
+
+ pool = ks_pool_get(bwres);
+
+ ks_pool_close(&pool);
+
+ return KS_STATUS_SUCCESS;
+}
+
+KS_DECLARE(ks_status_t) blade_webresponse_header_add(blade_webresponse_t *bwres, const char *header, const char *value)
+{
+ ks_assert(bwres);
+ ks_assert(header);
+ ks_assert(value);
+
+ ks_hash_insert(bwres->headers, (void *)ks_pstrdup(ks_pool_get(bwres), header), (void *)ks_pstrdup(ks_pool_get(bwres), value));
+
+ return KS_STATUS_SUCCESS;
+}
+
+KS_DECLARE(const char *) blade_webresponse_header_get(blade_webresponse_t *bwres, const char *header)
+{
+ ks_assert(bwres);
+ ks_assert(header);
+
+ return (const char *)ks_hash_search(bwres->headers, (void *)header, KS_UNLOCKED);
+}
+
+KS_DECLARE(ks_status_t) blade_webresponse_content_json_append(blade_webresponse_t *bwres, cJSON *json)
+{
+ ks_assert(bwres);
+ ks_assert(json);
+
+ blade_webresponse_header_add(bwres, "Content-Type", "application/json");
+
+ ks_sb_json(bwres->content, json);
+
+ return KS_STATUS_SUCCESS;
+}
+
+KS_DECLARE(ks_status_t) blade_webresponse_content_string_append(blade_webresponse_t *bwres, const char *str)
+{
+ ks_assert(bwres);
+ ks_assert(str);
+
+ ks_sb_append(bwres->content, str);
+
+ return KS_STATUS_SUCCESS;
+}
+
+KS_DECLARE(ks_status_t) blade_webresponse_content_json_get(blade_webresponse_t *bwres, cJSON **json)
+{
+ ks_status_t ret = KS_STATUS_SUCCESS;
+
+ ks_assert(bwres);
+ ks_assert(json);
+
+ if (!(*json = cJSON_Parse(ks_sb_cstr(bwres->content)))) ret = KS_STATUS_FAIL;
+
+ return ret;
+}
+
+KS_DECLARE(ks_status_t) blade_webresponse_send(blade_webresponse_t *bwres, struct mg_connection *conn)
+{
+ ks_assert(bwres);
+ ks_assert(conn);
+
+ mg_printf(conn,
+ "HTTP/1.1 %s %s\r\n"
+ "Content-Length: %lu\r\n"
+ "Connection: close\r\n",
+ bwres->status_code,
+ bwres->status_message,
+ ks_sb_length(bwres->content));
+
+ for (ks_hash_iterator_t *it = ks_hash_first(bwres->headers, KS_UNLOCKED); it; it = ks_hash_next(&it)) {
+ const char *key;
+ const char *value;
+
+ ks_hash_this(it, (const void **)&key, NULL, (void **)&value);
+ mg_printf(conn, "%s: %s\r\n", key, value);
+ }
+
+ mg_write(conn, "\r\n", 2);
+
+ mg_write(conn, ks_sb_cstr(bwres->content), ks_sb_length(bwres->content));
+
+ return KS_STATUS_SUCCESS;
+}
+
+/* For Emacs:
+ * Local Variables:
+ * mode:c
+ * indent-tabs-mode:t
+ * tab-width:4
+ * c-basic-offset:4
+ * End:
+ * For VIM:
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
+ */
diff --git a/libs/libblade/src/include/blade.h b/libs/libblade/src/include/blade.h
index 812e95456c..5e1cb3db82 100644
--- a/libs/libblade/src/include/blade.h
+++ b/libs/libblade/src/include/blade.h
@@ -51,6 +51,7 @@
#include "blade_channel.h"
#include "blade_subscription.h"
#include "blade_tuple.h"
+#include "blade_web.h"
#include "blade_transportmgr.h"
#include "blade_rpcmgr.h"
diff --git a/libs/libblade/src/include/blade_types.h b/libs/libblade/src/include/blade_types.h
index 7e6f960235..93d61b6f95 100644
--- a/libs/libblade/src/include/blade_types.h
+++ b/libs/libblade/src/include/blade_types.h
@@ -62,6 +62,8 @@ typedef struct blade_connectionmgr_s blade_connectionmgr_t;
typedef struct blade_sessionmgr_s blade_sessionmgr_t;
typedef struct blade_session_callback_data_s blade_session_callback_data_t;
+typedef struct blade_webrequest_s blade_webrequest_t;
+typedef struct blade_webresponse_s blade_webresponse_t;
typedef struct blade_restmgr_s blade_restmgr_t;
typedef ks_bool_t (*blade_rpc_request_callback_t)(blade_rpc_request_t *brpcreq, void *data);
diff --git a/libs/libblade/src/include/blade_web.h b/libs/libblade/src/include/blade_web.h
new file mode 100644
index 0000000000..062259f6ee
--- /dev/null
+++ b/libs/libblade/src/include/blade_web.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2017, Shane Bryldt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of the original author; nor the names of any contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _BLADE_WEB_H_
+#define _BLADE_WEB_H_
+#include
+
+KS_BEGIN_EXTERN_C
+KS_DECLARE(ks_status_t) blade_webrequest_create(blade_webrequest_t **bwreqP, const char *action, const char *path);
+KS_DECLARE(ks_status_t) blade_webrequest_load(blade_webrequest_t **bwreqP, struct mg_connection *conn);
+KS_DECLARE(ks_status_t) blade_webrequest_destroy(blade_webrequest_t **bwreqP);
+KS_DECLARE(const char *) blade_webrequest_action_get(blade_webrequest_t *bwreq);
+KS_DECLARE(const char *) blade_webrequest_path_get(blade_webrequest_t *bwreq);
+KS_DECLARE(ks_status_t) blade_webrequest_query_add(blade_webrequest_t *bwreq, const char *name, const char *value);
+KS_DECLARE(const char *) blade_webrequest_query_get(blade_webrequest_t *bwreq, const char *name);
+KS_DECLARE(ks_status_t) blade_webrequest_header_add(blade_webrequest_t *bwreq, const char *header, const char *value);
+KS_DECLARE(ks_status_t) blade_webrequest_header_printf(blade_webrequest_t *bwreq, const char *header, const char *fmt, ...);
+KS_DECLARE(const char *) blade_webrequest_header_get(blade_webrequest_t *bwreq, const char *header);
+KS_DECLARE(ks_status_t) blade_webrequest_content_json_append(blade_webrequest_t *bwreq, cJSON *json);
+KS_DECLARE(ks_status_t) blade_webrequest_content_string_append(blade_webrequest_t *bwreq, const char *str);
+KS_DECLARE(ks_status_t) blade_webrequest_send(blade_webrequest_t *bwreq, ks_bool_t secure, const char *host, ks_port_t port, blade_webresponse_t **bwresP);
+
+KS_DECLARE(ks_status_t) blade_webrequest_oauth2_token_by_credentials_send(ks_bool_t secure, const char *host, ks_port_t port, const char *path, const char *client_id, const char *client_secret, const char **token);
+KS_DECLARE(ks_status_t) blade_webrequest_oauth2_token_by_code_send(ks_bool_t secure, const char *host, ks_port_t port, const char *path, const char *client_id, const char *client_secret, const char *code, const char **token);
+
+
+KS_DECLARE(ks_status_t) blade_webresponse_create(blade_webresponse_t **bwresP, const char *status);
+KS_DECLARE(ks_status_t) blade_webresponse_load(blade_webresponse_t **bwresP, struct mg_connection *conn);
+KS_DECLARE(ks_status_t) blade_webresponse_destroy(blade_webresponse_t **bwresP);
+KS_DECLARE(ks_status_t) blade_webresponse_header_add(blade_webresponse_t *bwres, const char *header, const char *value);
+KS_DECLARE(const char *) blade_webresponse_header_get(blade_webresponse_t *bwres, const char *header);
+KS_DECLARE(ks_status_t) blade_webresponse_content_json_append(blade_webresponse_t *bwres, cJSON *json);
+KS_DECLARE(ks_status_t) blade_webresponse_content_string_append(blade_webresponse_t *bwres, const char *str);
+KS_DECLARE(ks_status_t) blade_webresponse_content_json_get(blade_webresponse_t *bwres, cJSON **json);
+KS_DECLARE(ks_status_t) blade_webresponse_send(blade_webresponse_t *bwres, struct mg_connection *conn);
+
+KS_END_EXTERN_C
+
+#endif
+
+/* For Emacs:
+ * Local Variables:
+ * mode:c
+ * indent-tabs-mode:t
+ * tab-width:4
+ * c-basic-offset:4
+ * End:
+ * For VIM:
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
+ */
diff --git a/libs/libblade/switchblade/switchblade.c b/libs/libblade/switchblade/switchblade.c
index 5c4b89c70e..9df78d46b0 100644
--- a/libs/libblade/switchblade/switchblade.c
+++ b/libs/libblade/switchblade/switchblade.c
@@ -139,37 +139,31 @@ void command_quit(blade_handle_t *bh, char *args)
int rest_service_test(blade_restmgr_t *brestmgr, struct mg_connection *conn, const char **captures)
{
- const struct mg_request_info *info = NULL;
+ blade_webrequest_t *request = NULL;
+ blade_webresponse_t *response = NULL;
cJSON *json = NULL;
cJSON *json_captures = NULL;
- ks_sb_t *sb = NULL;
+ const char *token = NULL;
+ blade_webrequest_load(&request, conn);
+
+ // make a json object to send back
json = cJSON_CreateObject();
-
- ks_sb_create(&sb, NULL, 0);
-
- info = mg_get_request_info(conn);
-
- cJSON_AddStringToObject(json, "method", info->request_method);
-
+ cJSON_AddStringToObject(json, "method", blade_webrequest_action_get(request));
cJSON_AddItemToObject(json, "captures", (json_captures = cJSON_CreateArray()));
-
for (int i = 0; captures[i]; ++i) cJSON_AddItemToArray(json_captures, cJSON_CreateString(captures[i]));
- ks_sb_json(sb, json);
-
- mg_printf(conn,
- "HTTP/1.1 200 OK\r\n"
- "Content-Length: %lu\r\n"
- "Content-Type: text/plain\r\n"
- "Connection: close\r\n\r\n",
- ks_sb_length(sb));
-
- mg_write(conn, ks_sb_cstr(sb), ks_sb_length(sb));
-
- ks_sb_destroy(&sb);
+ blade_webresponse_create(&response, "200");
+ blade_webresponse_content_json_append(response, json);
+ blade_webresponse_send(response, conn);
+ blade_webresponse_destroy(&response);
cJSON_Delete(json);
+ blade_webrequest_destroy(&request);
+
+ //blade_webrequest_oauth2_token_by_credentials_send(KS_FALSE, "192.168.1.99", 80, "/oauth2/token.php", "testclient", "testpass", &token);
+ //
+ //ks_pool_free(&token);
return 200;
}
diff --git a/libs/libks/src/ks_sb.c b/libs/libks/src/ks_sb.c
index e2ebb7c8e5..4f9beacee5 100644
--- a/libs/libks/src/ks_sb.c
+++ b/libs/libks/src/ks_sb.c
@@ -88,6 +88,7 @@ KS_DECLARE(ks_status_t) ks_sb_destroy(ks_sb_t **sbP)
ks_assert(*sbP);
sb = *sbP;
+ *sbP = NULL;
if (sb->pool_owner) {
ks_pool_t *pool = ks_pool_get(sb);