| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- #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<std::pair<HttpContextPtr, std::atomic<int>>*> mq;
- struct WsMessage {
- WsMessageType type;
- hv::WebSocketChannel* channel;
- HttpRequestPtr request;
- std::string body;
- };
- libhv_MQ<WsMessage> 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<HttpContextPtr, std::atomic<int>> 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<HttpContextPtr, std::atomic<int>>* 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<hv::WebSocketChannel*>(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<hv::WebSocketChannel*>(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;
- }
|