diff --git a/.gitmodules b/.gitmodules index 563e5145..302f5629 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "3rd/libhv/libhv"] - path = 3rd/libhv/libhv - url = https://github.com/ithewei/libhv.git [submodule "3rd/lz4/lz4"] path = 3rd/lz4/lz4 url = https://github.com/lz4/lz4 diff --git a/3rd/libhv/CMakeLists.txt b/3rd/libhv/CMakeLists.txt deleted file mode 100644 index f17a066c..00000000 --- a/3rd/libhv/CMakeLists.txt +++ /dev/null @@ -1,42 +0,0 @@ -cmake_minimum_required(VERSION 3.10) - -project(libhv_bindings) - -set(CMAKE_C_STANDARD 11) -set(CMAKE_C_STANDARD_REQUIRED ON) -set(CMAKE_CXX_STANDARD 14) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -option(BUILD_SHARED "build shared library" OFF) -option(BUILD_STATIC "build static library" ON) -option(BUILD_EXAMPLES "build examples" OFF) - -option(WITH_OPENSSL "with openssl library" OFF) - -add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/libhv) - -AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_LIST_DIR}/src LIBHV_BINDINGS_SRC) - -add_library(${PROJECT_NAME} STATIC ${LIBHV_BINDINGS_SRC}) - -target_link_libraries(${PROJECT_NAME} hv_static) - -# define WITHOUT_HTTP_CONTENT -target_compile_definitions(libhv_bindings PRIVATE WITHOUT_HTTP_CONTENT) -target_compile_definitions(hv_static PRIVATE WITHOUT_HTTP_CONTENT) - -target_include_directories(${PROJECT_NAME} PRIVATE - ${CMAKE_CURRENT_LIST_DIR}/../../include - ${CMAKE_CURRENT_LIST_DIR}/include - ${CMAKE_CURRENT_LIST_DIR}/libhv - ${CMAKE_CURRENT_LIST_DIR}/libhv/base - ${CMAKE_CURRENT_LIST_DIR}/libhv/evpp - ${CMAKE_CURRENT_LIST_DIR}/libhv/event - ${CMAKE_CURRENT_LIST_DIR}/libhv/http - ${CMAKE_CURRENT_LIST_DIR}/libhv/ssl - ${CMAKE_CURRENT_LIST_DIR}/libhv/cpputil -) - -target_include_directories(${PROJECT_NAME} INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/include -) \ No newline at end of file diff --git a/3rd/libhv/compile_flags.txt b/3rd/libhv/compile_flags.txt deleted file mode 100644 index 01d80705..00000000 --- a/3rd/libhv/compile_flags.txt +++ /dev/null @@ -1,15 +0,0 @@ --xc++ --std=c++14 - --I../../include - --Iinclude/ --Ilibhv/ --Ilibhv/base/ --Ilibhv/evpp/ --Ilibhv/event/ --Ilibhv/http/ --Ilibhv/ssl/ --Ilibhv/cpputil/ - --DWITHOUT_HTTP_CONTENT \ No newline at end of file diff --git a/3rd/libhv/include/libhv_bindings.hpp b/3rd/libhv/include/libhv_bindings.hpp deleted file mode 100644 index b9c459a5..00000000 --- a/3rd/libhv/include/libhv_bindings.hpp +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once - -#include "pocketpy.h" -#include "http/HttpMessage.h" -#include "base/hplatform.h" - -#include "pocketpy/common/name.h" - -extern "C" void pk__add_module_libhv(); - -void libhv_HttpRequest_create(py_OutRef out, HttpRequestPtr ptr); - -py_Type libhv_register_HttpRequest(py_GlobalRef mod); -py_Type libhv_register_HttpClient(py_GlobalRef mod); -py_Type libhv_register_HttpServer(py_GlobalRef mod); -py_Type libhv_register_WebSocketClient(py_GlobalRef mod); - -#include -#include -#include - -template -class libhv_MQ { -private: - std::atomic lock; - std::deque queue; - -public: - void push(T msg) { - while(lock.exchange(true)) { - std::this_thread::yield(); - } - queue.push_back(msg); - lock.store(false); - } - - bool pop(T* msg) { - while(lock.exchange(true)) { - std::this_thread::yield(); - } - if(queue.empty()) { - lock.store(false); - return false; - } - *msg = queue.front(); - queue.pop_front(); - lock.store(false); - return true; - } -}; - -enum class WsMessageType { - onopen, - onclose, - onmessage, -}; diff --git a/3rd/libhv/libhv b/3rd/libhv/libhv deleted file mode 160000 index fded2ba2..00000000 --- a/3rd/libhv/libhv +++ /dev/null @@ -1 +0,0 @@ -Subproject commit fded2ba287199d3c20c619e06ddc571e2972ed92 diff --git a/3rd/libhv/src/HttpClient.cpp b/3rd/libhv/src/HttpClient.cpp deleted file mode 100644 index c1a7e0ff..00000000 --- a/3rd/libhv/src/HttpClient.cpp +++ /dev/null @@ -1,318 +0,0 @@ -#include "HttpMessage.h" -#include "libhv_bindings.hpp" -#include "base/herr.h" -#include "http/client/HttpClient.h" - -struct libhv_HttpResponse { - HttpRequestPtr request; - HttpResponsePtr response; - bool ok; - - bool is_valid() { return ok && response != NULL; } - - libhv_HttpResponse(HttpRequestPtr request) : request(request), response(NULL), ok(false) {} -}; - -static bool libhv_HttpResponse_status_code(int argc, py_Ref argv) { - PY_CHECK_ARGC(1); - libhv_HttpResponse* resp = (libhv_HttpResponse*)py_touserdata(argv); - if(!resp->is_valid()) return RuntimeError("HttpResponse: no response"); - py_newint(py_retval(), resp->response->status_code); - return true; -}; - -static bool libhv_HttpResponse_headers(int argc, py_Ref argv) { - PY_CHECK_ARGC(1); - libhv_HttpResponse* resp = (libhv_HttpResponse*)py_touserdata(argv); - if(!resp->is_valid()) return RuntimeError("HttpResponse: no response"); - py_Ref headers = py_getslot(argv, 0); - if(py_isnil(headers)) { - py_newdict(headers); - py_Ref _0 = py_pushtmp(); - py_Ref _1 = py_pushtmp(); - for(auto& kv: resp->response->headers) { - py_newstr(_0, kv.first.c_str()); - py_newstr(_1, kv.second.c_str()); - py_dict_setitem(headers, _0, _1); - } - py_shrink(2); - } - py_assign(py_retval(), headers); - return true; -}; - -static bool libhv_HttpResponse_text(int argc, py_Ref argv) { - PY_CHECK_ARGC(1); - libhv_HttpResponse* resp = (libhv_HttpResponse*)py_touserdata(argv); - if(!resp->is_valid()) return RuntimeError("HttpResponse: no response"); - py_Ref text = py_getslot(argv, 1); - if(py_isnil(text)) { - c11_sv sv; - sv.data = resp->response->body.c_str(); - sv.size = resp->response->body.size(); - py_newstrv(text, sv); - } - py_assign(py_retval(), text); - return true; -}; - -static bool libhv_HttpResponse_content(int argc, py_Ref argv) { - PY_CHECK_ARGC(1); - libhv_HttpResponse* resp = (libhv_HttpResponse*)py_touserdata(argv); - if(!resp->is_valid()) return RuntimeError("HttpResponse: no response"); - py_Ref content = py_getslot(argv, 2); - if(py_isnil(content)) { - int size = resp->response->body.size(); - unsigned char* buf = py_newbytes(content, size); - memcpy(buf, resp->response->body.data(), size); - } - py_assign(py_retval(), content); - return true; -}; - -static bool libhv_HttpResponse_json(int argc, py_Ref argv) { - PY_CHECK_ARGC(1); - libhv_HttpResponse* resp = (libhv_HttpResponse*)py_touserdata(argv); - if(!resp->is_valid()) return RuntimeError("HttpResponse: no response"); - const char* source = resp->response->body.c_str(); // json string is null-terminated - return py_json_loads(source); -}; - -static bool libhv_HttpResponse_completed(int argc, py_Ref argv) { - PY_CHECK_ARGC(1); - libhv_HttpResponse* resp = (libhv_HttpResponse*)py_touserdata(argv); - py_newbool(py_retval(), resp->is_valid()); - return true; -} - -static bool libhv_HttpResponse__new__(int argc, py_Ref argv) { - return py_exception(tp_NotImplementedError, ""); -} - -static bool libhv_HttpResponse__iter__(int argc, py_Ref argv) { - PY_CHECK_ARGC(1); - py_assign(py_retval(), argv); - return true; -} - -static bool libhv_HttpResponse__next__(int argc, py_Ref argv) { - PY_CHECK_ARGC(1); - libhv_HttpResponse* resp = (libhv_HttpResponse*)py_touserdata(argv); - if(!resp->is_valid()) { - py_newnone(py_retval()); - return true; - } else { - if(!py_tpcall(tp_StopIteration, 1, argv)) return false; - return py_raise(py_retval()); - } -} - -static bool libhv_HttpResponse__repr__(int argc, py_Ref argv) { - PY_CHECK_ARGC(1); - libhv_HttpResponse* resp = (libhv_HttpResponse*)py_touserdata(argv); - if(!resp->is_valid()) { - py_newstr(py_retval(), ""); - } else { - py_newfstr(py_retval(), "", (int)resp->response->status_code); - } - return true; -} - -static bool libhv_HttpResponse_cancel(int argc, py_Ref argv) { - PY_CHECK_ARGC(1); - libhv_HttpResponse* resp = (libhv_HttpResponse*)py_touserdata(argv); - resp->request->Cancel(); - py_newnone(py_retval()); - return true; -} - -static py_Type libhv_register_HttpResponse(py_GlobalRef mod) { - py_Type type = py_newtype("HttpResponse", tp_object, mod, [](void* ud) { - ((libhv_HttpResponse*)ud)->~libhv_HttpResponse(); - }); - - py_bindproperty(type, "status_code", libhv_HttpResponse_status_code, NULL); - py_bindproperty(type, "headers", libhv_HttpResponse_headers, NULL); - py_bindproperty(type, "text", libhv_HttpResponse_text, NULL); - py_bindproperty(type, "content", libhv_HttpResponse_content, NULL); - py_bindmethod(type, "json", libhv_HttpResponse_json); - - py_bindmagic(type, __new__, libhv_HttpResponse__new__); - py_bindmagic(type, __iter__, libhv_HttpResponse__iter__); - py_bindmagic(type, __next__, libhv_HttpResponse__next__); - py_bindmagic(type, __repr__, libhv_HttpResponse__repr__); - // completed - py_bindproperty(type, "completed", libhv_HttpResponse_completed, NULL); - // cancel - py_bindmethod(type, "cancel", libhv_HttpResponse_cancel); - return type; -} - -static bool libhv_HttpClient__send_request(py_Ref arg_self, - enum http_method method, - py_Ref arg_url, - py_Ref arg_params, - py_Ref arg_headers, - py_Ref arg_data, - py_Ref arg_json, - py_Ref arg_timeout) { - hv::HttpClient* cli = (hv::HttpClient*)py_touserdata(arg_self); - if(!py_checkstr(arg_url)) return false; - const char* url = py_tostr(arg_url); - if(!py_checkint(arg_timeout)) return false; - int timeout = py_toint(arg_timeout); - - auto req = std::make_shared(); - req->method = method; - req->url = url; - if(!py_isnone(arg_params)) { - if(!py_checktype(arg_params, tp_dict)) return false; - - bool ok = py_dict_apply( - arg_params, - [](py_Ref key, py_Ref value, void* ctx) { - HttpRequest* p_req = (HttpRequest*)ctx; - if(!py_checkstr(key)) return false; - if(!py_str(value)) return false; // key: str(value) - p_req->SetParam(py_tostr(key), py_tostr(py_retval())); - return true; - }, - req.get()); - if(!ok) return false; - } - - req->headers["Connection"] = "keep-alive"; - - if(!py_isnone(arg_headers)) { - if(!py_checktype(arg_headers, tp_dict)) return false; - - bool ok = py_dict_apply( - arg_headers, - [](py_Ref key, py_Ref value, void* ctx) { - HttpRequest* p_req = (HttpRequest*)ctx; - if(!py_checkstr(key)) return false; - if(!py_str(value)) return false; // key: str(value) - p_req->headers[py_tostr(key)] = py_tostr(py_retval()); - return true; - }, - req.get()); - if(!ok) return false; - } - - if(!py_isnone(arg_data)) { - // data must be str or bytes - if(py_istype(arg_data, tp_str)) { - req->body = py_tostr(arg_data); - } else if(py_istype(arg_data, tp_bytes)) { - int size; - unsigned char* buf = py_tobytes(arg_data, &size); - req->body.assign((const char*)buf, size); - } else { - return TypeError("HttpClient: data must be str or bytes"); - } - } - - if(!py_isnone(arg_json)) { - if(!py_isnone(arg_data)) { - return ValueError("HttpClient: data and json cannot be set at the same time"); - } - - if(!py_json_dumps(arg_json, 0)) return false; - req->body = py_tostr(py_retval()); - req->headers["Content-Type"] = "application/json"; - } - - req->timeout = timeout; - - libhv_HttpResponse* retval = - (libhv_HttpResponse*)py_newobject(py_retval(), - py_gettype("libhv", py_name("HttpResponse")), - 3, // headers, text, content - sizeof(libhv_HttpResponse)); - // placement new - new (retval) libhv_HttpResponse(req); - - int code = cli->sendAsync(req, [retval](const HttpResponsePtr& resp) { - if(resp == NULL) { - retval->ok = false; - retval->response = NULL; - } else { - retval->ok = true; - retval->response = resp; - } - }); - if(code != 0) { - const char* msg = hv_strerror(code); - return RuntimeError("HttpClient: %s (%d)", msg, code); - } - return true; -} - -py_Type libhv_register_HttpClient(py_GlobalRef mod) { - py_Type type = py_newtype("HttpClient", tp_object, mod, [](void* ud) { - ((hv::HttpClient*)ud)->~HttpClient(); - }); - py_GlobalRef type_object = py_tpobject(type); - libhv_register_HttpResponse(mod); - - py_bindmagic(type, __new__, [](int argc, py_Ref argv) { - PY_CHECK_ARGC(1); - hv::HttpClient* ud = - (hv::HttpClient*)py_newobject(py_retval(), py_totype(argv), 0, sizeof(hv::HttpClient)); - new (ud) hv::HttpClient(); - return true; - }); - - py_bind(type_object, - "get(self, url: str, params=None, headers=None, timeout=10)", - [](int argc, py_Ref argv) { - return libhv_HttpClient__send_request(py_arg(0), // self - HTTP_GET, // method - py_arg(1), // url - py_arg(2), // params - py_arg(3), // headers - py_None(), // data - py_None(), // json - py_arg(4)); // timeout - }); - - py_bind(type_object, - "post(self, url: str, params=None, headers=None, data=None, json=None, timeout=10)", - [](int argc, py_Ref argv) { - return libhv_HttpClient__send_request(py_arg(0), // self - HTTP_POST, // method - py_arg(1), // url - py_arg(2), // params - py_arg(3), // headers - py_arg(4), // data - py_arg(5), // json - py_arg(6)); // timeout - }); - - py_bind(type_object, - "put(self, url: str, params=None, headers=None, data=None, json=None, timeout=10)", - [](int argc, py_Ref argv) { - return libhv_HttpClient__send_request(py_arg(0), // self - HTTP_PUT, // method - py_arg(1), // url - py_arg(2), // params - py_arg(3), // headers - py_arg(4), // data - py_arg(5), // json - py_arg(6)); // timeout - }); - - py_bind(type_object, - "delete(self, url: str, params=None, headers=None, timeout=10)", - [](int argc, py_Ref argv) { - return libhv_HttpClient__send_request(py_arg(0), // self - HTTP_DELETE, // method - py_arg(1), // url - py_arg(2), // params - py_arg(3), // headers - py_None(), // data - py_None(), // json - py_arg(4)); // timeout - }); - return type; -} diff --git a/3rd/libhv/src/HttpRequest.cpp b/3rd/libhv/src/HttpRequest.cpp deleted file mode 100644 index 7538f92e..00000000 --- a/3rd/libhv/src/HttpRequest.cpp +++ /dev/null @@ -1,114 +0,0 @@ -#include "libhv_bindings.hpp" -#include "HttpMessage.h" - -struct libhv_HttpRequest { - HttpRequestPtr ptr; - - libhv_HttpRequest(HttpRequestPtr ptr) : ptr(ptr) {} -}; - -void libhv_HttpRequest_create(py_OutRef out, HttpRequestPtr ptr) { - py_Type type = py_gettype("libhv", py_name("HttpRequest")); - libhv_HttpRequest* self = - (libhv_HttpRequest*)py_newobject(out, type, 2, sizeof(libhv_HttpRequest)); - new (self) libhv_HttpRequest(ptr); -} - -py_Type libhv_register_HttpRequest(py_GlobalRef mod) { - py_Type type = py_newtype("HttpRequest", tp_object, mod, [](void* ud) { - ((libhv_HttpRequest*)ud)->~libhv_HttpRequest(); - }); - - py_bindmagic(type, __new__, [](int argc, py_Ref argv) { - return py_exception(tp_NotImplementedError, ""); - }); - - py_bindproperty( - type, - "method", - [](int argc, py_Ref argv) { - PY_CHECK_ARGC(1); - libhv_HttpRequest* req = (libhv_HttpRequest*)py_touserdata(argv); - py_newstr(py_retval(), req->ptr->Method()); - return true; - }, - NULL); - - py_bindproperty( - type, - "url", - [](int argc, py_Ref argv) { - PY_CHECK_ARGC(1); - libhv_HttpRequest* req = (libhv_HttpRequest*)py_touserdata(argv); - py_newstr(py_retval(), req->ptr->Url().c_str()); - return true; - }, - NULL); - - py_bindproperty( - type, - "path", - [](int argc, py_Ref argv) { - PY_CHECK_ARGC(1); - libhv_HttpRequest* req = (libhv_HttpRequest*)py_touserdata(argv); - py_newstr(py_retval(), req->ptr->Path().c_str()); - return true; - }, - NULL); - - // headers (cache in slots[0]) - py_bindproperty( - type, - "headers", - [](int argc, py_Ref argv) { - PY_CHECK_ARGC(1); - libhv_HttpRequest* req = (libhv_HttpRequest*)py_touserdata(argv); - py_Ref headers = py_getslot(argv, 0); - if(py_isnil(headers)) { - py_newdict(headers); - py_Ref _0 = py_pushtmp(); - py_Ref _1 = py_pushtmp(); - for(auto& kv: req->ptr->headers) { - py_newstr(_0, kv.first.c_str()); // TODO: tolower - py_newstr(_1, kv.second.c_str()); - py_dict_setitem(headers, _0, _1); - } - py_shrink(2); - } - py_assign(py_retval(), headers); - return true; - }, - NULL); - - // data (cache in slots[1]) - py_bindproperty( - type, - "data", - [](int argc, py_Ref argv) { - PY_CHECK_ARGC(1); - libhv_HttpRequest* req = (libhv_HttpRequest*)py_touserdata(argv); - py_Ref data = py_getslot(argv, 1); - - if(py_isnil(data)) { - auto content_type = req->ptr->ContentType(); - bool is_text_data = content_type == TEXT_PLAIN || - content_type == APPLICATION_JSON || - content_type == APPLICATION_XML || content_type == TEXT_HTML || - content_type == CONTENT_TYPE_NONE; - if(is_text_data) { - c11_sv sv; - sv.data = req->ptr->body.data(); - sv.size = req->ptr->body.size(); - py_newstrv(data, sv); - } else { - unsigned char* buf = py_newbytes(data, req->ptr->body.size()); - memcpy(buf, req->ptr->body.data(), req->ptr->body.size()); - } - } - py_assign(py_retval(), data); - return true; - }, - NULL); - - return type; -} \ No newline at end of file diff --git a/3rd/libhv/src/HttpServer.cpp b/3rd/libhv/src/HttpServer.cpp deleted file mode 100644 index 07bdaa60..00000000 --- a/3rd/libhv/src/HttpServer.cpp +++ /dev/null @@ -1,260 +0,0 @@ -#include "HttpMessage.h" -#include "WebSocketChannel.h" -#include "libhv_bindings.hpp" -#include "http/server/WebSocketServer.h" -#include "pocketpy.h" - -struct libhv_HttpServer { - hv::HttpService http_service; - hv::WebSocketService ws_service; - hv::WebSocketServer server; - - libhv_MQ>*> mq; - - struct WsMessage { - WsMessageType type; - hv::WebSocketChannel* channel; - HttpRequestPtr request; - std::string body; - }; - - libhv_MQ ws_mq; -}; - -static bool libhv_HttpServer__new__(int argc, py_Ref argv) { - libhv_HttpServer* self = - (libhv_HttpServer*)py_newobject(py_retval(), py_totype(argv), 0, sizeof(libhv_HttpServer)); - new (self) libhv_HttpServer(); - return true; -} - -static bool libhv_HttpServer__init__(int argc, py_Ref argv) { - PY_CHECK_ARGC(3); - libhv_HttpServer* self = (libhv_HttpServer*)py_touserdata(py_arg(0)); - PY_CHECK_ARG_TYPE(1, tp_str); - PY_CHECK_ARG_TYPE(2, tp_int); - const char* host = py_tostr(py_arg(1)); - int port = py_toint(py_arg(2)); - self->server.setHost(host); - self->server.setPort(port); - - // http - self->http_service.AllowCORS(); - http_ctx_handler internal_handler = [self](const HttpContextPtr& ctx) { - std::pair> msg(ctx, 0); - self->mq.push(&msg); - int code; - do { - code = msg.second.load(); - } while(code == 0); - return code; - }; - self->http_service.Any("*", internal_handler); - self->server.registerHttpService(&self->http_service); - - // websocket - self->ws_service.onopen = [self](const WebSocketChannelPtr& channel, - const HttpRequestPtr& req) { - self->ws_mq.push({WsMessageType::onopen, channel.get(), req, ""}); - }; - self->ws_service.onmessage = [self](const WebSocketChannelPtr& channel, - const std::string& msg) { - self->ws_mq.push({WsMessageType::onmessage, channel.get(), nullptr, msg}); - }; - self->ws_service.onclose = [self](const WebSocketChannelPtr& channel) { - self->ws_mq.push({WsMessageType::onclose, channel.get(), nullptr, ""}); - }; - self->server.registerWebSocketService(&self->ws_service); - - py_newnone(py_retval()); - return true; -} - -static bool libhv_HttpServer_dispatch(int argc, py_Ref argv) { - PY_CHECK_ARGC(2); - libhv_HttpServer* self = (libhv_HttpServer*)py_touserdata(py_arg(0)); - py_Ref callable = py_arg(1); - if(!py_callable(callable)) return TypeError("dispatcher must be callable"); - - std::pair>* mq_msg; - if(!self->mq.pop(&mq_msg)) { - py_newbool(py_retval(), false); - return true; - } else { - HttpContextPtr ctx = mq_msg->first; - libhv_HttpRequest_create(py_retval(), ctx->request); - // call dispatcher - if(!py_call(callable, 1, py_retval())) return false; - - py_Ref object; - int status_code = 200; - if(py_istuple(py_retval())) { - int length = py_tuple_len(py_retval()); - if(length == 2 || length == 3) { - // "Hello, world!", 200 - object = py_tuple_getitem(py_retval(), 0); - py_ItemRef status_code_object = py_tuple_getitem(py_retval(), 1); - if(!py_checkint(status_code_object)) return false; - status_code = py_toint(status_code_object); - - if(length == 3) { - // "Hello, world!", 200, {"Content-Type": "text/plain"} - py_ItemRef headers_object = py_tuple_getitem(py_retval(), 2); - if(!py_checktype(headers_object, tp_dict)) return false; - bool ok = py_dict_apply( - headers_object, - [](py_Ref key, py_Ref value, void* ctx_) { - if(!py_checkstr(key) || !py_checkstr(value)) return false; - ((hv::HttpContext*)ctx_) - ->response->SetHeader(py_tostr(key), py_tostr(value)); - return true; - }, - ctx.get()); - if(!ok) return false; - } - } else { - return TypeError("dispatcher return tuple must have 2 or 3 elements"); - } - } else { - // "Hello, world!" - object = py_retval(); - } - - switch(py_typeof(object)) { - case tp_bytes: { - int size; - unsigned char* buf = py_tobytes(object, &size); - ctx->response->Data(buf, size, false); - break; - } - case tp_str: { - c11_sv sv = py_tosv(object); - ctx->response->String(std::string(sv.data, sv.size)); - break; - } - case tp_NoneType: { - break; - } - default: { - if(!py_json_dumps(object, 0)) return false; - c11_sv sv = py_tosv(py_retval()); - ctx->response->String(std::string(sv.data, sv.size)); - ctx->response->SetContentType(APPLICATION_JSON); - break; - } - } - - mq_msg->second.store(status_code); - } - py_newbool(py_retval(), true); - return true; -} - -static bool libhv_HttpServer_start(int argc, py_Ref argv) { - PY_CHECK_ARGC(1); - libhv_HttpServer* self = (libhv_HttpServer*)py_touserdata(py_arg(0)); - int code = self->server.start(); - py_newint(py_retval(), code); - return true; -} - -static bool libhv_HttpServer_stop(int argc, py_Ref argv) { - PY_CHECK_ARGC(1); - libhv_HttpServer* self = (libhv_HttpServer*)py_touserdata(py_arg(0)); - int code = self->server.stop(); - py_newint(py_retval(), code); - return true; -} - -static bool libhv_HttpServer_ws_set_ping_interval(int argc, py_Ref argv) { - PY_CHECK_ARGC(2); - libhv_HttpServer* self = (libhv_HttpServer*)py_touserdata(py_arg(0)); - PY_CHECK_ARG_TYPE(1, tp_int); - int interval = py_toint(py_arg(1)); - self->ws_service.setPingInterval(interval); - py_newnone(py_retval()); - return true; -} - -static bool libhv_HttpServer_ws_close(int argc, py_Ref argv) { - PY_CHECK_ARGC(2); - libhv_HttpServer* self = (libhv_HttpServer*)py_touserdata(py_arg(0)); - PY_CHECK_ARG_TYPE(1, tp_int); - py_i64 channel = py_toint(py_arg(1)); - int code = reinterpret_cast(channel)->close(); - py_newint(py_retval(), code); - return true; -} - -static bool libhv_HttpServer_ws_send(int argc, py_Ref argv) { - PY_CHECK_ARGC(3); - libhv_HttpServer* self = (libhv_HttpServer*)py_touserdata(py_arg(0)); - PY_CHECK_ARG_TYPE(1, tp_int); - PY_CHECK_ARG_TYPE(2, tp_str); - py_i64 channel = py_toint(py_arg(1)); - const char* msg = py_tostr(py_arg(2)); - - hv::WebSocketChannel* p_channel = reinterpret_cast(channel); - int code = p_channel->send(msg); - py_newint(py_retval(), code); - return true; -} - -static bool libhv_HttpServer_ws_recv(int argc, py_Ref argv) { - PY_CHECK_ARGC(1); - libhv_HttpServer* self = (libhv_HttpServer*)py_touserdata(py_arg(0)); - libhv_HttpServer::WsMessage msg; - if(!self->ws_mq.pop(&msg)) { - py_newnone(py_retval()); - return true; - } - py_Ref data = py_newtuple(py_retval(), 2); - switch(msg.type) { - case WsMessageType::onopen: { - // "onopen", (channel, request) - assert(msg.request != nullptr); - py_newstr(py_offset(data, 0), "onopen"); - py_Ref p = py_newtuple(py_offset(data, 1), 2); - py_newint(py_offset(p, 0), (py_i64)msg.channel); - libhv_HttpRequest_create(py_offset(p, 1), msg.request); - break; - } - case WsMessageType::onclose: { - // "onclose", channel - py_newstr(py_offset(data, 0), "onclose"); - py_newint(py_offset(data, 1), (py_i64)msg.channel); - break; - } - case WsMessageType::onmessage: { - // "onmessage", (channel, body) - py_newstr(py_offset(data, 0), "onmessage"); - py_Ref p = py_newtuple(py_offset(data, 1), 2); - py_newint(py_offset(p, 0), (py_i64)msg.channel); - c11_sv sv; - sv.data = msg.body.data(); - sv.size = msg.body.size(); - py_newstrv(py_offset(p, 1), sv); - break; - } - } - return true; -} - -py_Type libhv_register_HttpServer(py_GlobalRef mod) { - py_Type type = py_newtype("HttpServer", tp_object, mod, [](void* ud) { - libhv_HttpServer* self = (libhv_HttpServer*)ud; - self->~libhv_HttpServer(); - }); - - py_bindmagic(type, __new__, libhv_HttpServer__new__); - py_bindmagic(type, __init__, libhv_HttpServer__init__); - py_bindmethod(type, "start", libhv_HttpServer_start); - py_bindmethod(type, "stop", libhv_HttpServer_stop); - py_bindmethod(type, "dispatch", libhv_HttpServer_dispatch); - - py_bindmethod(type, "ws_set_ping_interval", libhv_HttpServer_ws_set_ping_interval); - py_bindmethod(type, "ws_close", libhv_HttpServer_ws_close); - py_bindmethod(type, "ws_send", libhv_HttpServer_ws_send); - py_bindmethod(type, "ws_recv", libhv_HttpServer_ws_recv); - return type; -} diff --git a/3rd/libhv/src/WebSocketClient.cpp b/3rd/libhv/src/WebSocketClient.cpp deleted file mode 100644 index 5e3c48b2..00000000 --- a/3rd/libhv/src/WebSocketClient.cpp +++ /dev/null @@ -1,124 +0,0 @@ -#include "HttpMessage.h" -#include "libhv_bindings.hpp" -#include "pocketpy.h" -#include "http/client/WebSocketClient.h" - -struct libhv_WebSocketClient { - hv::WebSocketClient ws; - - libhv_MQ> mq; - - libhv_WebSocketClient() { - ws.onopen = [this]() { - mq.push({WsMessageType::onopen, ""}); - }; - ws.onclose = [this]() { - mq.push({WsMessageType::onclose, ""}); - }; - ws.onmessage = [this](const std::string& msg) { - mq.push({WsMessageType::onmessage, msg}); - }; - - // reconnect: 1,2,4,8,10,10,10... - reconn_setting_t reconn; - reconn_setting_init(&reconn); - reconn.min_delay = 1000; - reconn.max_delay = 10000; - reconn.delay_policy = 2; - ws.setReconnect(&reconn); - } -}; - -py_Type libhv_register_WebSocketClient(py_GlobalRef mod) { - py_Type type = py_newtype("WebSocketClient", tp_object, mod, [](void* ud) { - libhv_WebSocketClient* self = (libhv_WebSocketClient*)ud; - self->~libhv_WebSocketClient(); - }); - - py_bindmagic(type, __new__, [](int argc, py_Ref argv) { - PY_CHECK_ARGC(1); - libhv_WebSocketClient* self = (libhv_WebSocketClient*) - py_newobject(py_retval(), py_totype(argv), 0, sizeof(libhv_WebSocketClient)); - new (self) libhv_WebSocketClient(); - return true; - }); - - py_bindmethod(type, "open", [](int argc, py_Ref argv) { - libhv_WebSocketClient* self = (libhv_WebSocketClient*)py_touserdata(argv); - PY_CHECK_ARG_TYPE(1, tp_str); - const char* url = py_tostr(py_arg(1)); - http_headers headers = DefaultHeaders; - if(argc == 2) { - // open(self, url) - } else if(argc == 3) { - // open(self, url, headers) - if(!py_checktype(py_arg(2), tp_dict)) return false; - bool ok = py_dict_apply( - py_arg(2), - [](py_Ref key, py_Ref value, void* ctx) { - http_headers* p_headers = (http_headers*)ctx; - if(!py_checkstr(key)) return false; - if(!py_checkstr(value)) return false; - p_headers->operator[] (py_tostr(key)) = py_tostr(value); - return true; - }, - &headers); - if(!ok) return false; - } else { - return TypeError("open() takes 2 or 3 arguments"); - } - int code = self->ws.open(url, headers); - py_newint(py_retval(), code); - return true; - }); - - py_bindmethod(type, "close", [](int argc, py_Ref argv) { - PY_CHECK_ARGC(1); - libhv_WebSocketClient* self = (libhv_WebSocketClient*)py_touserdata(argv); - int code = self->ws.close(); - py_newint(py_retval(), code); - return true; - }); - - py_bindmethod(type, "send", [](int argc, py_Ref argv) { - PY_CHECK_ARGC(2); - libhv_WebSocketClient* self = (libhv_WebSocketClient*)py_touserdata(argv); - PY_CHECK_ARG_TYPE(1, tp_str); - const char* msg = py_tostr(py_arg(1)); - int code = self->ws.send(msg); - py_newint(py_retval(), code); - return true; - }); - - py_bindmethod(type, "recv", [](int argc, py_Ref argv) { - PY_CHECK_ARGC(1); - libhv_WebSocketClient* self = (libhv_WebSocketClient*)py_touserdata(py_arg(0)); - - std::pair mq_msg; - if(!self->mq.pop(&mq_msg)) { - py_newnone(py_retval()); - return true; - } else { - py_Ref p = py_newtuple(py_retval(), 2); - switch(mq_msg.first) { - case WsMessageType::onopen: { - py_newstr(py_offset(p, 0), "onopen"); - py_newnone(py_offset(p, 1)); - break; - } - case WsMessageType::onclose: { - py_newstr(py_offset(p, 0), "onclose"); - py_newnone(py_offset(p, 1)); - break; - } - case WsMessageType::onmessage: { - py_newstr(py_offset(p, 0), "onmessage"); - py_newstrv(py_offset(p, 1), {mq_msg.second.data(), (int)mq_msg.second.size()}); - break; - } - } - return true; - } - }); - return type; -} diff --git a/3rd/libhv/src/libhv_bindings.cpp b/3rd/libhv/src/libhv_bindings.cpp deleted file mode 100644 index f54cb856..00000000 --- a/3rd/libhv/src/libhv_bindings.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "libhv_bindings.hpp" -#include "base/herr.h" - -extern "C" void pk__add_module_libhv() { - py_GlobalRef mod = py_newmodule("libhv"); - - libhv_register_HttpRequest(mod); - libhv_register_HttpClient(mod); - libhv_register_HttpServer(mod); - libhv_register_WebSocketClient(mod); - - py_bindfunc(mod, "strerror", [](int argc, py_Ref argv) { - PY_CHECK_ARGC(1); - PY_CHECK_ARG_TYPE(0, tp_int); - int code = py_toint(py_arg(0)); - const char* msg = hv_strerror(code); - py_newstr(py_retval(), msg); - return true; - }); -} diff --git a/CMakeLists.txt b/CMakeLists.txt index ac483d90..5fec1e27 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -110,11 +110,6 @@ if(PK_BUILD_MODULE_LZ4) add_definitions(-DPK_BUILD_MODULE_LZ4) endif() -if(PK_BUILD_MODULE_LIBHV) - add_subdirectory(3rd/libhv) - add_definitions(-DPK_BUILD_MODULE_LIBHV) -endif() - if(PK_BUILD_MODULE_CUTE_PNG) add_subdirectory(3rd/cute_png) add_definitions(-DPK_BUILD_MODULE_CUTE_PNG) @@ -181,10 +176,6 @@ if(PK_BUILD_MODULE_LZ4) target_link_libraries(${PROJECT_NAME} lz4) endif() -if(PK_BUILD_MODULE_LIBHV) - target_link_libraries(${PROJECT_NAME} libhv_bindings) -endif() - if(PK_BUILD_MODULE_CUTE_PNG) target_link_libraries(${PROJECT_NAME} cute_png) endif() diff --git a/CMakeOptions.txt b/CMakeOptions.txt index 3dc0813a..8d19068a 100644 --- a/CMakeOptions.txt +++ b/CMakeOptions.txt @@ -15,7 +15,6 @@ option(PK_ENABLE_MIMALLOC "" OFF) # modules option(PK_BUILD_MODULE_LZ4 "" OFF) -option(PK_BUILD_MODULE_LIBHV "" OFF) option(PK_BUILD_MODULE_CUTE_PNG "" OFF) # PK_IS_MAIN determines whether the project is being used from root diff --git a/compile_flags.txt b/compile_flags.txt index e98f6100..eeaf47d4 100644 --- a/compile_flags.txt +++ b/compile_flags.txt @@ -4,5 +4,4 @@ -std=c11 -Iinclude/ -I3rd/lz4/ --I3rd/libhv/include/ -I3rd/cute_headers/include/ \ No newline at end of file diff --git a/docs/modules/libhv.md b/docs/modules/libhv.md deleted file mode 100644 index b6eb28e5..00000000 --- a/docs/modules/libhv.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -icon: package -label: libhv ---- - -!!! -This module is optional. Set option `PK_BUILD_MODULE_LIBHV` to `ON` in your `CMakeLists.txt` to enable it. -!!! - -`libhv` is a git submodule located at `3rd/libhv/libhv`. If you cannot find it, please run the following command to initialize the submodule: - -```bash -git submodule update --init --recursive -``` - -Simple bindings for [libhv](https://github.com/ithewei/libhv), which provides cross platform implementation of the following: -+ HTTP server -+ HTTP client -+ WebSocket server -+ WebSocket client - -#### Source code - -:::code source="../../include/typings/libhv.pyi" ::: diff --git a/include/pocketpy/interpreter/modules.h b/include/pocketpy/interpreter/modules.h index 7f3385f2..34d988db 100644 --- a/include/pocketpy/interpreter/modules.h +++ b/include/pocketpy/interpreter/modules.h @@ -27,12 +27,6 @@ void pk__add_module_conio(); void pk__add_module_lz4(); void pk__add_module_pkpy(); -#ifdef PK_BUILD_MODULE_LIBHV -void pk__add_module_libhv(); -#else -#define pk__add_module_libhv() -#endif - #ifdef PK_BUILD_MODULE_CUTE_PNG void pk__add_module_cute_png(); #else diff --git a/plugins/flutter/pocketpy/example/macos/Podfile.lock b/plugins/flutter/pocketpy/example/macos/Podfile.lock index b180369c..4af3d1de 100644 --- a/plugins/flutter/pocketpy/example/macos/Podfile.lock +++ b/plugins/flutter/pocketpy/example/macos/Podfile.lock @@ -15,7 +15,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 - pocketpy: 99ce4fe9fc8cccfda3633f5599c2d8f802c6d5e8 + pocketpy: b4fdf42a775d9d6498ce13a2dde7fc8f462ea034 PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 diff --git a/plugins/flutter/pocketpy/example/macos/Runner.xcodeproj/project.pbxproj b/plugins/flutter/pocketpy/example/macos/Runner.xcodeproj/project.pbxproj index 936d66f8..e5cb6e92 100644 --- a/plugins/flutter/pocketpy/example/macos/Runner.xcodeproj/project.pbxproj +++ b/plugins/flutter/pocketpy/example/macos/Runner.xcodeproj/project.pbxproj @@ -240,6 +240,7 @@ 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, + A4D061C1E57F49C3123F636C /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -404,6 +405,23 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + A4D061C1E57F49C3123F636C /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ diff --git a/plugins/flutter/pocketpy/ios/Classes/empty.c b/plugins/flutter/pocketpy/ios/Classes/empty.c new file mode 100644 index 00000000..08d4d881 --- /dev/null +++ b/plugins/flutter/pocketpy/ios/Classes/empty.c @@ -0,0 +1 @@ +// nothing \ No newline at end of file diff --git a/plugins/flutter/pocketpy/ios/Classes/pocketpy.c b/plugins/flutter/pocketpy/ios/Classes/pocketpy.c deleted file mode 100644 index 8a5a08ab..00000000 --- a/plugins/flutter/pocketpy/ios/Classes/pocketpy.c +++ /dev/null @@ -1,3 +0,0 @@ -// Relative import to be able to reuse the C sources. -// See the comment in ../{projectName}}.podspec for more information. -#include "../../src/pocketpy.c" diff --git a/plugins/flutter/pocketpy/ios/pocketpy.podspec b/plugins/flutter/pocketpy/ios/pocketpy.podspec index 79d327ef..a22a211c 100644 --- a/plugins/flutter/pocketpy/ios/pocketpy.podspec +++ b/plugins/flutter/pocketpy/ios/pocketpy.podspec @@ -17,12 +17,19 @@ A new Flutter FFI plugin project. # builds of apps using this FFI plugin. Podspec does not support relative # paths, so Classes contains a forwarder C file that relatively imports # `../src/*` so that the C sources can be shared among all target platforms. - s.source = { :path => '.' } - s.source_files = 'Classes/**/*' s.dependency 'Flutter' s.platform = :ios, '13.0' + s.swift_version = '5.0', + + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.library = 'c' + # Flutter.framework does not contain a i386 slice. - s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } - s.swift_version = '5.0' + s.pod_target_xcconfig = { + 'DEFINES_MODULE' => 'YES', + 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' + 'OTHER_LDFLAGS' => '-force_load ' + __dir__ + '/Frameworks/libpocketpy.a', + } end diff --git a/plugins/flutter/pocketpy/lib/pocketpy.dart b/plugins/flutter/pocketpy/lib/pocketpy.dart index 8d1f325c..426087f6 100644 --- a/plugins/flutter/pocketpy/lib/pocketpy.dart +++ b/plugins/flutter/pocketpy/lib/pocketpy.dart @@ -14,7 +14,8 @@ void flutterPrint(Pointer text) { final PocketpyBindings pocket = () { DynamicLibrary dylib; if (Platform.isMacOS || Platform.isIOS) { - dylib = DynamicLibrary.open('$_libName.framework/$_libName'); + // dylib = DynamicLibrary.open('$_libName.framework/$_libName'); + dylib = DynamicLibrary.process(); } else if (Platform.isAndroid || Platform.isLinux) { dylib = DynamicLibrary.open('lib$_libName.so'); } else if (Platform.isWindows) { diff --git a/plugins/flutter/pocketpy/macos/Classes/empty.c b/plugins/flutter/pocketpy/macos/Classes/empty.c new file mode 100644 index 00000000..08d4d881 --- /dev/null +++ b/plugins/flutter/pocketpy/macos/Classes/empty.c @@ -0,0 +1 @@ +// nothing \ No newline at end of file diff --git a/plugins/flutter/pocketpy/macos/Classes/pocketpy.c b/plugins/flutter/pocketpy/macos/Classes/pocketpy.c deleted file mode 100644 index 8a5a08ab..00000000 --- a/plugins/flutter/pocketpy/macos/Classes/pocketpy.c +++ /dev/null @@ -1,3 +0,0 @@ -// Relative import to be able to reuse the C sources. -// See the comment in ../{projectName}}.podspec for more information. -#include "../../src/pocketpy.c" diff --git a/plugins/flutter/pocketpy/macos/pocketpy.podspec b/plugins/flutter/pocketpy/macos/pocketpy.podspec index 7d46558d..34c4757c 100644 --- a/plugins/flutter/pocketpy/macos/pocketpy.podspec +++ b/plugins/flutter/pocketpy/macos/pocketpy.podspec @@ -17,11 +17,17 @@ A new Flutter FFI plugin project. # builds of apps using this FFI plugin. Podspec does not support relative # paths, so Classes contains a forwarder C file that relatively imports # `../src/*` so that the C sources can be shared among all target platforms. - s.source = { :path => '.' } - s.source_files = 'Classes/**/*' s.dependency 'FlutterMacOS' s.platform = :osx, '10.11' - s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } s.swift_version = '5.0' + + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.library = 'c' + s.pod_target_xcconfig = { + 'DEFINES_MODULE' => 'YES', + 'OTHER_LDFLAGS' => '-force_load ' + __dir__ + '/Frameworks/libpocketpy.a', + } end + diff --git a/plugins/flutter/pocketpy/src/pocketpy.c b/plugins/flutter/pocketpy/src/pocketpy.c deleted file mode 100644 index e9b6a44c..00000000 --- a/plugins/flutter/pocketpy/src/pocketpy.c +++ /dev/null @@ -1 +0,0 @@ -// nothing diff --git a/src/interpreter/vm.c b/src/interpreter/vm.c index 069f1b1a..90e917ab 100644 --- a/src/interpreter/vm.c +++ b/src/interpreter/vm.c @@ -262,7 +262,6 @@ void VM__ctor(VM* self) { pk__add_module_conio(); pk__add_module_lz4(); // optional - pk__add_module_libhv(); // optional pk__add_module_cute_png(); // optional pk__add_module_pkpy();