Skip to content

Commit

Permalink
feat: add new overloading method for serve
Browse files Browse the repository at this point in the history
  • Loading branch information
antho-bunny committed Aug 29, 2024
1 parent 49b0fa1 commit c6b1efd
Show file tree
Hide file tree
Showing 8 changed files with 173 additions and 13 deletions.
5 changes: 5 additions & 0 deletions .changeset/fuzzy-tables-think.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@bunny.net/edgescript-sdk": minor
---

New overloading for serve to avoid putting listener
2 changes: 1 addition & 1 deletion example/deno-simple-http-page/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ function sleep(ms: number): Promise<void> {
}

console.log("Starting server...");
BunnySDK.net.http.serve(async (req) => {
BunnySDK.net.http.serve({}, async (req) => {
console.log(`[INFO]: ${req.method} - ${req.url}`);
await sleep(1);
return new Response("blbl");
Expand Down
27 changes: 27 additions & 0 deletions libs/bunny-sdk/src/net/ip.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import * as IP from "./ip.ts";

test('toString should convert IPv4 tuple to string', () => {
const ip: IP.IPv4 = [192, 168, 1, 1];
expect(IP.toString(ip)).toBe('192.168.1.1');
});

test('tryParseFromString should parse valid IP string', () => {
const ipString = '192.168.1.1';
const expected: IP.IPv4 = [192, 168, 1, 1];
expect(IP.tryParseFromString(ipString)).toEqual(expected);
});

test('tryParseFromString should return SyntaxError for invalid IP string', () => {
const invalidIpString = '999.999.999.999';
expect(IP.tryParseFromString(invalidIpString)).toBeInstanceOf(SyntaxError);
});

test('tryParseFromString should return SyntaxError for non-numeric IP string', () => {
const invalidIpString = 'abc.def.ghi.jkl';
expect(IP.tryParseFromString(invalidIpString)).toBeInstanceOf(SyntaxError);
});

test('tryParseFromString should return SyntaxError for incomplete IP string', () => {
const invalidIpString = '192.168.1';
expect(IP.tryParseFromString(invalidIpString)).toBeInstanceOf(SyntaxError);
});
11 changes: 11 additions & 0 deletions libs/bunny-sdk/src/net/ip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,14 @@ export type IPv4 = [Range<0, 255>, Range<0, 255>, Range<0, 255>, Range<0, 255>];
export function toString(ip: IPv4): string {
return `${ip[0]}.${ip[1]}.${ip[2]}.${ip[3]}`;
}

/**
* Try to parse na IP
*/
export function tryParseFromString(ip: string): IPv4 | SyntaxError {
const parts = ip.split('.').map(Number);
if (parts.length !== 4 || parts.some(part => isNaN(part) || part < 0 || part > 255)) {
return new SyntaxError('Invalid IP address');
}
return [parts[0], parts[1], parts[2], parts[3]] as IPv4;
}
49 changes: 40 additions & 9 deletions libs/bunny-sdk/src/net/serve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,55 @@ type ServeHandler = {} & unknown;
/**
* Serves HTTP requests on the given [TcpListener]
*/
function serve(handler: ServerHandler, listener: { port: number; hostname: string; }): ServeHandler;
function serve(handler: ServerHandler, listener: TcpListener): ServeHandler;
function serve(handler: ServerHandler, listener?: TcpListener | { port: number; hostname: string; },): ServeHandler {
const platform = internal_getPlatform();

const listenerUnion = Tcp.isTcpListener(listener) ? listener : Tcp.unstable_new();
function is_port_and_hostname(value: unknown): value is { port: number; hostname: string; } {
if (typeof value === "object" && value !== null) {
let port = value["port"];
return port !== undefined && typeof port === "number" && value["hostname"] !== undefined;
}

return false;
}

function serve(handler: ServerHandler): ServeHandler;
function serve(listener: { port: number; hostname: string; }, handler: ServerHandler): ServeHandler;
function serve(listener: TcpListener, handler: ServerHandler): ServeHandler;
function serve(listener: ServerHandler | { port: number; hostname: string; } | TcpListener, handler?: ServerHandler): ServeHandler {
let raw_handler: ServerHandler | undefined;
let raw_listener: TcpListener;

if (is_port_and_hostname(listener)) {
const addr = SocketAddr.v4.tryFromString(`${listener.hostname}:${listener.port}`);
if (addr instanceof Error) {
throw addr;
}
raw_listener = Tcp.bind(addr);
raw_handler = handler;
} else if (Tcp.isTcpListener(listener)) {
raw_handler = handler;
raw_listener = listener;
} else {
raw_handler = listener;
raw_listener = Tcp.unstable_new();
}

if (raw_handler === undefined) {
throw new Error("An issue happened.");
}

const platform = internal_getPlatform();

switch (platform.runtime) {
case "bunny": {
Bunny.v1.serve(handler);
Bunny.v1.serve(raw_handler);
return {};
}
case "node": {
return NodeImpl.node_serve(listenerUnion, handler);
return NodeImpl.node_serve(raw_listener, raw_handler);
}

case "deno": {
const addr = Tcp.unstable_local_addr(listenerUnion);
const addr = Tcp.unstable_local_addr(raw_listener);

if (!SocketAddr.isV4(addr)) {
throw new Error("An issue happened with the addr.");
Expand All @@ -42,7 +73,7 @@ function serve(handler: ServerHandler, listener?: TcpListener | { port: number;
const port = SocketAddr.v4.port(addr);
const hostname = Ip.toString(SocketAddr.v4.ip(addr));

Deno.serve({ port, hostname }, handler);
Deno.serve({ port, hostname }, raw_handler);
return {};
}
case "unknown": {
Expand Down
48 changes: 48 additions & 0 deletions libs/bunny-sdk/src/net/socket/v4.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { SocketAddrV4, port, tryFromString, ip } from './v4.ts';

describe('SocketAddrV4', () => {
describe('port', () => {
it('should return the port number', () => {
const addr: SocketAddrV4 = { _tag: "SocketAddrV4", port: 8080, ip: [127, 0, 0, 1] };
expect(port(addr)).toBe(8080);
});
});

describe('ip', () => {
it('should return the IP address', () => {
const addr: SocketAddrV4 = { _tag: "SocketAddrV4", port: 8080, ip: [127, 0, 0, 1] };
expect(ip(addr)).toStrictEqual([127, 0, 0, 1]);
});
});

describe('tryFromString', () => {
it('should parse a valid SocketAddrV4 string', () => {
const result = tryFromString("127.0.0.1:8080");
expect(result).toEqual({ _tag: "SocketAddrV4", port: 8080, ip: [127, 0, 0, 1] });
});

it('should return a SyntaxError for an invalid IP', () => {
const result = tryFromString("invalid_ip:8080");
expect(result).toBeInstanceOf(SyntaxError);
expect((result as SyntaxError).message).toBe('Invalid IP address');
});

it('should return a SyntaxError for an invalid port', () => {
const result = tryFromString("127.0.0.1:invalid_port");
expect(result).toBeInstanceOf(SyntaxError);
expect((result as SyntaxError).message).toBe('Invalid Port');
});

it('should return a SyntaxError for a port out of range', () => {
const result = tryFromString("127.0.0.1:70000");
expect(result).toBeInstanceOf(SyntaxError);
expect((result as SyntaxError).message).toBe('Invalid Port');
});

it('should return a SyntaxError for an invalid format', () => {
const result = tryFromString("127.0.0.1-8080");
expect(result).toBeInstanceOf(SyntaxError);
expect((result as SyntaxError).message).toBe('Invalid SocketAddrV4 address');
});
});
});
32 changes: 29 additions & 3 deletions libs/bunny-sdk/src/net/socket/v4.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { IPv4 } from "../ip.ts";
import * as IP from "../ip.ts";

export type SocketAddrV4 = {
readonly _tag: "SocketAddrV4",
port: number,
ip: IPv4,
ip: IP.IPv4,
};

/**
Expand All @@ -16,6 +16,32 @@ export function port(addr: SocketAddrV4): number {
/**
* Returns the IP address associated with this socket address.
*/
export function ip(addr: SocketAddrV4): IPv4 {
export function ip(addr: SocketAddrV4): IP.IPv4 {
return addr.ip;
}

/**
* Try to parse a SocketAddrV4
*/
export function tryFromString(value: string): SocketAddrV4 | SyntaxError {
const parts = value.split(':');

if (parts.length !== 2) {
return new SyntaxError('Invalid SocketAddrV4 address');
}

let ip = IP.tryParseFromString(parts[0]);
if (ip instanceof SyntaxError) {
return ip;
}
const port = Number(parts[1]);
if (isNaN(port) || port < 0 || port > 65535) {
return new SyntaxError('Invalid Port');
}

return ({
_tag: "SocketAddrV4",
port,
ip,
})
}
12 changes: 12 additions & 0 deletions libs/bunny-sdk/src/net/tcp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,15 @@ export function unstable_new(): TcpListener {
}
}


/**
* Bind an Addr
*/
export function bind(addr: SocketAddr.v4.SocketAddrV4): TcpListener {
// TODO: Add a proper bind to ensure the port is available.

return ({
_tag: 'TcpListener',
addr,
});
}

0 comments on commit c6b1efd

Please sign in to comment.