From 072c1bcfb4e3262596ccf81c47ee6a6eb7cb24ed Mon Sep 17 00:00:00 2001 From: maosiping Date: Tue, 23 Nov 2021 12:13:29 +0800 Subject: [PATCH 1/2] Surpport Http without curl. Signed-off-by: maosiping --- frameworks/js/builtin/BUILD.gn | 9 +- frameworks/js/builtin/CMakeLists.txt | 1 + frameworks/js/builtin/fetch_module.cpp | 41 ++- .../js/builtin/http_request/http_constant.cpp | 21 +- .../js/builtin/http_request/http_constant.h | 21 +- .../js/builtin/http_request/http_request.cpp | 287 +++++++++++++++++- .../js/builtin/http_request/http_request.h | 19 +- .../http_request/http_request_utils.cpp | 140 +++++++-- .../builtin/http_request/http_request_utils.h | 24 +- .../js/builtin/http_request/response_data.cpp | 76 ++++- .../js/builtin/http_request/response_data.h | 4 + 11 files changed, 576 insertions(+), 67 deletions(-) diff --git a/frameworks/js/builtin/BUILD.gn b/frameworks/js/builtin/BUILD.gn index e016eb2a0..3f0459b37 100644 --- a/frameworks/js/builtin/BUILD.gn +++ b/frameworks/js/builtin/BUILD.gn @@ -43,7 +43,14 @@ http_lite_include_dirs = [ config("http_lite_config") { include_dirs = http_lite_include_dirs - defines = ["NO_SSL_CERTIFICATION=1"] + defines = [ + "NO_SSL_CERTIFICATION=1", + "HTTP_REQUEST_USE_CURL=1", + ] + ldflags = [ + "-lpthread", + "-Wl,-rpath-link=$ohos_root_path/$root_out_dir", + ] } lite_library("http_lite_shared") { diff --git a/frameworks/js/builtin/CMakeLists.txt b/frameworks/js/builtin/CMakeLists.txt index 6ce2e1312..5c24ce69d 100644 --- a/frameworks/js/builtin/CMakeLists.txt +++ b/frameworks/js/builtin/CMakeLists.txt @@ -58,5 +58,6 @@ target_link_libraries(test_fetch_module curl-d) target_link_libraries(test_fetch_module securec) add_compile_definitions(NO_SSL_CERTIFICATION=1) +add_compile_definitions(HTTP_REQUEST_USE_CURL=1) add_subdirectory(test) \ No newline at end of file diff --git a/frameworks/js/builtin/fetch_module.cpp b/frameworks/js/builtin/fetch_module.cpp index e3d18da6d..8323fb72f 100644 --- a/frameworks/js/builtin/fetch_module.cpp +++ b/frameworks/js/builtin/fetch_module.cpp @@ -32,6 +32,11 @@ void InitFetchModule(JSIValue exports) const char *const FetchModule::HTTP_API_KEY_FETCH = "fetch"; const char *const FetchModule::HTTP_API_KEY_STRING_TO_ARRAY_BUFFER = "stringToArrayBuffer"; +static void FreeJsString(char *s) +{ + JSI::ReleaseString(s); +} + JSIValue FetchModule::Fetch(const JSIValue thisVal, const JSIValue *args, uint8_t argsNum) { if (argsNum < 1) { @@ -43,7 +48,12 @@ JSIValue FetchModule::Fetch(const JSIValue thisVal, const JSIValue *args, uint8_ asyncCallback->responseCallback[CB_SUCCESS] = JSI::GetNamedProperty(args[0], CB_SUCCESS); asyncCallback->responseCallback[CB_FAIL] = JSI::GetNamedProperty(args[0], CB_FAIL); asyncCallback->responseCallback[CB_COMPLETE] = JSI::GetNamedProperty(args[0], CB_COMPLETE); - JsAsyncWork::DispatchAsyncWork(HttpAsyncCallback::AsyncExecHttpRequest, static_cast(asyncCallback)); + if (!JsAsyncWork::DispatchAsyncWork(HttpAsyncCallback::AsyncExecHttpRequest, + static_cast(asyncCallback))) { + HTTP_REQUEST_ERROR("dispatch work failed"); + delete asyncCallback; + return JSI::CreateUndefined(); + } } else { delete asyncCallback; } @@ -57,8 +67,8 @@ bool FetchModule::JsObjectToRequestData(JSIValue options, RequestData *req) return false; } - std::unique_ptr urlString( - JSI::GetStringProperty(options, HttpConstant::KEY_HTTP_REQUEST_URL), JSI::ReleaseString); + std::unique_ptr urlString( + JSI::GetStringProperty(options, HttpConstant::KEY_HTTP_REQUEST_URL), FreeJsString); if (urlString == nullptr) { return false; } @@ -97,14 +107,13 @@ void FetchModule::GetNameValue(JSIValue nv, std::map & continue; } - std::unique_ptr keyStr(JSI::ValueToString(key.get()), JSI::ReleaseString); + std::unique_ptr keyStr(JSI::ValueToString(key.get()), FreeJsString); if (keyStr == nullptr) { HTTP_REQUEST_ERROR("key to string failed"); continue; } - std::unique_ptr valStr(JSI::GetStringProperty(nv, keyStr.get()), - JSI::ReleaseString); + std::unique_ptr valStr(JSI::GetStringProperty(nv, keyStr.get()), FreeJsString); if (valStr == nullptr) { HTTP_REQUEST_ERROR("get val failed"); continue; @@ -125,8 +134,7 @@ void FetchModule::GetRequestBody(JSIValue options, RequestData *requestData) if (JSI::ValueIsString(body.get())) { size_t size = 0; - std::unique_ptr bodyStr(JSI::ValueToString(body.get(), size), - JSI::ReleaseString); + std::unique_ptr bodyStr(JSI::ValueToString(body.get(), size), FreeJsString); if (bodyStr == nullptr) { HTTP_REQUEST_ERROR("get body str failed"); return; @@ -138,8 +146,7 @@ void FetchModule::GetRequestBody(JSIValue options, RequestData *requestData) } if (JSI::ValueIsObject(body.get())) { - std::unique_ptr jsonString(JSI::JsonStringify(body.get()), - JSI::ReleaseString); + std::unique_ptr jsonString(JSI::JsonStringify(body.get()), FreeJsString); if (jsonString == nullptr) { return; } @@ -170,8 +177,7 @@ void FetchModule::ParseExtraData(JSIValue options, RequestData *requestData) } if (JSI::ValueIsString(extraData.get())) { - std::unique_ptr dataStr(JSI::ValueToString(extraData.get()), - JSI::ReleaseString); + std::unique_ptr dataStr(JSI::ValueToString(extraData.get()), FreeJsString); if (dataStr == nullptr) { return; } @@ -201,6 +207,10 @@ void FetchModule::ParseExtraData(JSIValue options, RequestData *requestData) if (MethodForPost(requestData->GetMethod())) { GetRequestBody(options, requestData); +#if !HTTP_REQUEST_USE_CURL + requestData->SetHeader(HttpConstant::HTTP_HEADER_KEY_CONTENT_LENGTH, + std::to_string(requestData->GetBody().size())); +#endif } } @@ -212,7 +222,7 @@ std::string FetchModule::GetMethodFromOptions(JSIValue options) return HttpConstant::HTTP_METHOD_GET; } - std::unique_ptr methodStr(JSI::ValueToString(value.get()), JSI::ReleaseString); + std::unique_ptr methodStr(JSI::ValueToString(value.get()), FreeJsString); return methodStr == nullptr ? HttpConstant::HTTP_METHOD_GET : methodStr.get(); } @@ -225,7 +235,7 @@ JSIValue FetchModule::StringToArrayBuffer(const JSIValue thisVal, const JSIValue } size_t size = 0; - std::unique_ptr str(JSI::ValueToString(args[0], size), JSI::ReleaseString); + std::unique_ptr str(JSI::ValueToString(args[0], size), FreeJsString); if (str == nullptr || size == 0) { return JSI::CreateUndefined(); } @@ -248,8 +258,7 @@ std::string FetchModule::GetResponseTypeFromOptions(JSIValue options) return ""; } - std::unique_ptr responseType(JSI::ValueToString(value.get()), - JSI::ReleaseString); + std::unique_ptr responseType(JSI::ValueToString(value.get()), FreeJsString); return responseType == nullptr ? "" : responseType.get(); } diff --git a/frameworks/js/builtin/http_request/http_constant.cpp b/frameworks/js/builtin/http_request/http_constant.cpp index 6dd52733e..376ce8b03 100644 --- a/frameworks/js/builtin/http_request/http_constant.cpp +++ b/frameworks/js/builtin/http_request/http_constant.cpp @@ -13,6 +13,10 @@ * limitations under the License. */ +#if !HTTP_REQUEST_USE_CURL +#include +#endif + namespace OHOS { namespace ACELite { namespace HttpConstant { @@ -21,7 +25,8 @@ int HTTP_RESPONSE_CODE_INVALID = -1; const char *HTTP_RESPONSE_TYPE_JSON = "json"; -const char *HTTP_HEADER_SEPARATOR = ":"; +const char *HTTP_RAW_LINE_SEPARATOR = "\r\n"; +const char *HTTP_RAW_HEADER_SEPARATOR = ": "; const char *HTTP_DEFAULT_USER_AGENT = "libcurl-agent/1.0"; @@ -51,6 +56,20 @@ const char *KEY_HTTP_REQUEST_HEADER = "header"; const char *KEY_HTTP_REQUEST_METHOD = "method"; const char *KEY_HTTP_REQUEST_RESPONSE_TYPE = "responseType"; +#if !HTTP_REQUEST_USE_CURL +const char *HTTP_PROTOCOL_SEPARATOR = "://"; +const char *HTTP_PROTOCOL_HTTP = "http"; +const char *HTTP_HOST_NAME_PORT_SEPARATOR = ":"; +const char *HTTP_DEFAULT_PATH = "/"; +const char *HTTP_RAW_WORD_SEPARATOR = " "; +const char *HTTP_RAW_PROTOCOL = "HTTP/1.1"; +const char *HTTP_RAW_HOST_KEY = "Host"; +const char *HTTP_RAW_HEADER_TRANSFER_ENCODING = "transfer-encoding"; +const char *HTTP_RAW_HEADER_TRANSFER_ENCODING_CHUNKED = "chunked"; +const char *HTTP_HEADER_KEY_CONTENT_LENGTH = "content-length"; +uint16_t HTTP_DEFAULT_PORT = 80; +#endif + } // namespace HttpConstant } // namespace ACELite } // namespace OHOS \ No newline at end of file diff --git a/frameworks/js/builtin/http_request/http_constant.h b/frameworks/js/builtin/http_request/http_constant.h index a1bba27c4..5bdae7d85 100644 --- a/frameworks/js/builtin/http_request/http_constant.h +++ b/frameworks/js/builtin/http_request/http_constant.h @@ -16,6 +16,10 @@ #ifndef OHOS_ACELITE_HTTP_CONSTANT_H #define OHOS_ACELITE_HTTP_CONSTANT_H +#if !HTTP_REQUEST_USE_CURL +#include +#endif + namespace OHOS { namespace ACELite { namespace HttpConstant { @@ -24,7 +28,8 @@ extern const int HTTP_RESPONSE_CODE_INVALID; extern const char *const HTTP_RESPONSE_TYPE_JSON; -extern const char *const HTTP_HEADER_SEPARATOR; +extern const char *const HTTP_RAW_LINE_SEPARATOR; +extern const char *const HTTP_RAW_HEADER_SEPARATOR; extern const char *const HTTP_DEFAULT_USER_AGENT; @@ -54,6 +59,20 @@ extern const char *const KEY_HTTP_REQUEST_HEADER; extern const char *const KEY_HTTP_REQUEST_METHOD; extern const char *const KEY_HTTP_REQUEST_RESPONSE_TYPE; +#if !HTTP_REQUEST_USE_CURL +extern const char *const HTTP_PROTOCOL_SEPARATOR; +extern const char *const HTTP_PROTOCOL_HTTP; +extern const char *const HTTP_HOST_NAME_PORT_SEPARATOR; +extern const char *const HTTP_DEFAULT_PATH; +extern const char *const HTTP_RAW_WORD_SEPARATOR; +extern const char *const HTTP_RAW_PROTOCOL; +extern const char *const HTTP_RAW_HOST_KEY; +extern const char *const HTTP_RAW_HEADER_TRANSFER_ENCODING; +extern const char *const HTTP_RAW_HEADER_TRANSFER_ENCODING_CHUNKED; +extern const char *const HTTP_HEADER_KEY_CONTENT_LENGTH; +extern const uint16_t HTTP_DEFAULT_PORT; +#endif + } // namespace HttpConstant } // namespace ACELite } // namespace OHOS diff --git a/frameworks/js/builtin/http_request/http_request.cpp b/frameworks/js/builtin/http_request/http_request.cpp index cc5d55791..80f636566 100644 --- a/frameworks/js/builtin/http_request/http_request.cpp +++ b/frameworks/js/builtin/http_request/http_request.cpp @@ -15,8 +15,20 @@ #include "http_request.h" #include "http_request_utils.h" -#include +#if !HTTP_REQUEST_USE_CURL +#include +#include +#include +#include +#include +#include +#include +#include +#else +#include +#endif +#if HTTP_REQUEST_USE_CURL #define ACE_CURL_EASY_SET_OPTION(handle, opt, data, respData) \ do { \ CURLcode result = curl_easy_setopt(handle, opt, data); \ @@ -52,17 +64,69 @@ return false; \ } \ } while (0) +#else +#define HTTP_REQUEST_BUF_LEN 1024 +#define HTTP_REQUEST_POLL_TIMEOUT 500 // 0.5 Seconds +#define HTTP_REQUEST_CONNECT_TIMEOUT 5 // 5 Seconds +#define HTTP_RESPONSE_SET_ERR_STRING(respData, ...) \ + do { \ + char err[128] = {0}; \ + (void)sprintf_s(err, sizeof(err), __VA_ARGS__); \ + (respData)->SetErrString(err); \ + } while (0) +#endif namespace OHOS { namespace ACELite { -std::mutex HttpRequest::mutex; +#if HTTP_REQUEST_USE_CURL + +class PthreadMutex { + pthread_mutex_t mutex; + +public: + void Lock() + { + pthread_mutex_lock(&mutex); + } -bool HttpRequest::initialized = false; + void UnLock() + { + pthread_mutex_unlock(&mutex); + } + + PthreadMutex() + { + pthread_mutex_init(&mutex, nullptr); + } + + ~PthreadMutex() + { + pthread_mutex_destroy(&mutex); + } +}; + +class PthreadLockGuard { + PthreadMutex *mutex; + +public: + explicit PthreadLockGuard(PthreadMutex *pthreadMutex) : mutex(pthreadMutex) + { + pthreadMutex->Lock(); + } + + ~PthreadLockGuard() + { + mutex->UnLock(); + } +}; bool HttpRequest::Initialize() { - std::lock_guard lock(mutex); + static PthreadMutex mutex; + static bool initialized = false; + + PthreadLockGuard lock(&mutex); if (initialized) { return true; } @@ -71,7 +135,7 @@ bool HttpRequest::Initialize() return false; } initialized = true; - return true; + return initialized; } bool HttpRequest::Request(RequestData *requestData, ResponseData *responseData) @@ -107,7 +171,7 @@ bool HttpRequest::Request(RequestData *requestData, ResponseData *responseData) std::vector vec(responseData->GetHeaders().size()); for (const auto &p : requestData->GetHeader()) { - vec.emplace_back(p.first + HttpConstant::HTTP_HEADER_SEPARATOR + p.second); + vec.emplace_back(p.first + HttpConstant::HTTP_RAW_HEADER_SEPARATOR + p.second); } std::unique_ptr header(MakeHeaders(vec), curl_slist_free_all); if (header != nullptr) { @@ -143,7 +207,7 @@ bool HttpRequest::SetOption(RequestData *requestData, CURL *curl, ResponseData * ACE_CURL_EASY_SET_OPTION(curl, CURLOPT_POSTFIELDSIZE, requestData->GetBody().size(), responseData); } - if (EncodeUrlParam(curl, const_cast(requestData->GetUrl())) && MethodForGet(method)) { + if (EncodeUrlParam(const_cast(requestData->GetUrl())) && MethodForGet(method)) { requestData->SetHeader(HttpConstant::HTTP_HEADER_KEY_CONTENT_TYPE, HttpConstant::HTTP_CONTENT_TYPE_URL_ENCODE); } @@ -172,6 +236,215 @@ struct curl_slist *HttpRequest::MakeHeaders(const std::vector &vec) } return header; } +#else +bool HttpRequest::Request(RequestData *requestData, ResponseData *responseData) +{ + if (EncodeUrlParam(const_cast(requestData->GetUrl())) && MethodForGet(requestData->GetMethod())) { + requestData->SetHeader(HttpConstant::HTTP_HEADER_KEY_CONTENT_TYPE, HttpConstant::HTTP_CONTENT_TYPE_URL_ENCODE); + } + HTTP_REQUEST_INFO("final url %s", requestData->GetUrl().c_str()); + RequestHost host = ParseUrl(requestData->GetUrl()); + if (!host.parseOK) { + HTTP_RESPONSE_SET_ERR_STRING(responseData, "parse url failed"); + return false; + } + int sock = MakeTcpConnection(host, responseData); + if (sock < 0) { + return false; + } + + std::string rawData; + MakeHttpRawRequest(requestData, &host, rawData); + + if (!SendRequest(sock, rawData, responseData)) { + return false; + } + + std::string rawResponse; + if (!GetResponse(sock, rawResponse, responseData)) { + return false; + } + + responseData->ParseResponseData(rawResponse); + close(sock); + return responseData->GetCode() > 0; +} + +void HttpRequest::MakeHttpRawRequest(RequestData *requestData, RequestHost *requestHost, std::string &rawData) +{ + std::string requestLine = requestData->GetMethod() + HttpConstant::HTTP_RAW_WORD_SEPARATOR + requestHost->path; + if (!requestHost->param.empty()) { + requestLine += HttpConstant::HTTP_URL_PARAM_SEPARATOR + requestHost->param; + } + requestLine += HttpConstant::HTTP_RAW_WORD_SEPARATOR; + requestLine += HttpConstant::HTTP_RAW_PROTOCOL; + + rawData += requestLine + HttpConstant::HTTP_RAW_LINE_SEPARATOR; + + rawData += HttpConstant::HTTP_RAW_HOST_KEY; + rawData += HttpConstant::HTTP_RAW_HEADER_SEPARATOR + requestHost->namePort + HttpConstant::HTTP_RAW_LINE_SEPARATOR; + + for (const auto &p : requestData->GetHeader()) { + rawData += p.first + HttpConstant::HTTP_RAW_HEADER_SEPARATOR + p.second + HttpConstant::HTTP_RAW_LINE_SEPARATOR; + } + rawData += HttpConstant::HTTP_RAW_LINE_SEPARATOR; + + rawData.append(requestData->GetBody().c_str(), requestData->GetBody().size()); +} + +int HttpRequest::MakeTcpConnection(const RequestHost &requestHost, ResponseData *responseData) +{ + struct hostent *hh = gethostbyname(requestHost.hostName.c_str()); + if (hh == nullptr) { + HTTP_RESPONSE_SET_ERR_STRING(responseData, "get host error: %s", strerror(errno)); + return -1; + } + auto *addr = (struct in_addr *)(void *)hh->h_addr_list[0]; + struct sockaddr_in addrIn = {0}; + addrIn.sin_family = AF_INET; // only support IPv4 + addrIn.sin_port = htons(requestHost.hostPort); + addrIn.sin_addr.s_addr = addr->s_addr; + HTTP_REQUEST_INFO("host ip = %s", inet_ntoa(addrIn.sin_addr)); + + int sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock <= 0) { + HTTP_RESPONSE_SET_ERR_STRING(responseData, "create socket error: %s", strerror(errno)); + return -1; + } + struct timeval timeout = {HTTP_REQUEST_CONNECT_TIMEOUT, 0}; + setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (void *)&timeout, sizeof(timeout)); + setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (void *)&timeout, sizeof(timeout)); + if (connect(sock, reinterpret_cast(&addrIn), sizeof(addrIn)) < 0) { + HTTP_RESPONSE_SET_ERR_STRING(responseData, "connect error: %s", strerror(errno)); + close(sock); + return -1; + } + if (!MakeNonBlock(sock, responseData)) { + close(sock); + return -1; + } + if (!SetNoDelay(sock, responseData)) { + close(sock); + return -1; + } + return sock; +} + +bool HttpRequest::SendRequest(int sock, const std::string &rawData, ResponseData *responseData) +{ + auto curPos = rawData.c_str(); + auto leftSize = rawData.size(); + nfds_t num = 1; + struct pollfd fds[1] = {0}; + fds[0].fd = sock; + fds[0].events = 0; + fds[0].events |= POLLOUT; + + HTTP_REQUEST_INFO("begin send"); + while (leftSize > 0) { + int ret = poll(fds, num, HTTP_REQUEST_POLL_TIMEOUT); + if (ret == -1) { + HTTP_RESPONSE_SET_ERR_STRING(responseData, "poll to send failed %s", strerror(errno)); + return false; + } + if (ret == 0) { + HTTP_RESPONSE_SET_ERR_STRING(responseData, "poll to send timeout"); + return false; + } + auto sendLen = send(sock, curPos, leftSize, 0); + if (sendLen < 0) { + if (errno == EAGAIN) { + continue; + } + HTTP_RESPONSE_SET_ERR_STRING(responseData, "send failed %s", strerror(errno)); + return false; + } + if (sendLen == 0) { + break; + } + curPos += sendLen; + leftSize -= sendLen; + } + HTTP_REQUEST_INFO("after send"); + + if (leftSize != 0) { + HTTP_RESPONSE_SET_ERR_STRING(responseData, "send not complete"); + return false; + } + return true; +} + +bool HttpRequest::GetResponse(int sock, std::string &rawResponse, ResponseData *responseData) +{ + char buf[HTTP_REQUEST_BUF_LEN] = {0}; + nfds_t num = 1; + struct pollfd fds[1] = {0}; + fds[0].fd = sock; + fds[0].events = 0; + fds[0].events |= POLLIN; + + HTTP_REQUEST_INFO("begin recv"); + while (true) { + int ret = poll(fds, num, HTTP_REQUEST_POLL_TIMEOUT); + if (ret == -1) { + HTTP_RESPONSE_SET_ERR_STRING(responseData, "poll to recv failed %s", strerror(errno)); + return false; + } + if (ret == 0) { + HTTP_REQUEST_INFO("poll to recv timeout maybe no data to recv"); + break; + } + (void)memset_s(buf, sizeof(buf), 0, sizeof(buf)); + auto recvLen = recv(sock, buf, sizeof(buf), 0); + if (recvLen < 0) { + if (errno == EAGAIN) { + continue; + } + HTTP_RESPONSE_SET_ERR_STRING(responseData, "recv failed %s", strerror(errno)); + return false; + } + if (recvLen == 0) { + break; + } + rawResponse.append(buf, recvLen); + } + HTTP_REQUEST_INFO("after recv"); + + return true; +} + +bool HttpRequest::MakeNonBlock(int sock, ResponseData *responseData) +{ + int flags = fcntl(sock, F_GETFL, 0); + while (flags == -1 && errno == EINTR) { + flags = fcntl(sock, F_GETFL, 0); + } + if (flags == -1) { + HTTP_RESPONSE_SET_ERR_STRING(responseData, "make non block failed %s", strerror(errno)); + return false; + } + int ret = fcntl(sock, F_SETFL, flags | O_NONBLOCK); + while (ret == -1 && errno == EINTR) { + ret = fcntl(sock, F_SETFL, flags | O_NONBLOCK); + } + if (ret == -1) { + HTTP_RESPONSE_SET_ERR_STRING(responseData, "make non block failed %s", strerror(errno)); + return false; + } + return true; +} + +bool HttpRequest::SetNoDelay(int sock, ResponseData *responseData) +{ + int val = 1; + int rv = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void *)&val, (socklen_t)sizeof(val)); + if (rv == -1) { + HTTP_RESPONSE_SET_ERR_STRING(responseData, "set no delay failed %s", strerror(errno)); + return false; + } + return true; +} +#endif } // namespace ACELite } // namespace OHOS diff --git a/frameworks/js/builtin/http_request/http_request.h b/frameworks/js/builtin/http_request/http_request.h index c5b84d021..e67aafc62 100644 --- a/frameworks/js/builtin/http_request/http_request.h +++ b/frameworks/js/builtin/http_request/http_request.h @@ -16,14 +16,17 @@ #ifndef OHOS_ACELITE_HTTP_REQUEST_H #define OHOS_ACELITE_HTTP_REQUEST_H +#if HTTP_REQUEST_USE_CURL #include "curl/curl.h" +#else +#include "http_request_utils.h" +#endif #include "http_constant.h" #include "non_copyable.h" #include "request_data.h" #include "response_data.h" #include "securec.h" #include -#include #include namespace OHOS { @@ -40,6 +43,7 @@ public: static bool Request(RequestData *requestData, ResponseData *responseData); private: +#if HTTP_REQUEST_USE_CURL static bool Initialize(); static bool SetOption(RequestData *requestData, CURL *curl, ResponseData *responseData); @@ -49,11 +53,14 @@ private: static size_t OnWritingMemoryHeader(const void *data, size_t size, size_t memBytes, void *userData); static struct curl_slist *MakeHeaders(const std::vector &vec); - -private: - static std::mutex mutex; - - static bool initialized; +#else + static void MakeHttpRawRequest(RequestData *requestData, RequestHost *requestHost, std::string &rawData); + static int MakeTcpConnection(const RequestHost &requestHost, ResponseData *responseData); + static bool SendRequest(int sock, const std::string &rawData, ResponseData *responseData); + static bool GetResponse(int sock, std::string &rawResponse, ResponseData *responseData); + static bool MakeNonBlock(int sock, ResponseData *responseData); + static bool SetNoDelay(int sock, ResponseData *responseData); +#endif }; } // namespace ACELite diff --git a/frameworks/js/builtin/http_request/http_request_utils.cpp b/frameworks/js/builtin/http_request/http_request_utils.cpp index 080417f4a..b9fa5acd9 100644 --- a/frameworks/js/builtin/http_request/http_request_utils.cpp +++ b/frameworks/js/builtin/http_request/http_request_utils.cpp @@ -16,11 +16,28 @@ #include "http_request_utils.h" #include "http_constant.h" #include -#include #include namespace OHOS { namespace ACELite { + +static bool IsUnReserved(unsigned char in) +{ + if (('0' <= in && in <= '9') || ('a' <= in && in <= 'z') || ('A' <= in && in <= 'Z')) { + return true; + } + switch (in) { + case '-': + case '.': + case '_': + case '~': + return true; + default: + break; + } + return false; +} + std::vector Split(const std::string &str, const std::string &sep) { std::string s = str; @@ -37,22 +54,6 @@ std::vector Split(const std::string &str, const std::string &sep) return res; } -std::string Strip(const std::string &str, char ch) -{ - int64_t i = 0; - while (i < str.size() && str[i] == ch) { - ++i; - } - int64_t j = static_cast(str.size()) - 1; - while (j > 0 && str[j] == ch) { - --j; - } - if (i >= 0 && i < str.size() && j >= 0 && j < str.size() && j - i + 1 > 0) { - return str.substr(i, j - i + 1); - } - return ""; -} - bool MethodForGet(const std::string &method) { return (method == HttpConstant::HTTP_METHOD_HEAD || method == HttpConstant::HTTP_METHOD_OPTIONS || @@ -81,7 +82,7 @@ std::string MakeUrl(const std::string &url, std::string param, const std::string return url + HttpConstant::HTTP_URL_PARAM_SEPARATOR + param; } -bool EncodeUrlParam(CURL *curl, std::string &url) +bool EncodeUrlParam(std::string &url) { size_t index = url.find(HttpConstant::HTTP_URL_PARAM_SEPARATOR); if (index == std::string::npos) { @@ -93,13 +94,106 @@ bool EncodeUrlParam(CURL *curl, std::string &url) return false; } - std::unique_ptr encodeOut( - curl_easy_escape(curl, param.c_str(), static_cast(strlen(param.c_str()))), curl_free); - if (encodeOut == nullptr || strlen(encodeOut.get()) == 0) { - return false; + char encoded[4]; + std::string encodeOut; + for (size_t i = 0; i < strlen(param.c_str()); ++i) { + auto c = static_cast(param.c_str()[i]); + if (IsUnReserved(c)) { + encodeOut += static_cast(c); + } else { + if (sprintf_s(encoded, sizeof(encoded), "%%%02X", c) < 0) { + return false; + } + encodeOut += encoded; + } } - url = url.substr(0, index) + HttpConstant::HTTP_URL_PARAM_SEPARATOR + encodeOut.get(); + url = url.substr(0, index) + HttpConstant::HTTP_URL_PARAM_SEPARATOR + encodeOut; return true; } + +#if !HTTP_REQUEST_USE_CURL +RequestHost::RequestHost() : parseOK(false), hostPort(0) {} + +RequestHost ParseUrl(const std::string &url) +{ + RequestHost requestHost; + + std::string tempUrl = url; + size_t index = tempUrl.find(HttpConstant::HTTP_PROTOCOL_SEPARATOR); + if (index == std::string::npos) { + return requestHost; + } + + std::string protocol = tempUrl.substr(0, index); + if (protocol != HttpConstant::HTTP_PROTOCOL_HTTP) { + return requestHost; + } + tempUrl = tempUrl.substr(index + strlen(HttpConstant::HTTP_PROTOCOL_SEPARATOR)); + + std::string path = HttpConstant::HTTP_DEFAULT_PATH; + std::string param; + index = tempUrl.find(HttpConstant::HTTP_DEFAULT_PATH); + if (index != std::string::npos) { + path = tempUrl.substr(index); + tempUrl = tempUrl.substr(0, index); + size_t subIndex = path.find(HttpConstant::HTTP_URL_PARAM_SEPARATOR); + if (subIndex != std::string::npos) { + param = path.substr(subIndex + strlen(HttpConstant::HTTP_URL_PARAM_SEPARATOR)); + path = path.substr(0, subIndex); + } + } else { + size_t subIndex = tempUrl.find(HttpConstant::HTTP_URL_PARAM_SEPARATOR); + if (subIndex != std::string::npos) { + param = tempUrl.substr(subIndex + strlen(HttpConstant::HTTP_URL_PARAM_SEPARATOR)); + tempUrl = tempUrl.substr(0, subIndex); + } + } + + std::string hostName = tempUrl; + uint16_t hostPort = HttpConstant::HTTP_DEFAULT_PORT; + index = tempUrl.find(HttpConstant::HTTP_HOST_NAME_PORT_SEPARATOR); + if (index != std::string::npos) { + hostName = tempUrl.substr(0, index); + std::string portString = tempUrl.substr(index + strlen(HttpConstant::HTTP_HOST_NAME_PORT_SEPARATOR)); + char *end = nullptr; + auto tempPort = strtol(portString.c_str(), &end, 10); + if (tempPort <= 0 || tempPort > UINT16_MAX || (end != nullptr && *end != '\0')) { + return requestHost; + } + hostPort = static_cast(tempPort); + } + + requestHost.parseOK = true; + requestHost.hostName = hostName; + requestHost.hostPort = hostPort; + requestHost.namePort = tempUrl; + requestHost.path = path; + requestHost.param = param; + return requestHost; +} + +void ParseChunk(const std::string &chunk, std::string &data) +{ + size_t begin = 0; + size_t sepLen = strlen(HttpConstant::HTTP_RAW_LINE_SEPARATOR); + size_t nextIndex = chunk.find(HttpConstant::HTTP_RAW_LINE_SEPARATOR); + if (nextIndex == std::string::npos) { + return; + } + while (begin < chunk.size()) { + auto length = chunk.substr(begin, nextIndex - begin); + auto maxChunkSize = chunk.size() - nextIndex - sepLen * 2; + char *end = nullptr; + auto len = strtol(length.c_str(), &end, 16); + if ((end != nullptr && *end != '\0') || len > maxChunkSize) { + return; + } + std::string chunkData = chunk.substr(nextIndex + sepLen, len); + data.append(chunkData.c_str(), chunkData.size()); + begin = nextIndex + len + 2 * sepLen; + nextIndex = chunk.find(HttpConstant::HTTP_RAW_LINE_SEPARATOR, begin); + } +} +#endif } // namespace ACELite } // namespace OHOS \ No newline at end of file diff --git a/frameworks/js/builtin/http_request/http_request_utils.h b/frameworks/js/builtin/http_request/http_request_utils.h index d3332e194..77c4fb7df 100644 --- a/frameworks/js/builtin/http_request/http_request_utils.h +++ b/frameworks/js/builtin/http_request/http_request_utils.h @@ -17,7 +17,6 @@ #define OHOS_ACELITE_HTTP_REQUEST_UTILS_H #include "ace_log.h" -#include "curl/curl.h" #include "securec.h" #include #include @@ -35,9 +34,20 @@ namespace OHOS { namespace ACELite { -std::vector Split(const std::string &str, const std::string &sep); +#if !HTTP_REQUEST_USE_CURL +struct RequestHost { + bool parseOK; + std::string hostName; + uint16_t hostPort; + std::string path; + std::string namePort; + std::string param; + + RequestHost(); +}; +#endif -std::string Strip(const std::string &str, char ch = ' '); +std::vector Split(const std::string &str, const std::string &sep); bool MethodForGet(const std::string &method); @@ -45,7 +55,13 @@ bool MethodForPost(const std::string &method); std::string MakeUrl(const std::string &url, std::string param, const std::string &extraParam); -bool EncodeUrlParam(CURL *curl, std::string &url); +bool EncodeUrlParam(std::string &url); + +#if !HTTP_REQUEST_USE_CURL +RequestHost ParseUrl(const std::string &url); + +void ParseChunk(const std::string &chunk, std::string &data); +#endif } // namespace ACELite } // namespace OHOS diff --git a/frameworks/js/builtin/http_request/response_data.cpp b/frameworks/js/builtin/http_request/response_data.cpp index ca81d0109..a327f2d0a 100644 --- a/frameworks/js/builtin/http_request/response_data.cpp +++ b/frameworks/js/builtin/http_request/response_data.cpp @@ -15,6 +15,9 @@ #include "response_data.h" #include "http_request_utils.h" +#if !HTTP_REQUEST_USE_CURL +#include +#endif namespace OHOS { namespace ACELite { @@ -34,23 +37,80 @@ void ResponseData::SetErrString(const std::string &err) errString = err; } +#if HTTP_REQUEST_USE_CURL void ResponseData::ParseHeaders(const std::string &headersStr) { - std::vector vec = Split(headersStr, "\n"); - for (auto header : vec) { - header = Strip(Strip(header), '\r'); + std::vector vec = Split(headersStr, HttpConstant::HTTP_RAW_LINE_SEPARATOR); + bool statusLineGet = false; + for (const auto &header : vec) { if (header.empty()) { continue; } - size_t posColon = header.find(HttpConstant::HTTP_HEADER_SEPARATOR); - if (posColon == std::string::npos) { - statusLine = Strip(header); + size_t posColon = header.find(HttpConstant::HTTP_RAW_HEADER_SEPARATOR); + if (posColon == std::string::npos && !statusLineGet) { + statusLine = header; + statusLineGet = true; continue; } - std::vector temp = Split(header, HttpConstant::HTTP_HEADER_SEPARATOR); - headers[Strip(temp[0])] = Strip(temp[1]); + std::vector temp = Split(header, HttpConstant::HTTP_RAW_HEADER_SEPARATOR); + if (temp.size() >= 2) { + headers[temp[0]] = temp[1]; + } + } +} +#else +void ResponseData::ParseResponseData(const std::string &rawResponse) +{ + size_t sepLen = strlen(HttpConstant::HTTP_RAW_LINE_SEPARATOR); + size_t index = rawResponse.find(HttpConstant::HTTP_RAW_LINE_SEPARATOR); + if (index != std::string::npos) { + statusLine = rawResponse.substr(0, index); + auto vec = Split(statusLine, HttpConstant::HTTP_RAW_WORD_SEPARATOR); + if (vec.size() < 2) { // should like HTTP/1.1 200 OK\r\n + code = -1; + errString = "wrong status line: " + statusLine; + return; + } + char *end = nullptr; + code = static_cast(strtol(vec[1].c_str(), &end, 10)); + if (end != nullptr && *end != '\0') { + code = -1; + errString = "get response code from status line failed: " + statusLine; + return; + } + } + + size_t nextIndex = rawResponse.find(HttpConstant::HTTP_RAW_LINE_SEPARATOR, index + sepLen); + bool chunked = false; + while (nextIndex != std::string::npos) { + std::string line = rawResponse.substr(index + sepLen, nextIndex - (index + sepLen)); + if (line.empty()) { + break; + } + std::vector temp = Split(line, HttpConstant::HTTP_RAW_HEADER_SEPARATOR); + if (temp.size() >= 2) { + headers[temp[0]] = temp[1]; + std::transform(temp[0].begin(), temp[0].end(), temp[0].begin(), tolower); + std::transform(temp[1].begin(), temp[1].end(), temp[1].begin(), tolower); + if (!chunked) { + chunked = temp[0] == HttpConstant::HTTP_RAW_HEADER_TRANSFER_ENCODING && + temp[1] == HttpConstant::HTTP_RAW_HEADER_TRANSFER_ENCODING_CHUNKED; + } + } + index = nextIndex; + nextIndex = rawResponse.find(HttpConstant::HTTP_RAW_LINE_SEPARATOR, index + sepLen); + } + + if (nextIndex != std::string::npos) { + std::string chunk = rawResponse.substr(nextIndex + sepLen); + if (chunked) { + ParseChunk(chunk, data); + } else { + data.append(chunk.c_str(), chunk.size()); + } } } +#endif int32_t ResponseData::GetCode() const { diff --git a/frameworks/js/builtin/http_request/response_data.h b/frameworks/js/builtin/http_request/response_data.h index 56bd0e259..1b94b1dd3 100644 --- a/frameworks/js/builtin/http_request/response_data.h +++ b/frameworks/js/builtin/http_request/response_data.h @@ -31,7 +31,11 @@ public: void SetErrString(const std::string &err); +#if HTTP_REQUEST_USE_CURL void ParseHeaders(const std::string &headersStr); +#else + void ParseResponseData(const std::string &rawResponse); +#endif void AppendData(const char *data, size_t size); -- Gitee From fa365a15ef1e9a4583dcf4181f9b935ce6d0a446 Mon Sep 17 00:00:00 2001 From: maosiping Date: Tue, 23 Nov 2021 14:45:04 +0800 Subject: [PATCH 2/2] Fix Cmake Signed-off-by: maosiping --- frameworks/js/builtin/CMakeLists.txt | 1 + frameworks/js/builtin/test_fetch_module.cpp | 170 -------------------- 2 files changed, 1 insertion(+), 170 deletions(-) delete mode 100644 frameworks/js/builtin/test_fetch_module.cpp diff --git a/frameworks/js/builtin/CMakeLists.txt b/frameworks/js/builtin/CMakeLists.txt index 5c24ce69d..17add99fd 100644 --- a/frameworks/js/builtin/CMakeLists.txt +++ b/frameworks/js/builtin/CMakeLists.txt @@ -56,6 +56,7 @@ target_link_libraries(test_fetch_module jerry-libm) target_link_libraries(test_fetch_module jerry-port-default) target_link_libraries(test_fetch_module curl-d) target_link_libraries(test_fetch_module securec) +target_link_libraries(test_fetch_module pthread) add_compile_definitions(NO_SSL_CERTIFICATION=1) add_compile_definitions(HTTP_REQUEST_USE_CURL=1) diff --git a/frameworks/js/builtin/test_fetch_module.cpp b/frameworks/js/builtin/test_fetch_module.cpp deleted file mode 100644 index 723d71904..000000000 --- a/frameworks/js/builtin/test_fetch_module.cpp +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (c) 2021 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "fetch_module.h" -#include "http_request/http_async_callback.h" -#include "http_request/http_request_utils.h" -#include "jerryscript-core.h" -#include "js_async_work.h" -#include "message_queue_utils.h" - -#define FUNC_BEGIN() \ - do { \ - HTTP_REQUEST_INFO("%s BEGIN ##########", __FUNCTION__); \ - } while (0) - -#define FUNC_END_NO_NEW_LINE() \ - do { \ - HTTP_REQUEST_INFO("%s END ##########", __FUNCTION__); \ - } while (0) - -#define FUNC_END() \ - do { \ - HTTP_REQUEST_INFO("%s END ##########\n\n\n", __FUNCTION__); \ - } while (0) - -namespace OHOS { -namespace ACELite { -void InitFetchModule(JSIValue exports); -class JerryInitializer { -private: - int *temp; - JSIValue exports; - -public: - JerryInitializer() - { - temp = new int; - jerry_init(JERRY_INIT_EMPTY); - JsAsyncWork::SetAppQueueHandler(temp); - exports = JSI::CreateObject(); - InitFetchModule(exports); - } - - ~JerryInitializer() - { - jerry_cleanup(); - delete temp; - JSI::ReleaseValue(exports); - } -}; - -void TestPutMessage(void *data) -{ - auto msg = static_cast(data); - auto asyncWork = static_cast(msg->data); - asyncWork->workHandler(asyncWork->data); -} - -JSIValue TestCallbackOnSuccess(const JSIValue thisVal, const JSIValue *args, uint8_t argsNum) -{ - FUNC_BEGIN(); - (void)thisVal; - (void)argsNum; - - JSIValue para = args[0]; - HTTP_REQUEST_INFO("code = %d", - static_cast(JSI::GetNumberProperty(para, HttpConstant::KEY_HTTP_RESPONSE_CODE))); - - size_t size = 0; - char *data = JSI::GetStringProperty(para, HttpConstant::KEY_HTTP_RESPONSE_DATA, size); - std::string body; - for (uint32_t index = 0; index < size; ++index) { - if (data[index] != 0) { - body += data[index]; - } else { - body += "0"; - } - } - HTTP_REQUEST_INFO("%s", body.c_str()); - - JSIValue head = JSI::GetNamedProperty(para, HttpConstant::KEY_HTTP_RESPONSE_HEADERS); - - JSIValue keys = JSI::GetObjectKeys(head); - uint32_t length = JSI::GetArrayLength(keys); - for (uint32_t i = 0; i < length; ++i) { - JSIValue k = JSI::GetPropertyByIndex(keys, i); - char *s = JSI::ValueToString(k); - char *v = JSI::GetStringProperty(head, s); - HTTP_REQUEST_INFO("%s ---------------- %s", s, v); - } - - FUNC_END_NO_NEW_LINE(); - return JSI::CreateUndefined(); -} - -JSIValue TestCallbackOnFail(const JSIValue thisVal, const JSIValue *args, uint8_t argsNum) -{ - FUNC_BEGIN(); - (void)thisVal; - (void)argsNum; - - HTTP_REQUEST_INFO("err = %s", JSI::ValueToString(args[0])); - HTTP_REQUEST_INFO("code = %d", static_cast(JSI::ValueToNumber(args[1]))); - - FUNC_END_NO_NEW_LINE(); - return JSI::CreateUndefined(); -} - -JSIValue TestCallbackOnComplete(const JSIValue thisVal, const JSIValue *args, uint8_t argsNum) -{ - FUNC_BEGIN(); - (void)thisVal; - (void)args; - (void)argsNum; - - HTTP_REQUEST_INFO("request complete"); - - FUNC_END_NO_NEW_LINE(); - return JSI::CreateUndefined(); -} - -void TestHttpModuleMethodAndHeaderByDefault() -{ - FUNC_BEGIN(); - - JSIValue object = JSI::CreateObject(); - if (object == nullptr) { - return; - } - - JSIValue header = JSI::CreateObject(); - JSI::SetStringProperty(header, "no-use", "test value"); - JSI::SetNamedProperty(object, HttpConstant::KEY_HTTP_REQUEST_HEADER, header); - - JSIValue url = JSI::CreateString("https://www.zhihu.com"); - JSI::SetNamedProperty(object, HttpConstant::KEY_HTTP_REQUEST_URL, url); - - JSI::SetNamedProperty(object, CB_SUCCESS, JSI::CreateFunction(TestCallbackOnSuccess)); - JSI::SetNamedProperty(object, CB_FAIL, JSI::CreateFunction(TestCallbackOnFail)); - JSI::SetNamedProperty(object, CB_COMPLETE, JSI::CreateFunction(TestCallbackOnComplete)); - - JSIValue arg[1] = {object}; - FetchModule::Fetch(nullptr, arg, 1); - - FUNC_END(); -} - -} // namespace ACELite -} // namespace OHOS - -int main() -{ - OHOS::ACELite::JerryInitializer jerryInitializer; - - OHOS::ACELite::TestHttpModuleMethodAndHeaderByDefault(); - - return 0; -} \ No newline at end of file -- Gitee