feat: add networking module with TCP Socket/Server and example applications
Implement a full-featured `net` module supporting TCP `Socket` and `Server` classes via libuv, including example applications (chat server, echo server, HTTP client/server, file explorer, system dashboard) and benchmark scripts (fibonacci, n-body). Add GEMINI.md project documentation and Stderr class to io module.
This commit is contained in:
+37
-1
@@ -4,6 +4,7 @@
|
||||
#include "modules.h"
|
||||
|
||||
#include "io.wren.inc"
|
||||
#include "net.wren.inc"
|
||||
#include "os.wren.inc"
|
||||
#include "repl.wren.inc"
|
||||
#include "scheduler.wren.inc"
|
||||
@@ -51,8 +52,22 @@ extern void stdinIsTerminal(WrenVM* vm);
|
||||
extern void stdinReadStart(WrenVM* vm);
|
||||
extern void stdinReadStop(WrenVM* vm);
|
||||
extern void stdoutFlush(WrenVM* vm);
|
||||
extern void stderrWrite(WrenVM* vm);
|
||||
extern void schedulerCaptureMethods(WrenVM* vm);
|
||||
extern void timerStartTimer(WrenVM* vm);
|
||||
extern void socketAllocate(WrenVM* vm);
|
||||
extern void socketFinalize(void* data);
|
||||
extern void socketConnect(WrenVM* vm);
|
||||
extern void socketWrite(WrenVM* vm);
|
||||
extern void socketRead(WrenVM* vm);
|
||||
extern void socketClose(WrenVM* vm);
|
||||
extern void netCaptureClass(WrenVM* vm);
|
||||
extern void netShutdown();
|
||||
extern void serverAllocate(WrenVM* vm);
|
||||
extern void serverFinalize(void* data);
|
||||
extern void serverBind(WrenVM* vm);
|
||||
extern void serverAccept(WrenVM* vm);
|
||||
extern void serverClose(WrenVM* vm);
|
||||
|
||||
// The maximum number of foreign methods a single class defines. Ideally, we
|
||||
// would use variable-length arrays for each class in the table below, but
|
||||
@@ -69,7 +84,7 @@ extern void timerStartTimer(WrenVM* vm);
|
||||
// If you add a new class to the largest module below, make sure to bump this.
|
||||
// Note that it also includes an extra slot for the sentinel value indicating
|
||||
// the end of the list.
|
||||
#define MAX_CLASSES_PER_MODULE 6
|
||||
#define MAX_CLASSES_PER_MODULE 8
|
||||
|
||||
// Describes one foreign method in a class.
|
||||
typedef struct
|
||||
@@ -167,6 +182,27 @@ static ModuleRegistry modules[] =
|
||||
CLASS(Stdout)
|
||||
STATIC_METHOD("flush()", stdoutFlush)
|
||||
END_CLASS
|
||||
CLASS(Stderr)
|
||||
STATIC_METHOD("write(_)", stderrWrite)
|
||||
END_CLASS
|
||||
END_MODULE
|
||||
MODULE(net)
|
||||
CLASS(Socket)
|
||||
ALLOCATE(socketAllocate)
|
||||
FINALIZE(socketFinalize)
|
||||
STATIC_METHOD("captureClass_()", netCaptureClass)
|
||||
METHOD("connect_(_,_,_)", socketConnect)
|
||||
METHOD("write_(_,_)", socketWrite)
|
||||
METHOD("read_(_)", socketRead)
|
||||
METHOD("close_()", socketClose)
|
||||
END_CLASS
|
||||
CLASS(Server)
|
||||
ALLOCATE(serverAllocate)
|
||||
FINALIZE(serverFinalize)
|
||||
METHOD("bind_(_,_)", serverBind)
|
||||
METHOD("accept_(_)", serverAccept)
|
||||
METHOD("close_()", serverClose)
|
||||
END_CLASS
|
||||
END_MODULE
|
||||
MODULE(os)
|
||||
CLASS(Platform)
|
||||
|
||||
@@ -286,9 +286,14 @@ static void initVM()
|
||||
uv_loop_init(loop);
|
||||
}
|
||||
|
||||
extern void ioShutdown();
|
||||
extern void netShutdown();
|
||||
extern void schedulerShutdown();
|
||||
|
||||
static void freeVM()
|
||||
{
|
||||
ioShutdown();
|
||||
netShutdown();
|
||||
schedulerShutdown();
|
||||
|
||||
uv_loop_close(loop);
|
||||
|
||||
@@ -558,6 +558,13 @@ void stdoutFlush(WrenVM* vm)
|
||||
wrenSetSlotNull(vm, 0);
|
||||
}
|
||||
|
||||
void stderrWrite(WrenVM* vm)
|
||||
{
|
||||
const char* text = wrenGetSlotString(vm, 1);
|
||||
fprintf(stderr, "%s", text);
|
||||
wrenSetSlotNull(vm, 0);
|
||||
}
|
||||
|
||||
static void allocCallback(uv_handle_t* handle, size_t suggestedSize,
|
||||
uv_buf_t* buf)
|
||||
{
|
||||
|
||||
Vendored
+4
@@ -303,3 +303,7 @@ class Stdin {
|
||||
class Stdout {
|
||||
foreign static flush()
|
||||
}
|
||||
|
||||
class Stderr {
|
||||
foreign static write(string)
|
||||
}
|
||||
|
||||
@@ -306,4 +306,8 @@ static const char* ioModuleSource =
|
||||
"\n"
|
||||
"class Stdout {\n"
|
||||
" foreign static flush()\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"class Stderr {\n"
|
||||
" foreign static write(string)\n"
|
||||
"}\n";
|
||||
|
||||
@@ -0,0 +1,411 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "uv.h"
|
||||
#include "vm.h"
|
||||
#include "scheduler.h"
|
||||
#include "net.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Helper Structures
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
typedef struct {
|
||||
uv_tcp_t* handle;
|
||||
WrenHandle* fiber; // Fiber waiting for an operation (connect, read, write)
|
||||
uv_connect_t* connectReq;
|
||||
uv_write_t* writeReq;
|
||||
} SocketData;
|
||||
|
||||
typedef struct SocketListNode {
|
||||
uv_tcp_t* handle;
|
||||
struct SocketListNode* next;
|
||||
} SocketListNode;
|
||||
|
||||
typedef struct {
|
||||
uv_tcp_t* handle;
|
||||
WrenHandle* acceptFiber; // Fiber waiting for accept()
|
||||
SocketListNode* pendingHead;
|
||||
SocketListNode* pendingTail;
|
||||
} ServerData;
|
||||
|
||||
static WrenHandle* socketClassHandle = NULL;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Socket Implementation
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void socketAllocate(WrenVM* vm)
|
||||
{
|
||||
wrenSetSlotNewForeign(vm, 0, 0, sizeof(SocketData));
|
||||
SocketData* data = (SocketData*)wrenGetSlotForeign(vm, 0);
|
||||
data->handle = (uv_tcp_t*)malloc(sizeof(uv_tcp_t));
|
||||
uv_tcp_init(getLoop(), data->handle);
|
||||
data->handle->data = data;
|
||||
data->fiber = NULL;
|
||||
data->connectReq = NULL;
|
||||
data->writeReq = NULL;
|
||||
}
|
||||
|
||||
static void closeCallback(uv_handle_t* handle)
|
||||
{
|
||||
free(handle);
|
||||
}
|
||||
|
||||
void socketFinalize(void* data)
|
||||
{
|
||||
SocketData* socketData = (SocketData*)data;
|
||||
if (socketData->handle) {
|
||||
uv_close((uv_handle_t*)socketData->handle, closeCallback);
|
||||
}
|
||||
}
|
||||
|
||||
static void connectCallback(uv_connect_t* req, int status)
|
||||
{
|
||||
SocketData* data = (SocketData*)req->data;
|
||||
WrenHandle* fiber = data->fiber;
|
||||
free(req);
|
||||
data->connectReq = NULL;
|
||||
data->fiber = NULL;
|
||||
|
||||
if (status < 0) {
|
||||
schedulerResumeError(fiber, uv_strerror(status));
|
||||
} else {
|
||||
schedulerResume(fiber, false);
|
||||
}
|
||||
}
|
||||
|
||||
void socketConnect(WrenVM* vm)
|
||||
{
|
||||
SocketData* data = (SocketData*)wrenGetSlotForeign(vm, 0);
|
||||
const char* host = wrenGetSlotString(vm, 1);
|
||||
int port = (int)wrenGetSlotDouble(vm, 2);
|
||||
WrenHandle* fiber = wrenGetSlotHandle(vm, 3);
|
||||
|
||||
struct sockaddr_in addr;
|
||||
int r = uv_ip4_addr(host, port, &addr);
|
||||
if (r != 0) {
|
||||
schedulerResumeError(fiber, "Invalid IP address.");
|
||||
return;
|
||||
}
|
||||
|
||||
data->fiber = fiber;
|
||||
data->connectReq = (uv_connect_t*)malloc(sizeof(uv_connect_t));
|
||||
data->connectReq->data = data;
|
||||
|
||||
r = uv_tcp_connect(data->connectReq, data->handle, (const struct sockaddr*)&addr, connectCallback);
|
||||
if (r != 0) {
|
||||
free(data->connectReq);
|
||||
data->connectReq = NULL;
|
||||
schedulerResumeError(fiber, uv_strerror(r));
|
||||
}
|
||||
}
|
||||
|
||||
// Redefine writeCallback to handle the buffer
|
||||
|
||||
static void writeCallback_safe(uv_write_t* req, int status)
|
||||
|
||||
{
|
||||
|
||||
typedef struct {
|
||||
|
||||
uv_write_t req;
|
||||
|
||||
uv_buf_t buf;
|
||||
|
||||
} WriteRequest;
|
||||
|
||||
|
||||
|
||||
WriteRequest* wr = (WriteRequest*)req;
|
||||
|
||||
SocketData* data = (SocketData*)req->data;
|
||||
|
||||
WrenHandle* fiber = data->fiber;
|
||||
|
||||
|
||||
|
||||
free(wr->buf.base);
|
||||
|
||||
free(wr);
|
||||
|
||||
|
||||
|
||||
data->writeReq = NULL;
|
||||
|
||||
data->fiber = NULL;
|
||||
|
||||
|
||||
|
||||
if (status < 0) {
|
||||
|
||||
schedulerResumeError(fiber, uv_strerror(status));
|
||||
|
||||
} else {
|
||||
|
||||
schedulerResume(fiber, false);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void socketWrite(WrenVM* vm)
|
||||
|
||||
{
|
||||
|
||||
SocketData* data = (SocketData*)wrenGetSlotForeign(vm, 0);
|
||||
|
||||
const char* text = wrenGetSlotString(vm, 1);
|
||||
|
||||
size_t length = strlen(text);
|
||||
|
||||
WrenHandle* fiber = wrenGetSlotHandle(vm, 2);
|
||||
|
||||
|
||||
|
||||
data->fiber = fiber;
|
||||
|
||||
|
||||
|
||||
typedef struct {
|
||||
|
||||
uv_write_t req;
|
||||
|
||||
uv_buf_t buf;
|
||||
|
||||
} WriteRequest;
|
||||
|
||||
|
||||
|
||||
WriteRequest* wr = (WriteRequest*)malloc(sizeof(WriteRequest));
|
||||
|
||||
wr->req.data = data;
|
||||
|
||||
wr->buf.base = malloc(length);
|
||||
|
||||
wr->buf.len = length;
|
||||
|
||||
memcpy(wr->buf.base, text, length);
|
||||
|
||||
|
||||
|
||||
uv_write(&wr->req, (uv_stream_t*)data->handle, &wr->buf, 1, writeCallback_safe);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void allocCallback(uv_handle_t* handle, size_t suggestedSize, uv_buf_t* buf)
|
||||
{
|
||||
buf->base = (char*)malloc(suggestedSize);
|
||||
buf->len = suggestedSize;
|
||||
}
|
||||
|
||||
static void readCallback(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf)
|
||||
{
|
||||
SocketData* data = (SocketData*)stream->data;
|
||||
WrenHandle* fiber = data->fiber;
|
||||
|
||||
// Stop reading immediately. We only want one chunk.
|
||||
uv_read_stop(stream);
|
||||
data->fiber = NULL;
|
||||
|
||||
if (nread < 0) {
|
||||
if (nread == UV_EOF) {
|
||||
free(buf->base);
|
||||
schedulerResume(fiber, true);
|
||||
wrenSetSlotNull(getVM(), 2);
|
||||
schedulerFinishResume();
|
||||
return;
|
||||
}
|
||||
free(buf->base);
|
||||
schedulerResumeError(fiber, uv_strerror(nread));
|
||||
return;
|
||||
}
|
||||
|
||||
// Return the data
|
||||
schedulerResume(fiber, true);
|
||||
wrenSetSlotBytes(getVM(), 2, buf->base, nread);
|
||||
schedulerFinishResume();
|
||||
free(buf->base);
|
||||
}
|
||||
|
||||
void socketRead(WrenVM* vm)
|
||||
{
|
||||
SocketData* data = (SocketData*)wrenGetSlotForeign(vm, 0);
|
||||
WrenHandle* fiber = wrenGetSlotHandle(vm, 1);
|
||||
data->fiber = fiber;
|
||||
|
||||
uv_read_start((uv_stream_t*)data->handle, allocCallback, readCallback);
|
||||
}
|
||||
|
||||
void socketClose(WrenVM* vm)
|
||||
{
|
||||
SocketData* data = (SocketData*)wrenGetSlotForeign(vm, 0);
|
||||
if (data->handle && !uv_is_closing((uv_handle_t*)data->handle)) {
|
||||
uv_close((uv_handle_t*)data->handle, closeCallback);
|
||||
data->handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Server Implementation
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void serverAllocate(WrenVM* vm)
|
||||
{
|
||||
wrenSetSlotNewForeign(vm, 0, 0, sizeof(ServerData));
|
||||
ServerData* data = (ServerData*)wrenGetSlotForeign(vm, 0);
|
||||
data->handle = (uv_tcp_t*)malloc(sizeof(uv_tcp_t));
|
||||
uv_tcp_init(getLoop(), data->handle);
|
||||
data->handle->data = data;
|
||||
data->acceptFiber = NULL;
|
||||
data->pendingHead = NULL;
|
||||
data->pendingTail = NULL;
|
||||
}
|
||||
|
||||
void serverFinalize(void* data)
|
||||
{
|
||||
ServerData* serverData = (ServerData*)data;
|
||||
if (serverData->handle) {
|
||||
uv_close((uv_handle_t*)serverData->handle, closeCallback);
|
||||
}
|
||||
// Free pending list
|
||||
SocketListNode* current = serverData->pendingHead;
|
||||
while (current) {
|
||||
SocketListNode* next = current->next;
|
||||
uv_close((uv_handle_t*)current->handle, closeCallback);
|
||||
free(current);
|
||||
current = next;
|
||||
}
|
||||
}
|
||||
|
||||
void netCaptureClass(WrenVM* vm)
|
||||
{
|
||||
// Slot 0 is the Socket class itself because this is a static method on Socket.
|
||||
socketClassHandle = wrenGetSlotHandle(vm, 0);
|
||||
}
|
||||
|
||||
void netShutdown()
|
||||
{
|
||||
if (socketClassHandle != NULL) {
|
||||
wrenReleaseHandle(getVM(), socketClassHandle);
|
||||
socketClassHandle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void connectionCallback(uv_stream_t* server, int status)
|
||||
{
|
||||
ServerData* data = (ServerData*)server->data;
|
||||
|
||||
if (status < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
uv_tcp_t* client = (uv_tcp_t*)malloc(sizeof(uv_tcp_t));
|
||||
uv_tcp_init(getLoop(), client);
|
||||
|
||||
if (uv_accept(server, (uv_stream_t*)client) == 0) {
|
||||
if (data->acceptFiber) {
|
||||
WrenHandle* fiber = data->acceptFiber;
|
||||
data->acceptFiber = NULL;
|
||||
|
||||
WrenVM* vm = getVM();
|
||||
wrenEnsureSlots(vm, 3);
|
||||
|
||||
if (socketClassHandle == NULL) {
|
||||
fprintf(stderr, "FATAL: socketClassHandle NULL in connectionCallback\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
wrenSetSlotHandle(vm, 1, socketClassHandle);
|
||||
wrenSetSlotNewForeign(vm, 2, 1, sizeof(SocketData));
|
||||
SocketData* sockData = (SocketData*)wrenGetSlotForeign(vm, 2);
|
||||
sockData->handle = client;
|
||||
client->data = sockData;
|
||||
sockData->fiber = NULL;
|
||||
sockData->connectReq = NULL;
|
||||
sockData->writeReq = NULL;
|
||||
|
||||
schedulerResume(fiber, true);
|
||||
schedulerFinishResume();
|
||||
} else {
|
||||
SocketListNode* node = (SocketListNode*)malloc(sizeof(SocketListNode));
|
||||
node->handle = client;
|
||||
node->next = NULL;
|
||||
if (data->pendingTail) {
|
||||
data->pendingTail->next = node;
|
||||
data->pendingTail = node;
|
||||
} else {
|
||||
data->pendingHead = node;
|
||||
data->pendingTail = node;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
uv_close((uv_handle_t*)client, closeCallback);
|
||||
}
|
||||
}
|
||||
|
||||
void serverBind(WrenVM* vm)
|
||||
{
|
||||
ServerData* data = (ServerData*)wrenGetSlotForeign(vm, 0);
|
||||
const char* host = wrenGetSlotString(vm, 1);
|
||||
int port = (int)wrenGetSlotDouble(vm, 2);
|
||||
|
||||
struct sockaddr_in addr;
|
||||
uv_ip4_addr(host, port, &addr);
|
||||
|
||||
uv_tcp_bind(data->handle, (const struct sockaddr*)&addr, 0);
|
||||
int r = uv_listen((uv_stream_t*)data->handle, 128, connectionCallback);
|
||||
if (r != 0) {
|
||||
wrenEnsureSlots(vm, 3);
|
||||
wrenSetSlotString(vm, 2, uv_strerror(r));
|
||||
wrenAbortFiber(vm, 2);
|
||||
}
|
||||
}
|
||||
|
||||
void serverAccept(WrenVM* vm)
|
||||
{
|
||||
ServerData* data = (ServerData*)wrenGetSlotForeign(vm, 0);
|
||||
|
||||
if (data->pendingHead) {
|
||||
SocketListNode* node = data->pendingHead;
|
||||
data->pendingHead = node->next;
|
||||
if (data->pendingHead == NULL) data->pendingTail = NULL;
|
||||
|
||||
uv_tcp_t* client = node->handle;
|
||||
free(node);
|
||||
|
||||
wrenEnsureSlots(vm, 3);
|
||||
if (socketClassHandle == NULL) {
|
||||
fprintf(stderr, "FATAL: socketClassHandle NULL in serverAccept\n");
|
||||
exit(1);
|
||||
}
|
||||
wrenSetSlotHandle(vm, 2, socketClassHandle);
|
||||
wrenSetSlotNewForeign(vm, 0, 2, sizeof(SocketData));
|
||||
|
||||
SocketData* sockData = (SocketData*)wrenGetSlotForeign(vm, 0);
|
||||
sockData->handle = client;
|
||||
client->data = sockData;
|
||||
sockData->fiber = NULL;
|
||||
sockData->connectReq = NULL;
|
||||
sockData->writeReq = NULL;
|
||||
} else {
|
||||
WrenHandle* fiber = wrenGetSlotHandle(vm, 1);
|
||||
data->acceptFiber = fiber;
|
||||
wrenSetSlotNull(vm, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void serverClose(WrenVM* vm)
|
||||
{
|
||||
ServerData* data = (ServerData*)wrenGetSlotForeign(vm, 0);
|
||||
if (data->handle && !uv_is_closing((uv_handle_t*)data->handle)) {
|
||||
uv_close((uv_handle_t*)data->handle, closeCallback);
|
||||
data->handle = NULL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef net_h
|
||||
#define net_h
|
||||
|
||||
#include "wren.h"
|
||||
|
||||
void netBindSocketClass(WrenVM* vm);
|
||||
void netBindServerClass(WrenVM* vm);
|
||||
|
||||
// Socket methods
|
||||
void socketAllocate(WrenVM* vm);
|
||||
void socketFinalize(void* data);
|
||||
void socketConnect(WrenVM* vm);
|
||||
void socketWrite(WrenVM* vm);
|
||||
void socketRead(WrenVM* vm);
|
||||
void socketClose(WrenVM* vm);
|
||||
|
||||
// Server methods
|
||||
void serverAllocate(WrenVM* vm);
|
||||
void serverFinalize(void* data);
|
||||
void serverBind(WrenVM* vm);
|
||||
void serverAccept(WrenVM* vm);
|
||||
void serverClose(WrenVM* vm);
|
||||
|
||||
#endif
|
||||
Vendored
+62
@@ -0,0 +1,62 @@
|
||||
import "scheduler" for Scheduler
|
||||
|
||||
foreign class Socket {
|
||||
construct new() {}
|
||||
|
||||
static connect(host, port) {
|
||||
if (!(host is String)) Fiber.abort("Host must be a string.")
|
||||
if (!(port is Num)) Fiber.abort("Port must be a number.")
|
||||
|
||||
var socket = Socket.new()
|
||||
Scheduler.await_ { socket.connect_(host, port, Fiber.current) }
|
||||
return socket
|
||||
}
|
||||
|
||||
write(data) {
|
||||
if (!(data is String)) Fiber.abort("Data must be a string.")
|
||||
return Scheduler.await_ { write_(data, Fiber.current) }
|
||||
}
|
||||
|
||||
read() {
|
||||
return Scheduler.await_ { read_(Fiber.current) }
|
||||
}
|
||||
|
||||
close() {
|
||||
close_()
|
||||
}
|
||||
|
||||
foreign connect_(host, port, fiber)
|
||||
foreign write_(data, fiber)
|
||||
foreign read_(fiber)
|
||||
foreign close_()
|
||||
foreign static captureClass_()
|
||||
}
|
||||
|
||||
Socket.captureClass_()
|
||||
|
||||
foreign class Server {
|
||||
construct new() {}
|
||||
|
||||
static bind(host, port) {
|
||||
if (!(host is String)) Fiber.abort("Host must be a string.")
|
||||
if (!(port is Num)) Fiber.abort("Port must be a number.")
|
||||
|
||||
var server = Server.new()
|
||||
server.bind_(host, port)
|
||||
return server
|
||||
}
|
||||
|
||||
accept() {
|
||||
var socket = accept_(Fiber.current)
|
||||
if (socket != null) return socket
|
||||
return Scheduler.runNextScheduled_()
|
||||
}
|
||||
|
||||
close() {
|
||||
close_()
|
||||
}
|
||||
|
||||
foreign bind_(host, port)
|
||||
foreign accept_(fiber)
|
||||
foreign close_()
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// Please do not edit this file. It has been generated automatically
|
||||
// from `src/module/net.wren` using `util/wren_to_c_string.py`
|
||||
|
||||
static const char* netModuleSource =
|
||||
"import \"scheduler\" for Scheduler\n"
|
||||
"\n"
|
||||
"foreign class Socket {\n"
|
||||
" construct new() {}\n"
|
||||
"\n"
|
||||
" static connect(host, port) {\n"
|
||||
" if (!(host is String)) Fiber.abort(\"Host must be a string.\")\n"
|
||||
" if (!(port is Num)) Fiber.abort(\"Port must be a number.\")\n"
|
||||
" \n"
|
||||
" var socket = Socket.new()\n"
|
||||
" Scheduler.await_ { socket.connect_(host, port, Fiber.current) }\n"
|
||||
" return socket\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" write(data) {\n"
|
||||
" if (!(data is String)) Fiber.abort(\"Data must be a string.\")\n"
|
||||
" return Scheduler.await_ { write_(data, Fiber.current) }\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" read() {\n"
|
||||
" return Scheduler.await_ { read_(Fiber.current) }\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" close() {\n"
|
||||
" close_()\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" foreign connect_(host, port, fiber)\n"
|
||||
" foreign write_(data, fiber)\n"
|
||||
" foreign read_(fiber)\n"
|
||||
" foreign close_()\n"
|
||||
" foreign static captureClass_()\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"Socket.captureClass_()\n"
|
||||
"\n"
|
||||
"foreign class Server {\n"
|
||||
" construct new() {}\n"
|
||||
"\n"
|
||||
" static bind(host, port) {\n"
|
||||
" if (!(host is String)) Fiber.abort(\"Host must be a string.\")\n"
|
||||
" if (!(port is Num)) Fiber.abort(\"Port must be a number.\")\n"
|
||||
" \n"
|
||||
" var server = Server.new()\n"
|
||||
" server.bind_(host, port)\n"
|
||||
" return server\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" accept() {\n"
|
||||
" var socket = accept_(Fiber.current)\n"
|
||||
" if (socket != null) return socket\n"
|
||||
" return Scheduler.runNextScheduled_()\n"
|
||||
" }\n"
|
||||
" \n"
|
||||
" close() {\n"
|
||||
" close_()\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" foreign bind_(host, port)\n"
|
||||
" foreign accept_(fiber)\n"
|
||||
" foreign close_()\n"
|
||||
"}\n";
|
||||
Reference in New Issue
Block a user