🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

h11

Package Overview
Dependencies
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

h11 - npm Package Compare versions

Comparing version
0.0.1
to
0.0.2
+22
build.ts
import { $ } from 'bun';
await $`rm -rf ./dist`;
await Promise.all([
Bun.build({
entrypoints: ['./src/h11.ts'],
outdir: './dist',
}),
$`tsc --project tsconfig.types.json`,
Bun.build({
entrypoints: ['./src/providers/bun.ts'],
outdir: './dist',
}),
$`tsc --project tsconfig.types.bun.json`,
Bun.build({
entrypoints: ['./src/providers/node.ts'],
outdir: './dist',
external: ['*'],
}),
$`tsc --project tsconfig.types.node.json`,
]);
import { H11 } from '../src/h11.ts';
import { bunProviderFactory } from '../src/providers/bun.ts';
const h11 = new H11();
h11.get('/hello/world/sasha', () => {
return new Response(`it is a super path`);
});
h11.get('/hello/world/:name', ({ params }) => {
return new Response(`hello world mister ${params.name}`);
});
h11.get('/hello/**', ({ params }) => {
return new Response(`it is a wild path ${params.wild}`);
});
const bunProvider = bunProviderFactory({ h11 });
// const server = Bun.serve({
// // hostname: 'hello.world',
// unix: '\0benchmark-h11-bun',
// // port: '443',
// async fetch(req, server) {
// console.log('-----', 'req.url', req.url);
// // console.log('-----', 'args', args);
// // req.body.
// const res = await bunProvider(req, server);
// return res;
// },
// });
// console.log('-----', 'server', server.url);
Bun.serve({
unix: '/tmp/my-socket.sock', // abstract namespace socket
fetch(req) {
return new Response(`404!`);
},
});
import { createServer } from 'node:http';
import { H11 } from '../src/h11.ts';
import {
getAddress,
nodeProviderFactory,
} from '../src/providers/nodejs-old.ts';
const h11 = new H11();
h11.get('/hello/world/sasha', () => {
return new Response(`it is a super path`);
});
h11.get('/hello/world/:name', ({ params }) => {
return new Response(`hello world mister ${params.name}`);
});
h11.get('/hello/**', ({ params }) => {
return new Response(`it is a wild path ${params.wild}`);
});
h11.get('/**', ({ req }) => {
return new Response(req.url);
});
const nodeProvider = nodeProviderFactory({ h11 });
const server = createServer((req, res) => {
nodeProvider({
req,
res,
origin: getAddress(server) || 'http://localhost:3000',
});
});
server.listen(3000);
import { Readable } from 'stream';
console.log('-----', 'Readable', Readable);
import { Radix } from './radix/radix.ts';
const FORCE_STOP = Symbol('FORCE_STOP');
export type Method = 'GET' | 'POST' | 'DELET' | 'PUT' | 'PATCH';
type HandlerResponse = Promise<Response> | Response;
export type ExecProps = {
req: Request;
providers: Record<string, any>;
data: Record<any, any>;
};
export type HandlerPure = (props: Props) => HandlerResponse;
export type HandlerContainer = {
handler: HandlerPure;
plugins: Plugin[];
};
type Props = {
req: Request;
params: Record<string, string>;
data: Record<any, any>;
};
type Handler = HandlerPure | HandlerContainer;
export type Plugin = (props: Props) => HandlerResponse;
type NotFoundHandler = (req: Request) => HandlerResponse;
type ErrorHandler = (props: { error: Error } & Props) => HandlerResponse;
const defaultOnNotFound: NotFoundHandler = (req) => {
console.log(`h9: Not found ${req.url}`);
return new Response('Not Found', {
status: 404,
statusText: 'Not Found 404',
headers: {
'Content-Type': 'text/plain',
},
});
};
const defaultOnError: ErrorHandler = ({ req, error }) => {
console.error(`h9: Error ${req.url} - ${error.message}`);
return new Response('Error 500', {
status: 500,
statusText: 'System error 500',
headers: {
'Content-Type': 'text/plain',
},
});
};
export class H11<TExecProps extends ExecProps = ExecProps> {
types!: TExecProps;
radix = new Radix();
onNotFound: NotFoundHandler = defaultOnNotFound;
onError: ErrorHandler = defaultOnError;
private addRoute(pattern: string, method: Method, handler: Handler) {
const preparedHandler =
typeof handler === 'function' ? { handler, plugins: [] } : handler;
this.radix.add(pattern, method, preparedHandler);
}
get(pattern: string, handler: Handler) {
this.addRoute(pattern, 'GET', handler);
return this;
}
post(pattern: string, handler: Handler) {
this.addRoute(pattern, 'POST', handler);
return this;
}
async exec({ req, data, providers }: TExecProps) {
const url = new URL(req.url);
const findResult = this.radix.find(url.pathname, req.method as any);
if (!findResult) {
return this.onNotFound(req);
}
const props = { req, params: findResult.params, data, providers };
try {
for (const plugin of findResult.handler.plugins) {
const response = await plugin(props);
if (response) {
return response;
}
}
const response = await findResult.handler.handler(props);
return response;
} catch (error) {
return this.onError({ ...props, error: error as Error });
}
}
}
import type { Server } from 'bun';
import type { H11 } from '../h11.ts';
export const bunProviderFactory = ({ h11 }: { h11: H11 }) => {
return async (req: Request, server: Server) => {
const res = await h11.exec({
req,
providers: {
bun: {
req,
server,
},
},
data: {},
});
return res;
};
};
import type { IncomingMessage, ServerResponse, Server } from 'node:http';
import { Readable } from 'node:stream';
import type { H11 } from '../h11.ts';
const joinPaths = (elem1: string, elem2: string) => {
if (elem1.at(-1) === '/') {
if (elem2[0] === '/') {
return `${elem1}${elem2.slice(1)}`;
} else {
return elem1 + elem2;
}
} else {
if (elem2[0] === '/') {
return elem1 + elem2;
} else {
return `${elem1}/${elem2}`;
}
}
};
export const getAddress = (server: Server) => {
const address = server.address();
if (typeof address !== 'object' || address === null) {
return;
}
const protocol = 'http';
const port = `:${address.port}`;
let url: string;
if (address.family === 'IPv6') {
url = `[${address.address}]`;
} else {
url = address.address;
}
return `${protocol}://${url}${port}`;
};
const createReqFromNode = (req: IncomingMessage, origin: string) => {
const method = req.method;
const headers = new Headers(req.headers as Record<string, string>);
const body =
method === 'GET' || method === 'HEAD'
? null
: (Readable.toWeb(req) as any as ReadableStream);
// Создаем и возвращаем новый Request объект
return new Request(joinPaths(origin, req.url!), {
method,
headers,
body,
});
};
const sendNodeRes = (
responseNative: Response,
res: ServerResponse<IncomingMessage>
) => {
const headers: Record<string, string> = {};
responseNative.headers.forEach((value, key) => (headers[key] = value));
res.writeHead(responseNative.status, responseNative.statusText, headers);
if (responseNative.body) {
const resStream = Readable.fromWeb(responseNative.body as any);
resStream.pipe(res);
} else {
res.end();
}
};
export const nodeProviderFactory =
({ h11 }: { h11: H11 }) =>
async ({
req,
res,
origin,
}: {
req: IncomingMessage;
res: ServerResponse<IncomingMessage>;
origin: string;
}) => {
const preapredOrigin = req.headers['host'] || origin;
const preparedRes = createReqFromNode(req, preapredOrigin);
const execRes = await h11.exec({
req: preparedRes,
data: {},
providers: {
node: {
req,
res,
},
},
});
sendNodeRes(execRes, res);
};
export type ProviderResponse = {
req: Request;
providers: Record<string, any>;
};
type A = Record<string, any>;
type B = { hello: 'world' };
type C = A & B;
const c: C = null as any as C;
{
"extends": "./tsconfig.types.json",
"include": ["src/providers/bun.ts"]
}
{
"extends": "./tsconfig.types.json",
"include": ["src/providers/node.ts"]
}
+17
-4
{
"name": "h11",
"module": "src/h9.ts",
"type": "module",
"version": "0.0.1",
"version": "0.0.2",
"scripts": {
"build": "bun build ./src/h9.ts --outdir ./dist && bun run types",
"types": "tsc --project tsconfig.types.json"
"build": "bun run ./build.ts"
},
"exports": {
".": {
"types": "./dist/types/h11.d.ts",
"default": "./dist/h11.ts"
},
"./bun": {
"types": "./dist/types/providers/bun.d.ts",
"default": "./dist/bun.ts"
},
"./node": {
"types": "./dist/types/providers/node.d.ts",
"default": "./dist/bun.ts"
}
},
"devDependencies": {

@@ -15,4 +27,5 @@ "@types/bun": "latest",

"dependencies": {
"mitata": "^0.1.14",
"utftu": "^1.0.26"
}
}
+1
-1

@@ -1,2 +0,2 @@

import type { HandlerContainer, Method } from '../../h9.ts';
import type { HandlerContainer, Method } from '../../h11.ts';

@@ -3,0 +3,0 @@ export class Node {

@@ -1,2 +0,2 @@

import type { HandlerContainer, Method } from '../h9.ts';
import type { HandlerContainer, Method } from '../h11.ts';
import { Node, addNodeToChildren } from './node/node.ts';

@@ -6,2 +6,3 @@

// /hello/world/:name/:family
export class Radix {

@@ -85,2 +86,3 @@ root = new Node({ segment: '' });

// hello/world/:name
add(pattern: string, method: Method = 'GET', handler: HandlerContainer) {

@@ -87,0 +89,0 @@ const patternSegments = pattern.slice(1).split('/');

@@ -9,3 +9,3 @@ {

},
"include": ["src/h9.ts"]
"include": ["src/h11.ts"]
}
import { waitTime } from 'utftu';
// import { H4 } from './h4.ts';
// const h4 = new H4();
// h4.get('/hello/world/sasha', () => {
// return new Response(`it is a super path`);
// });
// h4.get('/hello/world/:name', ({ params }) => {
// return new Response(`hello world mister ${params.name}`);
// });
// h4.get('/hello/**', ({ params }) => {
// return new Response(`it is a wild path ${params.wild}`);
// });
// // hello/word/sasha
// h4.get('/test', () => {
// const stream = new ReadableStream({
// async start(controller) {
// // Генерация данных
// const encoder = new TextEncoder();
// const data = ['<body>', 'World', 'Streamed', 'Response'];
// for (const chunk of data) {
// controller.enqueue(encoder.encode(chunk + '\n'));
// await new Promise((resolve) => {
// setTimeout(resolve, 1000);
// });
// }
// controller.close();
// },
// });
// const response = new Response(stream, {
// headers: {
// 'Content-Type': 'text/plain',
// },
// });
// return response;
// });
const firstPart = `
<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body>
${'<div>1111</div>'.repeat(20)}
`;
const firstPart1 = `
<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body>
${'<div>1111</div>'.repeat(100)}
`;
const secondPart = `
<div>${'222'.repeat(1000)}</div>
</body>
</html>
`;
const headers = {
// Vary: 'RSC, Next-Router-State-Tree, Next-Router-Prefetch, Accept-Encoding',
// link: '</_next/static/media/a34f9d1faa5f3315-s.p.woff2>; rel=preload; as="font"; crossorigin=""; type="font/woff2"',
// 'Cache-Control': 'no-store, must-revalidate',
// 'X-Powered-By': 'Next.js',
'Content-Type': 'text/html; charset=utf-8',
// 'Content-Encoding': 'gzip',
// Date: 'Thu, 29 Aug 2024 20:52:08 GMT',
// Connection: 'keep-alive',
// 'Keep-Alive': 'timeout=5',
// 'Transfer-Encoding': 'chunked',
};
console.log('-----', 'header', headers);
Bun.serve({
fetch(req) {
const stream = new ReadableStream({
async start(controller) {
controller.enqueue(firstPart);
await waitTime(4000);
controller.enqueue(secondPart);
controller.close();
},
});
return new Response(stream, {
headers: headers,
});
},
});
import { Radix } from './radix/radix.ts';
export type Method = 'GET' | 'POST' | 'DELET' | 'PUT' | 'PATCH';
type HandlerResponse = Promise<Response> | Response;
type PluginReponse = HandlerResponse | Promise<void> | void;
export type HandlerPure = (props: Props) => HandlerResponse;
export type HandlerContainer = {
handler: HandlerPure;
plugins: Plugin[];
};
type Props = {
req: Request;
params: Record<string, string>;
data: Record<any, any>;
};
type Handler = HandlerPure | HandlerContainer;
export type Plugin = (props: Props) => HandlerResponse;
type NotFoundHandler = (req: Request) => HandlerResponse;
type ErrorHandler = (props: { error: Error } & Props) => HandlerResponse;
const defaultOnNotFound: NotFoundHandler = (req) => {
console.log(`h9: Not found ${req.url}`);
return new Response('Not Found', {
status: 404,
statusText: 'Not Found 404',
headers: {
'Content-Type': 'text/plain',
},
});
};
const defaultOnError: ErrorHandler = ({ req, error }) => {
console.error(`h9: Error ${req.url} - ${error.message}`);
return new Response('Error 500', {
status: 500,
statusText: 'System error 500',
headers: {
'Content-Type': 'text/plain',
},
});
};
export class H9 {
radix = new Radix();
onNotFound: NotFoundHandler = defaultOnNotFound;
onError: ErrorHandler = defaultOnError;
private addRoute(pattern: string, method: Method, handler: Handler) {
const preparedHandler =
typeof handler === 'function' ? { handler, plugins: [] } : handler;
this.radix.add(pattern, method, preparedHandler);
}
get(pattern: string, handler: Handler) {
this.addRoute(pattern, 'GET', handler);
return this;
}
post(pattern: string, handler: Handler) {
this.addRoute(pattern, 'POST', handler);
return this;
}
async exec(req: Request) {
const url = new URL(req.url);
const findResult = this.radix.find(url.pathname, req.method as any);
if (!findResult) {
return this.onNotFound(req);
}
const data = {};
const props = { req, params: findResult.params, data };
try {
for (const plugin of findResult.handler.plugins) {
const response = await plugin(props);
if (response) {
return response;
}
}
const response = await findResult.handler.handler(props);
return response;
} catch (error) {
return this.onError({ ...props, error: error as Error });
}
}
}
// import http from 'node:http';
// const a = http.createServer();
// const req = new Request();
// a.listen();

Sorry, the diff of this file is not supported yet