Commit 667466a4 authored by Tom Niget's avatar Tom Niget

Implement necessary socket features for web server

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