mirror of
https://github.com/186526/handlers.js
synced 2024-10-13 00:29:43 +00:00
Add Txiki.js Adapter.
This commit is contained in:
@ -7,6 +7,9 @@ export const platform = (() => {
|
||||
if (typeof Deno != "undefined") {
|
||||
return "Deno";
|
||||
}
|
||||
if (typeof tjs != "undefined") {
|
||||
return "txiki.js";
|
||||
}
|
||||
if (typeof self != "undefined") {
|
||||
return "Service Worker";
|
||||
}
|
||||
@ -18,6 +21,8 @@ export const version = (() => {
|
||||
return process.version;
|
||||
case "Deno":
|
||||
return Deno.version.deno;
|
||||
case "txiki.js":
|
||||
return tjs.versions.tjs;
|
||||
case "Service Worker":
|
||||
return undefined;
|
||||
default:
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
import { NodePlatformAdapter } from "./node";
|
||||
import { SWPlatformAdapter } from "./serviceworker";
|
||||
import { DenoPlatformAdapter } from "./deno";
|
||||
import { TxikiPlatformAdapter } from "./txiki";
|
||||
|
||||
export const platformAdapaterMapping = {
|
||||
"Node.js": NodePlatformAdapter,
|
||||
"Service Worker": SWPlatformAdapter,
|
||||
"Deno": DenoPlatformAdapter,
|
||||
"txiki.js": TxikiPlatformAdapter,
|
||||
};
|
||||
|
||||
export { NodePlatformAdapter, SWPlatformAdapter, DenoPlatformAdapter };
|
||||
export { NodePlatformAdapter, SWPlatformAdapter, DenoPlatformAdapter, TxikiPlatformAdapter };
|
||||
|
||||
94
src/platform/txiki.js/serveHttp.ts
Normal file
94
src/platform/txiki.js/serveHttp.ts
Normal file
@ -0,0 +1,94 @@
|
||||
import { request } from "../../interface/request";
|
||||
import { response } from "../../interface";
|
||||
import { headers } from "../../interface/headers";
|
||||
import { methodENUM } from "../../interface/method";
|
||||
|
||||
import statusCode from "./statusCode.json";
|
||||
|
||||
export class HttpConn {
|
||||
private closed: boolean = false;
|
||||
private conn: tjs.Connection;
|
||||
private reader: ReadableStreamDefaultReader;
|
||||
|
||||
constructor(Connection: tjs.Connection) {
|
||||
this.conn = Connection;
|
||||
this.reader = this.reader ?? this.conn.readable.getReader();
|
||||
}
|
||||
|
||||
private readMessage(httpMessage: string): request<any> {
|
||||
const lines = httpMessage.split("\n");
|
||||
const firstLine = lines[0];
|
||||
const dividingIndex = lines.indexOf("\r") ?? lines.indexOf("");
|
||||
const rawHeaders = lines.slice(1, dividingIndex);
|
||||
|
||||
const [method, path, version] = firstLine.split(" ");
|
||||
|
||||
const requestHeaders = new headers({});
|
||||
for (const header of rawHeaders) {
|
||||
const [key, value] = header.split(": ");
|
||||
requestHeaders.set(key, value);
|
||||
}
|
||||
|
||||
const url = new URL(path, `http://${requestHeaders.get("Host")}/` ?? `http://${this.conn.localAddress.ip}:${this.conn.localAddress.port}/`);
|
||||
|
||||
const body = lines.slice(dividingIndex + 1).join("\n");
|
||||
|
||||
const requestMessage = new request<any>(<methodENUM>method, url, requestHeaders, body, {}, this.conn.remoteAddress.ip);
|
||||
return requestMessage;
|
||||
}
|
||||
|
||||
private handleResponse(response: response<any>) {
|
||||
let responseMessage: string = "";
|
||||
responseMessage += "HTTP/1.1 " + response.status + " " + statusCode[<"100">response.status.toString()] ?? "";
|
||||
|
||||
response.headers.forEach((key, value) => {
|
||||
responseMessage += "\n" + key + ": " + value;
|
||||
});
|
||||
|
||||
responseMessage += "\n\n" + response.body;
|
||||
|
||||
this.conn.write(new TextEncoder().encode(responseMessage));
|
||||
this.conn.shutdown();
|
||||
this.closed = true;
|
||||
}
|
||||
|
||||
private async read(): Promise<request<any> | undefined> {
|
||||
let message = "";
|
||||
const { done, value } = await this.reader.read();
|
||||
|
||||
if (done || this.closed) {
|
||||
this.closed = true;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
message += String.fromCharCode(...Object.values(<{
|
||||
[key: string]: number
|
||||
}>value));
|
||||
|
||||
const requestMessage = this.readMessage(message);
|
||||
return requestMessage;
|
||||
}
|
||||
|
||||
[Symbol.asyncIterator]() {
|
||||
const httpConn = this;
|
||||
|
||||
return {
|
||||
async next() {
|
||||
if (httpConn.closed) return { done: true, value: undefined };
|
||||
return {
|
||||
done: false,
|
||||
value: {
|
||||
request: await httpConn.read(),
|
||||
respondWith: (response: response<any>) => {
|
||||
httpConn.handleResponse(response);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default function serveHttp(Connection: tjs.Connection) {
|
||||
return new HttpConn(Connection);
|
||||
}
|
||||
64
src/platform/txiki.js/statusCode.json
Normal file
64
src/platform/txiki.js/statusCode.json
Normal file
@ -0,0 +1,64 @@
|
||||
{
|
||||
"100": "Continue",
|
||||
"101": "Switching Protocols",
|
||||
"102": "Processing",
|
||||
"103": "Checkpoint",
|
||||
"200": "OK",
|
||||
"201": "Created",
|
||||
"202": "Accepted",
|
||||
"203": "Non-Authoritative Information",
|
||||
"204": "No Content",
|
||||
"205": "Reset Content",
|
||||
"206": "Partial Content",
|
||||
"207": "Multi-Status",
|
||||
"208": "Already Reported",
|
||||
"300": "Multiple Choices",
|
||||
"301": "Moved Permanently",
|
||||
"302": "Found",
|
||||
"303": "See Other",
|
||||
"304": "Not Modified",
|
||||
"305": "Use Proxy",
|
||||
"306": "Switch Proxy",
|
||||
"307": "Temporary Redirect",
|
||||
"308": "Permanent Redirect",
|
||||
"400": "Bad Request",
|
||||
"401": "Unauthorized",
|
||||
"402": "Payment Required",
|
||||
"403": "Forbidden",
|
||||
"404": "Not Found",
|
||||
"405": "Method Not Allowed",
|
||||
"406": "Not Acceptable",
|
||||
"407": "Proxy Authentication Required",
|
||||
"408": "Request Time-out",
|
||||
"409": "Conflict",
|
||||
"410": "Gone",
|
||||
"411": "Length Required",
|
||||
"412": "Precondition Failed",
|
||||
"413": "Request Entity Too Large",
|
||||
"414": "Request-URI Too Long",
|
||||
"415": "Unsupported Media Type",
|
||||
"416": "Requested Range Not Satisfiable",
|
||||
"417": "Expectation Failed",
|
||||
"418": "I'm a teapot",
|
||||
"421": "Unprocessable Entity",
|
||||
"422": "Misdirected Request",
|
||||
"423": "Locked",
|
||||
"424": "Failed Dependency",
|
||||
"426": "Upgrade Required",
|
||||
"428": "Precondition Required",
|
||||
"429": "Too Many Requests",
|
||||
"431": "Request Header Fileds Too Large",
|
||||
"451": "Unavailable For Legal Reasons",
|
||||
"500": "Internal Server Error",
|
||||
"501": "Not Implemented",
|
||||
"502": "Bad Gateway",
|
||||
"503": "Service Unavailable",
|
||||
"504": "Gateway Timeout",
|
||||
"505": "HTTP Version Not Supported",
|
||||
"506": "Variant Also Negotiates",
|
||||
"507": "Insufficient Storage",
|
||||
"508": "Loop Detected",
|
||||
"509": "Bandwidth Limit Exceeded",
|
||||
"510": "Not Extended",
|
||||
"511": "Network Authentication Required"
|
||||
}
|
||||
36
src/platform/txiki.ts
Normal file
36
src/platform/txiki.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import { platformAdapater } from "./index";
|
||||
import { request } from "../interface/request";
|
||||
import { response } from "../interface/response";
|
||||
import { router } from "../router";
|
||||
import serveHttp from "./txiki.js/serveHttp";
|
||||
|
||||
export class TxikiPlatformAdapter<T = any, K = any> implements platformAdapater {
|
||||
public router: router<T, K>;
|
||||
|
||||
constructor(router: router<T, K>) {
|
||||
this.router = router;
|
||||
}
|
||||
|
||||
async listen(port?: number): Promise<void> {
|
||||
const Server = await tjs.listen("tcp", "0.0.0.0", port);
|
||||
|
||||
for await (const conn of Server) {
|
||||
const httpConn = serveHttp(conn);
|
||||
|
||||
for await (const conn of httpConn) {
|
||||
if (typeof conn == "undefined" || typeof conn.request == "undefined") {
|
||||
return;
|
||||
}
|
||||
conn.respondWith(await this.router.respond(conn.request));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async handleRequest(nativeRequest: request<any>): Promise<request<T>> {
|
||||
return nativeRequest;
|
||||
}
|
||||
|
||||
async handleResponse(response: response<K>): Promise<response<K>> {
|
||||
return response;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user