stuppid_bot

Untitled

Dec 19th, 2016
260
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. 'use strict';
  2. const http = require('http');
  3. const https = require('https');
  4. const url = require('url');
  5. const path = require('path');
  6. const fs = require('fs');
  7. const mimetypes = require(path.join(__dirname, 'lib', 'mimetypes'));
  8. const etag = require(path.join(__dirname, 'lib', 'etag'));
  9.  
  10. const FILE_METHODS = ['GET', 'HEAD'];
  11. const ADD_CHARSET_EXTENSIONS = ['js', 'css', 'htm', 'html', 'txt'];
  12.  
  13. class Server {
  14.   constructor(options = {}) {
  15.     this.hostname = options.hostname || 'localhost';
  16.     this.httpPort = options.httpPort || 80;
  17.     this.httpsPort = options.httpsPort || 443;
  18.     this.httpsServerOptions = options.httpsServerOptions || {
  19.       key: fs.readFileSync(path.join(__dirname, 'certs', 'server-key.pem')),
  20.       cert: fs.readFileSync(path.join(__dirname, 'certs', 'server-cert.pem'))
  21.     };
  22.     this.staticRoot = options.staticRoot || path.join(__dirname, 'static');
  23.     this.maxAge = options.maxAge || /* two weeks = */ 60 * 60 * 24 * 14;
  24.     this.addCharset = options.addCharset || 'utf-8';
  25.     this.addCharsetExtensions = ADD_CHARSET_EXTENSIONS.concat(
  26.       options.addCharsetExtensions || []
  27.     );
  28.   }
  29.  
  30.   defaultHandler(req, res) {
  31.     res.writeHead(200);
  32.     res.end("It works!");
  33.   }
  34.  
  35.   serveFile(filename, stat, req, res) {
  36.     // https://www.codeproject.com/articles/866319/http-not-modified-an-introduction
  37.     let tag = etag(stat);
  38.     // OK
  39.     if ('if-none-match' in req.headers) {
  40.       // console.log(req.headers['if-none-match']);
  41.       if (req.headers['if-none-match'] === tag) {
  42.         return this.notModified(res);
  43.       }
  44.     } else if ('if-modified-since' in req.headers) { // OK
  45.       // number | NaN
  46.       // console.log(req.headers['if-modified-since']);
  47.       let modifiedSince = Date.parse(req.headers['if-modified-since']);
  48.       // console.log(modifiedSince);
  49.       // 1482127232000
  50.       // console.log(stat.mtime.getTime());
  51.       // 1482127232838
  52.       // console.log(Date.parse(stat.mtime));
  53.       // 1482127232000
  54.       if (modifiedSince >= Date.parse(stat.mtime)) {
  55.         return this.notModified(res);
  56.       }
  57.     }
  58.     let type = mimetypes.guess(filename);
  59.     // Добавляем кодировку UTF-8 к файлам с определенным расширением
  60.     let pos = filename.lastIndexOf('.');
  61.     if (pos > -1) {
  62.       let extension = filename.slice(pos + 1);
  63.       if (this.addCharsetExtensions.includes(extension)) {
  64.         type = `${type}; charset=${this.addCharset}`;
  65.       }
  66.     }
  67.     let headers = {
  68.       'Content-Type': type,
  69.       'Content-Length': stat.size,
  70.       'Cache-Control': `max-age=${this.maxAge}, public`,
  71.       'ETag': tag,
  72.       'Last-Modified': stat.mtime.toUTCString(),
  73.     }
  74.     res.writeHead(200, headers);
  75.     if (req.method === 'HEAD') {
  76.       return res.end();
  77.     }
  78.     let stream = fs.createReadStream(filename);
  79.     stream.on('error', err => {
  80.       res.writeHead(500);
  81.       res.end(err.message);
  82.     });
  83.     stream.pipe(res);
  84.   }
  85.  
  86.   start() {
  87.     this.http = http.createServer(this.requestListener.bind(this))
  88.       .listen(this.httpPort, this.hostname);
  89.     this.https = https.createServer(
  90.       this.httpsServerOptions,
  91.       this.requestListener.bind(this)
  92.     ).listen(this.httpsPort, this.hostname);
  93.   }
  94.  
  95.   // Private methods
  96.  
  97.   normalizeHeaderName(name) {
  98.     // content-type -> Content-Type
  99.     // Можно предварительно имя перевести в нижний регистр name.toLowerCase()
  100.     // для универсальности
  101.     return name.split('-')
  102.       .map(s => s.substr(0, 1).toUpperCase() + s.slice(1))
  103.       .join('-');;
  104.   }
  105.  
  106.   notModified(res) {
  107.     res.writeHead(304);
  108.     res.end();
  109.   }
  110.  
  111.   requestListener(req, res) {
  112.     console.log(
  113.       '%s %s %s %s',
  114.       req.socket.remoteAddress,
  115.       new Date().toISOString(),
  116.       req.method,
  117.       req.url
  118.     );
  119.     res.setHeader('Server', 'NodeReverse');
  120.     // Отправлен ли запрос через https?
  121.     req.secure = req.socket.localPort === this.httpsPort;
  122.     console.log('Request is secure', req.secure);
  123.     if (!FILE_METHODS.includes(req.method)) {
  124.       return this.defaultHandler(req, res);
  125.     }
  126.     // Я не знаю нужно ли убирать .. из пути или NodeJS сам это делает, но
  127.     // на всякий случай лучше лишний раз вызвать path.normalize
  128.     let filename = path.join(
  129.       this.staticRoot,
  130.       decodeURI(path.normalize(url.parse(req.url).pathname))
  131.     );
  132.     fs.exists(filename, exists => {
  133.       if (!exists) {
  134.         return this.defaultHandler(req, res);
  135.       }
  136.       fs.stat(filename, (err, stat) => {
  137.         if (err) {
  138.           res.writeHead(500);
  139.           return res.end(err.message);
  140.         }
  141.         if (stat.isFile()) {
  142.           return this.serveFile(filename, stat, req, res);
  143.         }
  144.         this.defaultHandler(req, res);
  145.       });
  146.     });
  147.   }
  148. }
  149.  
  150. module.exports = {Server};
Advertisement
Add Comment
Please, Sign In to add comment