加入收藏 | 设为首页 | 会员中心 | 我要投稿 核心网 (https://www.hxwgxz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 业界 > 正文

Node.js 原生 api 搭建 web 服务器

发布时间:2019-02-16 07:08:45 所属栏目:业界 来源:想不起来了
导读:node.js 实现一个简单的 web 服务器还是比较简单的,以前利用 express 框架实现过『nodeJS搭一个简单的(代理)web服务器』。代码量很少,可是使用时需要安装依赖,多处使用难免有点不方便。于是便有了完全使用原生 api 来重写的想法,也当作一次 node.js 复

服务器接收到 http 请求,首先处理代理列表 proxyTable,然后再处理静态资源。虽然这里面只有二个步骤,但如果按照先后顺序编码,这种方式显然不够灵活,,不利于以后功能的扩展。koa 框架的中间件就是一个很好的解决方案。完整代码如下:

  1. 'use strict' 
  2.  
  3. const http = require('http') 
  4. const url = require('url') 
  5. const fs = require('fs') 
  6. const path = require('path') 
  7. const cp = require('child_process') 
  8. // 处理静态资源 
  9. function processStatic(req, res) {   
  10.   const mime = { 
  11.     css: 'text/css', 
  12.     gif: 'image/gif', 
  13.     html: 'text/html', 
  14.     ico: 'image/x-icon', 
  15.     jpeg: 'image/jpeg', 
  16.     jpg: 'image/jpeg', 
  17.     js: 'text/javascript', 
  18.     json: 'application/json', 
  19.     pdf: 'application/pdf', 
  20.     png: 'image/png', 
  21.     svg: 'image/svg+xml', 
  22.     woff: 'application/x-font-woff', 
  23.     woff2: 'application/x-font-woff', 
  24.     swf: 'application/x-shockwave-flash', 
  25.     tiff: 'image/tiff', 
  26.     txt: 'text/plain', 
  27.     wav: 'audio/x-wav', 
  28.     wma: 'audio/x-ms-wma', 
  29.     wmv: 'video/x-ms-wmv', 
  30.     xml: 'text/xml' 
  31.   }   
  32.   const requestUrl = req.url   
  33.   let pathName = url.parse(requestUrl).pathname   
  34.   // 中文乱码处理 
  35.   pathName = decodeURI(pathName)   
  36.   let ext = path.extname(pathName)   
  37.   // 特殊 url 处理 
  38.   if (!pathName.endsWith('/') && ext === '' && !requestUrl.includes('?')) { 
  39.     pathName += '/' 
  40.     const redirect = `http://${req.headers.host}${pathName}` 
  41.     redirectUrl(redirect, res) 
  42.   }   
  43.   // 解释 url 对应的资源文件路径 
  44.   let filePath = path.resolve(__dirname + pathName)   
  45.   // 设置 mime  
  46.   ext = ext ? ext.slice(1) : 'unknown' 
  47.   const contentType = mime[ext] || 'text/plain' 
  48.  
  49.   // 处理资源文件 
  50.   fs.stat(filePath, (err, stats) => {     
  51.    if (err) { 
  52.       res.writeHead(404, { 'content-type': 'text/html;charset=utf-8' }) 
  53.       res.end('<h1>404 Not Found</h1>')       
  54.       return 
  55.     }    // 处理文件 
  56.     if (stats.isFile()) { 
  57.       readFile(filePath, contentType, res) 
  58.     }    // 处理目录 
  59.     if (stats.isDirectory()) {       
  60.       let html = "<head><meta charset = 'utf-8'/></head><body><ul>" 
  61.       // 遍历文件目录,以超链接返回,方便用户选择 
  62.       fs.readdir(filePath, (err, files) => {         
  63.         if (err) { 
  64.           res.writeHead(500, { 'content-type': contentType }) 
  65.           res.end('<h1>500 Server Error</h1>') 
  66.           return 
  67.         } else {          
  68.            for (let file of files) {             
  69.             if (file === 'index.html') {              
  70.               const redirect = `http://${req.headers.host}${pathName}index.html` 
  71.               redirectUrl(redirect, res) 
  72.             } 
  73.             html += `<li><a href='${file}'>${file}</a></li>` 
  74.           } 
  75.           html += '</ul></body>' 
  76.           res.writeHead(200, { 'content-type': 'text/html' }) 
  77.           res.end(html) 
  78.         } 
  79.       }) 
  80.     } 
  81.   }) 
  82. // 重定向处理 
  83. function redirectUrl(url, res) { 
  84.   url = encodeURI(url) 
  85.   res.writeHead(302, { 
  86.     location: url 
  87.   }) 
  88.   res.end() 
  89. // 文件读取 
  90. function readFile(filePath, contentType, res) { 
  91.   res.writeHead(200, { 'content-type': contentType }) 
  92.   const stream = fs.createReadStream(filePath) 
  93.   stream.on('error', function() { 
  94.     res.writeHead(500, { 'content-type': contentType }) 
  95.     res.end('<h1>500 Server Error</h1>') 
  96.   }) 
  97.   stream.pipe(res) 
  98. // 处理代理列表 
  99. function processProxy(req, res) { 
  100.   const requestUrl = req.url 
  101.   const proxy = Object.keys(proxyTable) 
  102.   let not_found = true 
  103.   for (let index = 0; index < proxy.length; index++) {     
  104.     const k = proxy[index]     
  105.     const i = requestUrl.indexOf(k)     
  106.     if (i >= 0) { 
  107.       not_found = false 
  108.       const element = proxyTable[k] 
  109.       const newUrl = element.target + requestUrl.slice(i + k.length) 
  110.  
  111.       if (requestUrl !== newUrl) { 
  112.         const u = url.parse(newUrl, true) 
  113.         const options = { 
  114.           hostname : u.hostname,  
  115.           port     : u.port || 80, 
  116.           path     : u.path,        
  117.           method   : req.method, 
  118.           headers  : req.headers, 
  119.           timeout  : 6000 
  120.         }; 
  121.         if(element.changeOrigin){ 
  122.           options.headers['host'] = u.hostname + ':' + ( u.port || 80) 
  123.         } 
  124.         const request =  
  125.           http.request(options, response => {                 
  126.             // cookie 处理 
  127.             if(element.changeOrigin && response.headers['set-cookie']){ 
  128.               response.headers['set-cookie'] = getHeaderOverride(response.headers['set-cookie']) 
  129.             } 
  130.             res.writeHead(response.statusCode, response.headers) 
  131.             response.pipe(res) 
  132.           }) 
  133.           .on('error', err => { 
  134.             res.statusCode = 503 
  135.             res.end() 
  136.           }) 
  137.         req.pipe(request) 
  138.       } 
  139.       break 
  140.     } 
  141.   } 
  142.   return not_found 
  143. function getHeaderOverride(value){ 
  144.   if (Array.isArray(value)) { 
  145.       for (var i = 0; i < value.length; i++ ) {         
  146.          value[i] = replaceDomain(value[i]) 
  147.       } 
  148.   } else { 
  149.       value = replaceDomain(value) 
  150.   } 
  151.   return value} 
  152. function replaceDomain(value) { 
  153.   return value.replace(/domain=[a-z.]*;/,'domain=.localhost;').replace(/secure/, '') 
  154. function compose (middleware) { 
  155.   if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!')   
  156.   for (const fn of middleware) {     
  157.     if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!') 
  158.   }   
  159.   return function (context, next) { 
  160.     // 记录上一次执行中间件的位置     
  161.     let index = -1 
  162.     return dispatch(0)    
  163.     function dispatch (i) { 
  164.       // 理论上 i 会大于 index,因为每次执行一次都会把 i递增, 
  165.       // 如果相等或者小于,则说明next()执行了多次      
  166.       if (i <= index) return Promise.reject(new Error('next() called multiple times'))       
  167.       index = i 
  168.       let fn = middleware[i]       
  169.       if (i === middleware.length) fn = next 
  170.       if (!fn) return Promise.resolve()      
  171.       try {        
  172.         return Promise.resolve(fn(context, function next () {   
  173.            return dispatch(i + 1) 
  174.         })) 
  175.       } catch (err) {         
  176.          return Promise.reject(err) 
  177.       } 
  178.     } 
  179.   } 
  180. function Router(){   
  181.   this.middleware = [] 
  182. Router.prototype.use = function (fn){   
  183.   if (typeof fn !== 'function') throw new TypeError('middleware must be a function!') 
  184.   this.middleware.push(fn)  
  185.   return this} 
  186. Router.prototype.callback= function() {   
  187.   const fn = compose(this.middleware)   
  188.   const handleRequest = (req, res) => { 
  189.     const ctx = {req, res} 
  190.     return this.handleRequest(ctx, fn) 
  191.   } 
  192.   return handleRequest 
  193. Router.prototype.handleRequest= function(ctx, fn) { 
  194.   fn(ctx) 
  195.  
  196. // 代理列表 
  197. const proxyTable = { 
  198.   '/api': { 
  199.     target: 'http://127.0.0.1:8090/api', 
  200.     changeOrigin: true 
  201.   } 
  202.  
  203. const port = 8080 
  204. const hostname = 'localhost' 
  205. const appRouter = new Router() 
  206.  
  207. // 使用中间件 
  208. appRouter.use(async(ctx,next)=>{ 
  209.   if(processProxy(ctx.req, ctx.res)){ 
  210.     next() 
  211.   } 
  212. }).use(async(ctx)=>{ 
  213.   processStatic(ctx.req, ctx.res) 
  214. }) 
  215.  
  216. // 创建 http 服务 
  217. let httpServer = http.createServer(appRouter.callback()) 
  218.  
  219. // 设置监听端口 
  220. httpServer.listen(port, hostname, () => { 
  221.   console.log(`app is running at port:${port}`) 
  222.   console.log(`url: http://${hostname}:${port}`) 
  223.   cp.exec(`explorer http://${hostname}:${port}`, () => {}) 
  224. }) 

(编辑:核心网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读