Skip to content

Instantly share code, notes, and snippets.

@monochromer
Last active March 18, 2021 04:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save monochromer/045432184e8e231092550766b2d2b7d1 to your computer and use it in GitHub Desktop.
Save monochromer/045432184e8e231092550766b2d2b7d1 to your computer and use it in GitHub Desktop.
Веб-сервер на Node.js как итератор, как поток
const http = require('http');
const { once } = require('events');
class WebServer extends http.Server {
async *[Symbol.asyncIterator]() {
while (this.listening) {
const [ req, res ] = await once(this, 'request');
yield { req, res };
}
}
}
class Application {
middlewares = [];
server = null;
use(middleware) {
this.middlewares.push(middleware);
}
createHandler() {
return (ctx) => {
return this.middlewares.reduce((acc, fn) => {
return acc.then(() => fn(ctx))
}, Promise.resolve())
}
}
async run(...args) {
const server = this.server = new WebServer();
server.listen(...args);
const handler = this.createHandler();
for await (const ctx of server) {
handler(ctx)
.catch(error => {
ctx.res.statusCode = 500;
ctx.res.end(err.message);
});
}
}
}
const PORT = process.env.PORT || 3000;
const app = new Application();
app.use(async (ctx) => {
console.log('=== log ===');
console.log(ctx.req.url);
});
app.use(async (ctx) => {
ctx.state = ctx.state || {};
ctx.state.user = {
name: 'Alex',
age: 33
}
});
app.use(async (ctx) => {
ctx.res.setHeader('content-type', 'application/json');
ctx.res.end(JSON.stringify(ctx.state.user));
});
app.run(PORT, () => console.log(`Server is running on http://localhost:${PORT}`));
const http = require('http');
const { once } = require('events');
const { Readable } = require('stream');
const PORT = 3000;
class WebServer extends http.Server {
constructor() {
super(arguments);
}
async *[Symbol.asyncIterator]() {
while (this.listening) {
const [ req, res ] = await once(this, 'request');
yield {
req,
res
}
}
}
}
class StreamServer extends Readable {
constructor(options) {
super({
...options,
...{
objectMode: true
}
});
this._server = new WebServer;
this._server.on('close', () => {
this.push(null);
});
this._server.on('request', (req, res) => {
const ctx = { req, res }
if (!this.push(ctx)) {
this._server.close(() => {
console.log('Server is closed')
});
}
})
}
_read() {
if (!this._server.listening) {
this._server.listen(PORT, () => {
console.log(`Start on http://localhost:${PORT}`);
});
}
}
}
// server as iterator
// const server = new WebServer();
// server.listen(PORT, () => {
// console.log(`Start on http://localhost:${PORT}`);
// });
// (async () => {
// for await (let ctx of server) {
// ctx.res.end(JSON.stringify({
// url: ctx.req.url,
// headers: ctx.req.headers
// }))
// }
// })();
// server as stream
const server = new StreamServer();
server.on('data', ctx => {
ctx.res.end(JSON.stringify({
url: ctx.req.url,
headers: ctx.req.headers
}));
});
const http = require('http');
const { on } = require('events');
const PORT = process.env.PORT || 3000;
class WebApplication extends http.Server {
async listen(...args) {
super.listen(...args);
for await (const [req, res] of on(this, 'request')) {
try {
const { method, url } = req;
const eventName = method.toUpperCase() + ' ' + url;
if (this.listenerCount(eventName) > 0) {
this.emit(eventName, req, res);
} else {
const notFoundError = new Error('not found');
notFoundError.name = 'NotFound';
throw notFoundError;
}
} catch (error) {
switch (true) {
case (error.name === 'NotFound'): {
res.statusCode = 404;
res.end('NotFound');
break;
}
default: {
res.statusCode = 500;
res.end((error ?? 'error').toString());
}
}
}
}
}
}
const app = new WebApplication();
app.on('GET /', (req, res) => {
res.end('index');
});
app.on('GET /error/', (req, res) => {
throw new Error('boom')
});
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment