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 @@ <ClCompile Include="src\blade_stack.c" /> <ClCompile Include="src\blade_transport.c" /> <ClCompile Include="src\blade_tuple.c" /> + <ClCompile Include="src\blade_web.c" /> <ClCompile Include="src\unqlite.c" /> </ItemGroup> <ItemGroup> @@ -219,6 +220,7 @@ <ClInclude Include="src\include\blade_transport.h" /> <ClInclude Include="src\include\blade_tuple.h" /> <ClInclude Include="src\include\blade_types.h" /> + <ClInclude Include="src\include\blade_web.h" /> <ClInclude Include="src\include\unqlite.h" /> </ItemGroup> <ItemGroup> 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 @@ <ClCompile Include="src\blade_restmgr.c"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="src\blade_web.c"> + <Filter>Source Files</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="src\include\unqlite.h"> @@ -146,5 +149,8 @@ <ClInclude Include="src\include\blade_restmgr.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="src\include\blade_web.h"> + <Filter>Header Files</Filter> + </ClInclude> </ItemGroup> </Project> \ 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 <blade.h> + +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);