Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- 'use strict';
- const http = require('http');
- const https = require('https');
- const url = require('url');
- const path = require('path');
- const fs = require('fs');
- const mimetypes = require(path.join(__dirname, 'lib', 'mimetypes'));
- const etag = require(path.join(__dirname, 'lib', 'etag'));
- const FILE_METHODS = ['GET', 'HEAD'];
- const ADD_CHARSET_EXTENSIONS = ['js', 'css', 'htm', 'html', 'txt'];
- class Server {
- constructor(options = {}) {
- this.hostname = options.hostname || 'localhost';
- this.httpPort = options.httpPort || 80;
- this.httpsPort = options.httpsPort || 443;
- this.httpsServerOptions = options.httpsServerOptions || {
- key: fs.readFileSync(path.join(__dirname, 'certs', 'server-key.pem')),
- cert: fs.readFileSync(path.join(__dirname, 'certs', 'server-cert.pem'))
- };
- this.staticRoot = options.staticRoot || path.join(__dirname, 'static');
- this.maxAge = options.maxAge || /* two weeks = */ 60 * 60 * 24 * 14;
- this.addCharset = options.addCharset || 'utf-8';
- this.addCharsetExtensions = ADD_CHARSET_EXTENSIONS.concat(
- options.addCharsetExtensions || []
- );
- }
- defaultHandler(req, res) {
- res.writeHead(200);
- res.end("It works!");
- }
- serveFile(filename, stat, req, res) {
- // https://www.codeproject.com/articles/866319/http-not-modified-an-introduction
- let tag = etag(stat);
- // OK
- if ('if-none-match' in req.headers) {
- // console.log(req.headers['if-none-match']);
- if (req.headers['if-none-match'] === tag) {
- return this.notModified(res);
- }
- } else if ('if-modified-since' in req.headers) { // OK
- // number | NaN
- // console.log(req.headers['if-modified-since']);
- let modifiedSince = Date.parse(req.headers['if-modified-since']);
- // console.log(modifiedSince);
- // 1482127232000
- // console.log(stat.mtime.getTime());
- // 1482127232838
- // console.log(Date.parse(stat.mtime));
- // 1482127232000
- if (modifiedSince >= Date.parse(stat.mtime)) {
- return this.notModified(res);
- }
- }
- let type = mimetypes.guess(filename);
- // Добавляем кодировку UTF-8 к файлам с определенным расширением
- let pos = filename.lastIndexOf('.');
- if (pos > -1) {
- let extension = filename.slice(pos + 1);
- if (this.addCharsetExtensions.includes(extension)) {
- type = `${type}; charset=${this.addCharset}`;
- }
- }
- let headers = {
- 'Content-Type': type,
- 'Content-Length': stat.size,
- 'Cache-Control': `max-age=${this.maxAge}, public`,
- 'ETag': tag,
- 'Last-Modified': stat.mtime.toUTCString(),
- }
- res.writeHead(200, headers);
- if (req.method === 'HEAD') {
- return res.end();
- }
- let stream = fs.createReadStream(filename);
- stream.on('error', err => {
- res.writeHead(500);
- res.end(err.message);
- });
- stream.pipe(res);
- }
- start() {
- this.http = http.createServer(this.requestListener.bind(this))
- .listen(this.httpPort, this.hostname);
- this.https = https.createServer(
- this.httpsServerOptions,
- this.requestListener.bind(this)
- ).listen(this.httpsPort, this.hostname);
- }
- // Private methods
- normalizeHeaderName(name) {
- // content-type -> Content-Type
- // Можно предварительно имя перевести в нижний регистр name.toLowerCase()
- // для универсальности
- return name.split('-')
- .map(s => s.substr(0, 1).toUpperCase() + s.slice(1))
- .join('-');;
- }
- notModified(res) {
- res.writeHead(304);
- res.end();
- }
- requestListener(req, res) {
- console.log(
- '%s %s %s %s',
- req.socket.remoteAddress,
- new Date().toISOString(),
- req.method,
- req.url
- );
- res.setHeader('Server', 'NodeReverse');
- // Отправлен ли запрос через https?
- req.secure = req.socket.localPort === this.httpsPort;
- console.log('Request is secure', req.secure);
- if (!FILE_METHODS.includes(req.method)) {
- return this.defaultHandler(req, res);
- }
- // Я не знаю нужно ли убирать .. из пути или NodeJS сам это делает, но
- // на всякий случай лучше лишний раз вызвать path.normalize
- let filename = path.join(
- this.staticRoot,
- decodeURI(path.normalize(url.parse(req.url).pathname))
- );
- fs.exists(filename, exists => {
- if (!exists) {
- return this.defaultHandler(req, res);
- }
- fs.stat(filename, (err, stat) => {
- if (err) {
- res.writeHead(500);
- return res.end(err.message);
- }
- if (stat.isFile()) {
- return this.serveFile(filename, stat, req, res);
- }
- this.defaultHandler(req, res);
- });
- });
- }
- }
- module.exports = {Server};
Advertisement
Add Comment
Please, Sign In to add comment