1 | const Domain = require('./domain.js');
|
2 | const BlazeError = require('./error.js');
|
3 |
|
4 | const urlPattern = require('url-pattern');
|
5 | const url = require('url');
|
6 | const fs = require('fs-extra');
|
7 | const path = require('path');
|
8 | const mime = require('mime-types');
|
9 | const protocols = {
|
10 | "https": require('https'),
|
11 | "http": require('http')
|
12 | }
|
13 |
|
14 | const Plugin = require('./plugins.js');
|
15 | const form = require('./form.js');
|
16 | const context = require('./context.js');
|
17 |
|
18 | module.exports = class server {
|
19 | constructor(config) {
|
20 | this.Instances = [];
|
21 | this.Log = new require('./logger.js')(config.logger);
|
22 |
|
23 | let instances = [];
|
24 | config.http.ports.forEach(port => {
|
25 | instances.push({ protocol: 'http', port: port });
|
26 | });
|
27 |
|
28 | config.https.ports.forEach(port => {
|
29 | instances.push({ protocol: 'https', port: port });
|
30 | });
|
31 | instances.forEach(instanceConfig => {
|
32 | let serverInstance = protocols[instanceConfig.protocol].createServer({ SNICallback: Domain.handleSSL });
|
33 | serverInstance.on('request', async (request, response) => { await this.handleRequest(request, response, instanceConfig.protocol) });
|
34 | serverInstance.on('timeout', async (socket) => { await this.handleTimeout(socket.$_CONTEXT, new BlazeError('Request Timed Out', 408)); })
|
35 | serverInstance.on('error', async (error) => { await this.handleError(socket.$_CONTEXT, new BlazeError(error, 500)); });
|
36 | this.Instances.push({
|
37 | port: instanceConfig.port, server: serverInstance
|
38 | });
|
39 | });
|
40 | }
|
41 |
|
42 | async handleRequest(request, response, protocol) {
|
43 | let $_SOCKET = request.socket;
|
44 | let $_CONTEXT = $_SOCKET.$_CONTEXT = new context(request, response, this.Log);
|
45 |
|
46 | $_CONTEXT.$_REQUEST.uri = new URL(protocol + '://' + request.headers.host + request.url);
|
47 | $_CONTEXT.$_DOMAIN = await Domain.find(request.uri.host, protocol === 'https');
|
48 |
|
49 | try {
|
50 | if ($_CONTEXT.$_DOMAIN) {
|
51 | let filename = $_CONTEXT.$_REQUEST.uri.pathname;
|
52 | for (let pattern in $_CONTEXT.$_DOMAIN.redirects) {
|
53 | let match = new urlPattern(pattern)
|
54 | .match($_CONTEXT.$_REQUEST.uri.hostname + $_CONTEXT.$_REQUEST.uri.pathname);
|
55 |
|
56 | if (match) {
|
57 | let redirect = $_CONTEXT.$_DOMAIN.redirects[pattern];
|
58 | response.writeHead(redirect.code, { 'Location': $_CONTEXT.$_REQUEST.uri.protocol + '//' + eval('`' + redirect.rewrite.replace(/\$\{/g, '${match.') + '`') });
|
59 | break;
|
60 | }
|
61 | }
|
62 | for (let pattern in $_CONTEXT.$_DOMAIN.rewrites) {
|
63 | let match = new urlPattern(pattern)
|
64 | .match($_CONTEXT.$_REQUEST.uri.pathname);
|
65 |
|
66 | if (match) {
|
67 | let evaled = eval('`' + $_CONTEXT.$_DOMAIN.rewrites[pattern].replace(/\$\{/g, '${match.') + '`');
|
68 | let fileObject = new URL(evaled, $_CONTEXT.$_REQUEST.uri.origin + '/');
|
69 | filename = evaled.replace(fileObject.search, '')
|
70 | $_CONTEXT.$_REQUEST.uri.template = fileObject.searchParams;
|
71 | break;
|
72 | }
|
73 | }
|
74 |
|
75 | request.method = request.method.toLowerCase();
|
76 |
|
77 | $_CONTEXT.setFilePath(path.join($_CONTEXT.$_DOMAIN.root, decodeURIComponent(filename) || "/"));
|
78 |
|
79 | await Plugin.fire($_CONTEXT, Plugin.BEFORE_REQUEST);
|
80 |
|
81 | if (
|
82 | !fs.existsSync($_CONTEXT.$_SERVER.$_FILEPATH) ||
|
83 | !fs.statSync($_CONTEXT.$_SERVER.$_FILEPATH).isFile()
|
84 | ) {
|
85 | throw (new BlazeError(request.uri.href + " not found", 404));
|
86 | }
|
87 |
|
88 | if (!$_CONTEXT.$_RESPONSE.finished) {
|
89 | let contentType = mime.contentType($_CONTEXT.$_FILENAME);
|
90 | $_CONTEXT.$_RESPONSE.setHeader("Content-Type", contentType);
|
91 |
|
92 | await $_CONTEXT.$_DOMAIN.parse($_CONTEXT, fs.readFileSync($_CONTEXT.$_SERVER.$_FILEPATH, mime.charset(contentType) || null))
|
93 |
|
94 | this.end($_CONTEXT);
|
95 | }
|
96 | }
|
97 | } catch (error) {
|
98 | this.handleError($_CONTEXT, error);
|
99 | }
|
100 | }
|
101 |
|
102 | async end($_CONTEXT) {
|
103 | if (!$_CONTEXT.$_RESPONSE.finished) {
|
104 | $_CONTEXT.$_RESPONSE.end($_CONTEXT.$_SERVER.Response);
|
105 | await Plugin.fire($_CONTEXT, Plugin.AFTER_REQUEST);
|
106 | }
|
107 | }
|
108 |
|
109 | async handleTimeout($_CONTEXT, error) {
|
110 | await Plugin.fire($_CONTEXT, Plugin.BEFORE_TIMEOUT);
|
111 | await this.handleError($_CONTEXT, error);
|
112 | await Plugin.fire($_CONTEXT, Plugin.AFTER_TIMEOUT);
|
113 | }
|
114 |
|
115 | async handleError($_CONTEXT, error) {
|
116 | await Plugin.fire($_CONTEXT, Plugin.BEFORE_ERROR);
|
117 | if (!(error instanceof BlazeError)) {
|
118 | error = new BlazeError(error, 500);
|
119 | }
|
120 |
|
121 | $_CONTEXT.$_LOG(error)
|
122 | $_CONTEXT.$_SERVER.Response = error.format();
|
123 | this.end($_CONTEXT);
|
124 | await Plugin.fire($_CONTEXT, Plugin.AFTER_ERROR);
|
125 | }
|
126 |
|
127 | async listen() {
|
128 | this.Instances.forEach(instance => {
|
129 | instance.server.listen(instance.port);
|
130 | });
|
131 | }
|
132 | } |
\ | No newline at end of file |