From 901dd6726940a93f94cab1b86f9271507c05bd8b Mon Sep 17 00:00:00 2001 From: lixianjing Date: Fri, 22 Dec 2023 17:55:22 +0800 Subject: [PATCH] add data_reader_http --- docs/changes.md | 3 + src/awtk_global.c | 20 ++++ src/streams/inet/iostream_mbedtls.c | 20 ++++ src/streams/inet/iostream_mbedtls.h | 13 +++ src/streams/stream_factory.c | 23 ++-- src/tkc/data_reader_http.c | 160 ++++++++++++++++++++++++++++ src/tkc/data_reader_http.h | 55 ++++++++++ tests/data_reader_http_test.cc | 33 ++++++ 8 files changed, 321 insertions(+), 6 deletions(-) create mode 100644 src/tkc/data_reader_http.c create mode 100644 src/tkc/data_reader_http.h create mode 100644 tests/data_reader_http_test.cc diff --git a/docs/changes.md b/docs/changes.md index ed16cb38b..4bf5b6822 100644 --- a/docs/changes.md +++ b/docs/changes.md @@ -1,5 +1,8 @@ # 最新动态 +2023/12/22 + * 增加data\_reader\_http。 + 2023/12/21 * 增加控件继承的函数指针查找父集的逻辑(感谢智明提供补丁) diff --git a/src/awtk_global.c b/src/awtk_global.c index 82a2fae7d..64c488106 100644 --- a/src/awtk_global.c +++ b/src/awtk_global.c @@ -45,6 +45,10 @@ #include "base/vgcanvas_asset_manager.h" #endif +#ifdef WITH_SOCKET +#include "tkc/socket_helper.h" +#endif/*WITH_SOCKET*/ + #ifdef WITH_FSCRIPT_EXT #ifndef WITH_DATA_READER_WRITER #define WITH_DATA_READER_WRITER @@ -59,6 +63,9 @@ #include "tkc/data_reader_file.h" #include "tkc/data_reader_mem.h" #include "base/data_reader_asset.h" +#ifdef WITH_SOCKET +#include "tkc/data_reader_http.h" +#endif/*WITH_SOCKET*/ #endif /*WITH_DATA_READER_WRITER*/ #include "base/widget_animator_manager.h" @@ -300,6 +307,12 @@ ret_t tk_pre_init(void) { data_reader_factory_register(data_reader_factory(), "asset", data_reader_asset_create); data_reader_factory_register(data_reader_factory(), "mem", data_reader_mem_create); data_writer_factory_register(data_writer_factory(), "wbuffer", data_writer_wbuffer_create); +#ifdef WITH_SOCKET + data_reader_factory_register(data_reader_factory(), "http", data_reader_http_create); +#ifdef WITH_MBEDTLS + data_reader_factory_register(data_reader_factory(), "https", data_reader_http_create); +#endif/*WITH_MBEDTLS*/ +#endif/*WITH_SOCKET*/ #endif /*WITH_DATA_READER_WRITER*/ inited = TRUE; } @@ -327,6 +340,9 @@ ret_t tk_init(wh_t w, wh_t h, app_type_t app_type, const char* app_name, const c loop = main_loop_init(w, h); } return_value_if_fail(loop != NULL, RET_FAIL); +#ifdef WITH_SOCKET + tk_socket_init(); +#endif/*WITH_SOCKET*/ return RET_OK; } @@ -416,6 +432,10 @@ ret_t tk_deinit_internal(void) { #endif tk_semaphore_destroy(s_clear_cache_semaphore); +#ifdef WITH_SOCKET + tk_socket_deinit(); +#endif/*WITH_SOCKET*/ + return RET_OK; } diff --git a/src/streams/inet/iostream_mbedtls.c b/src/streams/inet/iostream_mbedtls.c index 2affb88e9..4793721c6 100644 --- a/src/streams/inet/iostream_mbedtls.c +++ b/src/streams/inet/iostream_mbedtls.c @@ -99,4 +99,24 @@ tk_iostream_t* tk_iostream_mbedtls_create(mbedtls_conn_t* conn) { return TK_IOSTREAM(obj); } + +#include "streams/inet/mbedtls_client.h" + +tk_iostream_t* tk_iostream_mbedtls_create_client(const char* host, uint16_t port) { + mbedtls_conn_t* conn = NULL; + tk_iostream_t* io = NULL; + char sport[16] = {0}; + return_value_if_fail(host != NULL, NULL); + + tk_snprintf(sport, sizeof(sport), "%d", (int)port); + conn = mbedtls_conn_client_create(host, sport, NULL, 0); + return_value_if_fail(conn != NULL, NULL); + + io = tk_iostream_mbedtls_create(conn); + if (io == NULL) { + mbedtls_conn_destroy(conn); + } + + return io; +} #endif /*WITH_MBEDTLS*/ diff --git a/src/streams/inet/iostream_mbedtls.h b/src/streams/inet/iostream_mbedtls.h index 74e5421d0..35532880f 100644 --- a/src/streams/inet/iostream_mbedtls.h +++ b/src/streams/inet/iostream_mbedtls.h @@ -58,6 +58,19 @@ struct _tk_iostream_mbedtls_t { */ tk_iostream_t* tk_iostream_mbedtls_create(mbedtls_conn_t* conn); +/** + * @method tk_iostream_mbedtls_create_client + * + * 创建iostream对象。 + * + * @param {const char*} host 主机地址。 + * @param {uint16_t} port 端口号。 + * + * @return {tk_iostream_t*} 返回iostream对象。 + * + */ +tk_iostream_t* tk_iostream_mbedtls_create_client(const char* host, uint16_t port); + #define TK_IOSTREAM_MBEDTLS(obj) ((tk_iostream_mbedtls_t*)(obj)) END_C_DECLS diff --git a/src/streams/stream_factory.c b/src/streams/stream_factory.c index e2ac85773..3e96f893c 100644 --- a/src/streams/stream_factory.c +++ b/src/streams/stream_factory.c @@ -23,10 +23,13 @@ #include "tkc/path.h" #include "tkc/utils.h" #include "tkc/serial_helper.h" -#ifdef WITH_SOCKET +#ifdef WITH_SOCKET #include "streams/inet/iostream_tcp.h" #include "streams/inet/iostream_udp.h" -#endif/*WITH_SOCKET*/ +#endif /*WITH_SOCKET*/ +#ifdef WITH_MBEDTLS +#include "streams/inet/iostream_mbedtls.h" +#endif /*WITH_MBEDTLS*/ #include "streams/serial/iostream_serial.h" #include "streams/stream_factory.h" @@ -39,13 +42,21 @@ tk_iostream_t* tk_stream_factory_create_iostream(const char* url) { return_value_if_fail(aurl != NULL, NULL); if (tk_str_start_with(url, STR_SCHEMA_TCP)) { -#ifdef WITH_SOCKET +#ifdef WITH_SOCKET io = tk_iostream_tcp_create_client(aurl->host, aurl->port); -#endif/*WITH_SOCKET*/ +#endif /*WITH_SOCKET*/ } else if (tk_str_start_with(url, STR_SCHEMA_UDP)) { -#ifdef WITH_SOCKET +#ifdef WITH_SOCKET io = tk_iostream_udp_create_client(aurl->host, aurl->port); -#endif/*WITH_SOCKET*/ +#endif /*WITH_SOCKET*/ + } else if (tk_str_start_with(url, STR_SCHEMA_HTTP)) { +#ifdef WITH_SOCKET + io = tk_iostream_tcp_create_client(aurl->host, aurl->port); +#endif /*WITH_SOCKET*/ + } else if (tk_str_start_with(url, STR_SCHEMA_HTTPS)) { +#ifdef WITH_MBEDTLS + io = tk_iostream_mbedtls_create_client(aurl->host, aurl->port); +#endif /*WITH_MBEDTLS*/ } else if (tk_str_eq(aurl->schema, "serial")) { char filename[MAX_PATH + 1] = {0}; diff --git a/src/tkc/data_reader_http.c b/src/tkc/data_reader_http.c new file mode 100644 index 000000000..dfd3f9d79 --- /dev/null +++ b/src/tkc/data_reader_http.c @@ -0,0 +1,160 @@ +/** + * File: data_reader.c + * Author: AWTK Develop Team + * Brief: data_reader + * + * Copyright (c) 2019 - 2023 Guangzhou ZHIYUAN Electronics Co.,Ltd. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * License http for more details. + * + */ + +/** + * History: + * ================================================================ + * 2023-12-22 Li XianJing created + * + */ + +#include "tkc/mem.h" +#include "tkc/url.h" +#include "tkc/utils.h" +#include "tkc/buffer.h" +#include "tkc/data_reader_http.h" +#include "streams/stream_factory.h" + +typedef struct _data_reader_http_t { + data_reader_t data_reader; + + uint64_t size; + uint8_t* data; +} data_reader_http_t; + +static const char* url_get_path(const char* url) { + const char* p = NULL; + return_value_if_fail(url != NULL, NULL); + + p = strstr(url, "://"); + if (p != NULL) { + p += 3; + p = strchr(p, '/'); + if (p == NULL) { + p = "/"; + } + } + + return p; +} + +static ret_t data_reader_http_get(data_reader_http_t* http, const char* url) { + char buff[1024]; + int32_t nr = 0; + url_t* aurl = NULL; + const char* p = NULL; + tk_iostream_t* io = NULL; + return_value_if_fail(url != NULL && http != NULL, RET_BAD_PARAMS); + + aurl = url_create(url); + return_value_if_fail(aurl != NULL, RET_BAD_PARAMS); + + io = tk_stream_factory_create_iostream(url); + return_value_if_fail(io != NULL, RET_BAD_PARAMS); + + tk_snprintf(buff, sizeof(buff) - 1, "GET %s HTTP/1.1\r\nHost: %s\r\n\r\n", url_get_path(url), + aurl->host); + nr = tk_iostream_write(io, buff, strlen(buff)); + url_destroy(aurl); + goto_error_if_fail(nr > 0); + + nr = tk_iostream_read(io, buff, sizeof(buff) - 1); + if (nr > 0) { + buff[nr] = '\0'; + p = strstr(buff, "Content-Length:"); + goto_error_if_fail(p != NULL); + p += strlen("Content-Length:"); + while (*p == ' ') { + p++; + } + http->size = tk_atoi(p); + goto_error_if_fail(http->size > 0); + + http->data = TKMEM_ALLOC(http->size + 1); + goto_error_if_fail(http->data != NULL); + + p = strstr(buff, "\r\n\r\n"); + goto_error_if_fail(p != NULL); + + p += 4; + nr = nr - (p - buff); + memcpy(http->data, p, nr); + + if (http->size > nr) { + nr = tk_iostream_read_len(io, http->data + nr, http->size - nr, TK_ISTREAM_DEFAULT_TIMEOUT); + goto_error_if_fail(nr > 0); + } + + http->data[http->size] = '\0'; + } + + TK_OBJECT_UNREF(io); + + return RET_OK; +error: + TK_OBJECT_UNREF(io); + + return RET_FAIL; +} + +static int32_t data_reader_http_read(data_reader_t* reader, uint64_t offset, void* data, + uint32_t size) { + data_reader_http_t* http = (data_reader_http_t*)reader; + return_value_if_fail(http != NULL && data != NULL, 0); + return_value_if_fail(offset <= http->size, 0); + size = tk_min(size, http->size - offset); + + memcpy(data, http->data + offset, size); + + return size; +} + +static uint64_t data_reader_http_get_size(data_reader_t* reader) { + data_reader_http_t* http = (data_reader_http_t*)reader; + return_value_if_fail(http != NULL, 0); + + return http->size; +} + +static ret_t data_reader_http_destroy(data_reader_t* reader) { + data_reader_http_t* http = (data_reader_http_t*)reader; + + TKMEM_FREE(http->data); + TKMEM_FREE(http); + + return RET_OK; +} + +static const data_reader_vtable_t s_data_reader_http_vtable = { + .read = data_reader_http_read, + .get_size = data_reader_http_get_size, + .destroy = data_reader_http_destroy, +}; + +data_reader_t* data_reader_http_create(const char* url) { + data_reader_http_t* http = NULL; + return_value_if_fail(url != NULL, NULL); + http = TKMEM_ZALLOC(data_reader_http_t); + return_value_if_fail(http != NULL, NULL); + + if (data_reader_http_get(http, url) != RET_OK) { + TKMEM_FREE(http->data); + TKMEM_FREE(http); + return NULL; + } + + http->data_reader.vt = &s_data_reader_http_vtable; + + return (data_reader_t*)http; +} diff --git a/src/tkc/data_reader_http.h b/src/tkc/data_reader_http.h new file mode 100644 index 000000000..70c694d9f --- /dev/null +++ b/src/tkc/data_reader_http.h @@ -0,0 +1,55 @@ +/** + * File: data_reader_http.h + * Author: AWTK Develop Team + * Brief: data_reader http + * + * Copyright (c) 2019 - 2023 Guangzhou ZHIYUAN Electronics Co.,Ltd. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * License http for more details. + * + */ + +/** + * History: + * ================================================================ + * 2023-12-22 Li XianJing created + * + */ + +#ifndef TK_DATA_READER_HTTP_H +#define TK_DATA_READER_HTTP_H + +#include "tkc/data_reader.h" + +BEGIN_C_DECLS + +/** + * @class data_reader_http_t + * @parent data_reader_t + * @annotation ["fake"] + * 基于HTTP/HTTPS实现的 data_reader。通过 data_reader_factory 创建 reader 时,URL的格式如下(请用函数data_reader_http_build_url生成): + * + * ``` + * http://t.weather.sojson.com/api/weather/city/101030100 + * https://restapi.amap.com/v3/weather/weatherInfo?city=110101&key=%3C%E7%94%A8%E6%88%B7key%3E + * ``` + */ + +/** + * @method data_reader_http_create + * 创建基于文件的data reader。 + * > 不要直接调用,而是注册到 data\_reader\_factory后,通过data\_reader\_factory调用。 + * @annotation ["constructor"] + * + * @param {const char*} url URL。 + * + * @return {data_reader_t*} 返回data reader对象。 + */ +data_reader_t* data_reader_http_create(const char* url); + +END_C_DECLS + +#endif /*TK_DATA_READER_HTTP_H*/ diff --git a/tests/data_reader_http_test.cc b/tests/data_reader_http_test.cc new file mode 100644 index 000000000..69150d723 --- /dev/null +++ b/tests/data_reader_http_test.cc @@ -0,0 +1,33 @@ +#include "gtest/gtest.h" +#include "tkc/data_reader_factory.h" +#include "tkc/data_reader_http.h" + +TEST(DataReaderHTTP, http) { + data_reader_t* reader = data_reader_http_create("http://www.baidu.com"); + + if(reader != NULL) { + char buffer[1024]; + uint32_t size = data_reader_get_size(reader); + int32_t ret = data_reader_read(reader, 0, buffer, sizeof(buffer)); + ASSERT_EQ(size > 0, true); + ASSERT_EQ(ret, sizeof(buffer)); + ret = data_reader_read(reader, 0, buffer, sizeof(buffer)); + data_reader_destroy(reader); + } +} + +TEST(DataReaderHTTP, https) { + data_reader_t* reader = data_reader_http_create("https://www.baidu.com"); + + if(reader != NULL) { + char buffer[1024]; + uint32_t size = data_reader_get_size(reader); + int32_t ret = data_reader_read(reader, 0, buffer, sizeof(buffer)); + ASSERT_EQ(size > 0, true); + ASSERT_EQ(ret, sizeof(buffer)); + + ret = data_reader_read(reader, 0, buffer, sizeof(buffer)); + data_reader_destroy(reader); + } +} +