Commit 7b7f4d0d authored by Tom Niget's avatar Tom Niget

Implement necessary socket features for web server

parent 31cd80fe
...@@ -5,13 +5,14 @@ ...@@ -5,13 +5,14 @@
#ifndef TYPON_BUILTINS_HPP #ifndef TYPON_BUILTINS_HPP
#define TYPON_BUILTINS_HPP #define TYPON_BUILTINS_HPP
#include <fmt/format.h>
#include <iostream> #include <iostream>
#include <optional> #include <optional>
#include <ostream> #include <ostream>
#include <string> #include <string>
#include <typon/typon.hpp>
#include <python/basedef.hpp> #include <python/basedef.hpp>
#include <typon/typon.hpp>
#ifdef __cpp_lib_unreachable #ifdef __cpp_lib_unreachable
#include <utility> #include <utility>
...@@ -21,26 +22,35 @@ ...@@ -21,26 +22,35 @@
[[noreturn]] inline void TYPON_UNREACHABLE() { std::abort(); } [[noreturn]] inline void TYPON_UNREACHABLE() { std::abort(); }
#endif #endif
#define COMMA() ,
#define METHOD(ret, name, args, ...) \
struct { \
ret operator() args __VA_ARGS__ type *self; \
} name{this};
#define FUNCTION(ret, name, args, ...) \
struct { \
ret operator() args __VA_ARGS__ \
} name;
using namespace std::literals; using namespace std::literals;
template<typename T> template <typename T>
concept PyUserType = requires { typename T::type; }; concept PyUserType = requires { typename T::type; };
template<typename T> template <typename T> struct RealType {
struct RealType {
using type = T; using type = T;
}; };
template<PyUserType T> template <PyUserType T> struct RealType<T> {
struct RealType<T> {
using type = typename T::type; using type = typename T::type;
}; };
template <typename T> using PyObj = std::shared_ptr<typename RealType<T>::type>; template <typename T> using PyObj = std::shared_ptr<typename RealType<T>::type>;
template <typename T, typename... Args> template <typename T, typename... Args> auto pyobj(Args &&...args) -> PyObj<T> {
auto pyobj(Args &&... args) -> PyObj<T> { return std::make_shared<typename RealType<T>::type>(
return std::make_shared<typename RealType<T>::type>(std::forward<Args>(args)...); std::forward<Args>(args)...);
} }
// typon_len // typon_len
...@@ -62,16 +72,16 @@ concept CppSize = requires(const T &t) { ...@@ -62,16 +72,16 @@ concept CppSize = requires(const T &t) {
{ t.size() } -> std::same_as<size_t>; { t.size() } -> std::same_as<size_t>;
}; };
template <typename T> template <typename T>
concept PyLen = requires(const T &t) { concept PyLen = requires(const T &t) {
{ t.py_len() } -> std::same_as<size_t>; { t.py_len() } -> std::same_as<size_t>;
}; };
template <CppSize T> template <CppSize T>
requires (!PyLen<T>) requires(!PyLen<T>)
size_t len(const T &t) { return t.size(); } size_t len(const T &t) {
return t.size();
}
template <PyLen T> size_t len(const T &t) { return t.py_len(); } template <PyLen T> size_t len(const T &t) { return t.py_len(); }
...@@ -105,6 +115,12 @@ public: ...@@ -105,6 +115,12 @@ public:
} }
} PyNone{}; } PyNone{};
#define system_error(err, message) \
do { \
puts(message); \
throw fmt::system_error(err, message); \
} while (0)
#include "builtins/bool.hpp" #include "builtins/bool.hpp"
#include "builtins/complex.hpp" #include "builtins/complex.hpp"
#include "builtins/dict.hpp" #include "builtins/dict.hpp"
......
...@@ -13,6 +13,7 @@ public: ...@@ -13,6 +13,7 @@ public:
PyBytes(const char *s) : std::string(s) {} PyBytes(const char *s) : std::string(s) {}
PyBytes(const std::string &s) : std::string(s) {} PyBytes(const std::string &s) : std::string(s) {}
PyBytes(std::string &&s) : std::string(std::move(s)) {} PyBytes(std::string &&s) : std::string(std::move(s)) {}
PyBytes(size_t count, char ch) : std::string(count, ch) {}
template <class InputIterator> template <class InputIterator>
PyBytes(InputIterator first, InputIterator last) : std::string(first, last) {} PyBytes(InputIterator first, InputIterator last) : std::string(first, last) {}
......
...@@ -5,53 +5,88 @@ ...@@ -5,53 +5,88 @@
#ifndef TYPON_SOCKET_HPP #ifndef TYPON_SOCKET_HPP
#define TYPON_SOCKET_HPP #define TYPON_SOCKET_HPP
#undef SOCK_STREAM #include "builtins.hpp"
#undef AF_INET6 #include <netinet/in.h>
#undef SOL_SOCKET
#undef SO_REUSEADDR
#include "builtins/bytes.hpp"
#include <tuple> #include <tuple>
namespace py_socket { namespace py_socket {
struct socket_t { struct socket_t {
int SOCK_STREAM = 1; #undef SOCK_STREAM
int AF_INET6 = 10; #undef AF_INET6
int SOL_SOCKET = 1; #undef SOL_SOCKET
int SO_REUSEADDR = 2; #undef SO_REUSEADDR
static constexpr int SOCK_STREAM = 1;
static constexpr int AF_INET6 = 10;
static constexpr int SOL_SOCKET = 1;
static constexpr int SO_REUSEADDR = 2;
struct { struct {
struct type { struct type {
struct { METHOD(typon::Task<std::tuple<type COMMA() std::string>>, accept, (), {
std::tuple<type, std::string> operator()() { return {}; } int connfd = co_await typon::io::accept(self->fd, NULL, NULL);
} accept; co_return std::make_tuple(type(connfd), std::string("")); // TODO
})
METHOD(typon::Task<void>, close, (),
{ co_await typon::io::close(self->fd); })
METHOD(void, listen, (int backlog), {
if (::listen(self->fd, backlog) < 0) {
self->close();
system_error(errno, "listen()");
}
})
METHOD(void, setsockopt, (int level, int optname, int optval), {
if (::setsockopt(self->fd, level, optname, &optval, sizeof(int)) < 0) {
system_error(errno, "setsockopt()");
}
})
struct { METHOD(void, bind, (std::tuple<std::string COMMA() int> address), {
void operator()() {} auto [host, port] = address;
} close; sockaddr_in6 addr;
std::memset(&addr, 0, sizeof(addr));
addr.sin6_family = AF_INET6;
addr.sin6_port = htons(port);
addr.sin6_addr = in6addr_any;
if (::bind(self->fd, (const sockaddr *)&addr, sizeof(addr)) < 0) {
system_error(errno, "bind()");
}
})
struct { METHOD(typon::Task<PyBytes>, recv, (int bufsize), {
void operator()(int backlog) {} PyBytes buf(bufsize, '\0');
} listen; co_await typon::io::recv(self->fd, buf.data(), buf.size(), 0);
co_return std::move(buf);
})
struct { METHOD(typon::Task<void>, send, (PyBytes data), {
void operator()(int level, int optname, int optval) {} std::cout << self->fd << std::endl;
} setsockopt; if (int sbytes = co_await typon::io::send(self->fd, data, 0);
sbytes < 0) {
co_await self->close();
system_error(-sbytes, "send()");
}
})
struct { type(int fd = -1) : fd(fd) { }
void operator()(std::tuple<std::string, int> address) {}
} bind;
struct { type(const type &other)
PyBytes operator()(int bufsize) { return {}; } : fd(other.fd), accept(this), close(this), listen(this),
} recv; setsockopt(this), bind(this), recv(this), send(this) {
}
struct { int fd;
void operator()(PyBytes data) {}
} send;
}; };
type operator()(int family, int type) { return {}; } type operator()(int family, int type_) {
if (int fd = ::socket(family, type_, 0); fd >= 0) {
return type(fd);
} else {
system_error(errno, "socket()");
}
}
} socket; } socket;
} all; } all;
...@@ -60,6 +95,6 @@ auto &get_all() { return all; } ...@@ -60,6 +95,6 @@ auto &get_all() { return all; }
namespace typon { namespace typon {
using PySocket = decltype(py_socket::all.socket)::type; using PySocket = decltype(py_socket::all.socket)::type;
}; }
#endif // TYPON_SOCKET_HPP #endif // TYPON_SOCKET_HPP
...@@ -14,6 +14,10 @@ struct sys_t { ...@@ -14,6 +14,10 @@ struct sys_t {
static constexpr auto &stdout = std::cout; static constexpr auto &stdout = std::cout;
static constexpr auto &stderr = std::cerr; static constexpr auto &stderr = std::cerr;
PyList<PyStr> argv; PyList<PyStr> argv;
FUNCTION(void, exit, (int code), {
std::exit(code);
})
} all; } all;
auto& get_all() { auto& get_all() {
......
...@@ -36,11 +36,11 @@ ...@@ -36,11 +36,11 @@
#include <typon/syscall_completion.hpp> #include <typon/syscall_completion.hpp>
#include <typon/theft_point.hpp> #include <typon/theft_point.hpp>
#include <liburing.h>
namespace typon namespace typon
{ {
#include <liburing.h>
struct Scheduler struct Scheduler
{ {
......
...@@ -17,17 +17,17 @@ class socket: ...@@ -17,17 +17,17 @@ class socket:
def listen(self, backlog: int) -> None: def listen(self, backlog: int) -> None:
pass pass
def accept(self) -> tuple[Self, str]: def accept(self) -> Task[tuple[Self, str]]:
pass pass
def recv(self, bufsize: int) -> bytes: def recv(self, bufsize: int) -> Task[bytes]:
pass pass
def send(self, data: bytes) -> None: def send(self, data: bytes) -> Task[None]:
pass pass
def __init__(self, family: int, type: int) -> Self: def __init__(self, family: int, type: int) -> Self:
pass pass
def close(self) -> None: def close(self) -> Task[None]:
pass pass
\ No newline at end of file
stdout: CppType["auto&"] stdout: CppType["auto&"]
argv: list[str] argv: list[str]
\ No newline at end of file
def exit(code: int) -> None:
...
\ No newline at end of file
# coding: utf-8 # coding: utf-8
import sys import sys
from socket import socket as pysocket, SOCK_STREAM, AF_INET6, SOL_SOCKET, SO_REUSEADDR from socket import socket, SOCK_STREAM, AF_INET6, SOL_SOCKET, SO_REUSEADDR
from typon import fork from typon import fork
BACKLOG = 1024 BACKLOG = 1024
...@@ -15,13 +15,13 @@ response_fmt = \ ...@@ -15,13 +15,13 @@ response_fmt = \
"{}" "{}"
def create_listening_socket(port): def create_listening_socket(port):
sockfd = pysocket(AF_INET6, SOCK_STREAM) sockfd = socket(AF_INET6, SOCK_STREAM)
sockfd.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) sockfd.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
sockfd.bind(("", port)) sockfd.bind(("", port))
sockfd.listen(BACKLOG) sockfd.listen(BACKLOG)
return sockfd return sockfd
def handle_connection(connfd: pysocket, filepath): def handle_connection(connfd: socket, filepath):
buf = connfd.recv(1024).decode("utf-8") buf = connfd.recv(1024).decode("utf-8")
length = buf.find("\r\n\r\n") length = buf.find("\r\n\r\n")
content = "Hello world" content = "Hello world"
...@@ -29,19 +29,21 @@ def handle_connection(connfd: pysocket, filepath): ...@@ -29,19 +29,21 @@ def handle_connection(connfd: pysocket, filepath):
connfd.send(response.encode("utf-8")) connfd.send(response.encode("utf-8"))
connfd.close() connfd.close()
def server_loop(sockfd: pysocket, filepath): def server_loop(sockfd: socket, filepath):
while True: while True:
connfd, _ = sockfd.accept() connfd, _ = sockfd.accept()
fork(lambda: handle_connection(connfd, filepath)) fork(lambda: handle_connection(connfd, filepath))
break
def server_loops(sockfd, filepath): def server_loops(sockfd, filepath):
for i in range(20): for i in range(1):
fork(lambda: server_loop(sockfd, filepath)) fork(lambda: server_loop(sockfd, filepath))
if __name__ == "__main__": if __name__ == "__main__":
if len(sys.argv) > 2: if len(sys.argv) > 2:
print("Usage: webserver [ filepath ]") print("Usage: webserver [ filepath ]")
sys.exit(1)
filepath = sys.argv[1] if len(sys.argv) == 2 else "webserver.cpp" filepath = sys.argv[1] if len(sys.argv) == 2 else "webserver.cpp"
print("Serving", filepath, "on port", PORT) print("Serving", filepath, "on port", PORT)
......
...@@ -42,10 +42,6 @@ class BlockVisitor(NodeVisitor): ...@@ -42,10 +42,6 @@ class BlockVisitor(NodeVisitor):
from transpiler.phases.emit_cpp.function import FunctionVisitor from transpiler.phases.emit_cpp.function import FunctionVisitor
yield from FunctionVisitor(self.scope, CoroutineMode.TASK).emit_block(node.scope, block()) yield from FunctionVisitor(self.scope, CoroutineMode.TASK).emit_block(node.scope, block())
yield "int main(int argc, char* argv[]) {"
yield "py_sys::all.argv = PyList<PyStr>(std::vector<PyStr>(argv, argv + argc));"
yield "root().call();"
yield "}"
return return
yield "struct {" yield "struct {"
......
...@@ -128,6 +128,7 @@ class ExpressionVisitor(NodeVisitor): ...@@ -128,6 +128,7 @@ class ExpressionVisitor(NodeVisitor):
return return
# TODO: precedence needed? # TODO: precedence needed?
if CoroutineMode.ASYNC in self.generator and node.is_await: if CoroutineMode.ASYNC in self.generator and node.is_await:
yield "(" # TODO: temporary
yield "co_await " yield "co_await "
node.in_await = True node.in_await = True
elif CoroutineMode.FAKE in self.generator: elif CoroutineMode.FAKE in self.generator:
...@@ -136,6 +137,8 @@ class ExpressionVisitor(NodeVisitor): ...@@ -136,6 +137,8 @@ class ExpressionVisitor(NodeVisitor):
yield "(" yield "("
yield from join(", ", map(self.reset().visit, node.args)) yield from join(", ", map(self.reset().visit, node.args))
yield ")" yield ")"
if CoroutineMode.ASYNC in self.generator and node.is_await:
yield ")"
def visit_Lambda(self, node: ast.Lambda) -> Iterable[str]: def visit_Lambda(self, node: ast.Lambda) -> Iterable[str]:
yield "[]" yield "[]"
......
...@@ -12,5 +12,12 @@ class FileVisitor(BlockVisitor): ...@@ -12,5 +12,12 @@ class FileVisitor(BlockVisitor):
stmt: ast.AST stmt: ast.AST
yield "#include <python/builtins.hpp>" yield "#include <python/builtins.hpp>"
visitor = ModuleVisitor(self.scope) visitor = ModuleVisitor(self.scope)
for stmt in node.body: code = [line for stmt in node.body for line in visitor.visit(stmt)]
yield from visitor.visit(stmt) yield from visitor.includes
yield "namespace PROGRAMNS {"
yield from code
yield "}"
yield "int main(int argc, char* argv[]) {"
yield "py_sys::all.argv = PyList<PyStr>(std::vector<PyStr>(argv, argv + argc));"
yield "PROGRAMNS::root().call();"
yield "}"
...@@ -78,3 +78,6 @@ class FunctionVisitor(BlockVisitor): ...@@ -78,3 +78,6 @@ class FunctionVisitor(BlockVisitor):
for child in items: for child in items:
yield from FunctionVisitor(scope, self.generator).visit(child) yield from FunctionVisitor(scope, self.generator).visit(child)
yield "}" yield "}"
def visit_Break(self, node: ast.Break) -> Iterable[str]:
yield "break;"
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
import ast import ast
from typing import Iterable from typing import Iterable
from dataclasses import dataclass, field
from transpiler.phases.emit_cpp import CoroutineMode from transpiler.phases.emit_cpp import CoroutineMode
from transpiler.phases.emit_cpp.block import BlockVisitor from transpiler.phases.emit_cpp.block import BlockVisitor
from transpiler.phases.emit_cpp.class_ import ClassVisitor from transpiler.phases.emit_cpp.class_ import ClassVisitor
...@@ -10,7 +12,9 @@ from transpiler.utils import compare_ast ...@@ -10,7 +12,9 @@ from transpiler.utils import compare_ast
# noinspection PyPep8Naming # noinspection PyPep8Naming
@dataclass
class ModuleVisitor(BlockVisitor): class ModuleVisitor(BlockVisitor):
includes: list[str] = field(default_factory=list)
def visit_Import(self, node: ast.Import) -> Iterable[str]: def visit_Import(self, node: ast.Import) -> Iterable[str]:
for alias in node.names: for alias in node.names:
if alias.name == "typon": if alias.name == "typon":
...@@ -20,7 +24,8 @@ class ModuleVisitor(BlockVisitor): ...@@ -20,7 +24,8 @@ class ModuleVisitor(BlockVisitor):
yield f'auto& {alias.asname or alias.name} = py_{alias.name}::get_all();' yield f'auto& {alias.asname or alias.name} = py_{alias.name}::get_all();'
def import_module(self, name: str) -> Iterable[str]: def import_module(self, name: str) -> Iterable[str]:
yield f'#include "python/{name}.hpp"' self.includes.append(f'#include "python/{name}.hpp"')
yield ""
def visit_ImportFrom(self, node: ast.ImportFrom) -> Iterable[str]: def visit_ImportFrom(self, node: ast.ImportFrom) -> Iterable[str]:
if node.module == "typon": if node.module == "typon":
......
...@@ -179,3 +179,6 @@ class ScoperBlockVisitor(ScoperVisitor): ...@@ -179,3 +179,6 @@ class ScoperBlockVisitor(ScoperVisitor):
if isinstance(node, ast.AST): if isinstance(node, ast.AST):
super().visit(node) super().visit(node)
node.scope = self.scope node.scope = self.scope
def visit_Break(self, node: ast.Break):
pass # TODO: check in loop
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment