nodejs的网络请求解析

Posted Mr song song

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了nodejs的网络请求解析相关的知识,希望对你有一定的参考价值。

一、context.js

  在context.js中,会处理错误,cookie,JSON格式化等。

1)cookie

  在处理cookie时,创建了一个Symbol类型的key,注意Symbol具有唯一性的特点,即 Symbol('context#cookies') === Symbol('context#cookies') 得到的是 false。

const Cookies = require('cookies')
const COOKIES = Symbol('context#cookies')

const proto = module.exports = 
  get cookies () 
    if (!this[COOKIES]) 
      this[COOKIES] = new Cookies(this.req, this.res, 
        keys: this.app.keys,
        secure: this.request.secure
      )
    
    return this[COOKIES]
  ,
  set cookies (_cookies) 
    this[COOKIES] = _cookies
  

  get在读取cookie,会初始化Cookies实例。

2)错误

  在默认的错误处理函数中,会配置头信息,触发错误事件,配置响应码等。

  onerror (err) 
    // 可以绕过KOA的错误处理
    if (err == null) return

    // 在处理跨全局变量时,正常的“instanceof”检查无法正常工作
    // See https://github.com/koajs/koa/issues/1466
    // 一旦jest修复,可能会删除它 https://github.com/facebook/jest/issues/2549.
    const isNativeError =
      Object.prototype.toString.call(err) === '[object Error]' ||
      err instanceof Error
    if (!isNativeError) err = new Error(util.format('non-error thrown: %j', err))

    let headerSent = false
    if (this.headerSent || !this.writable) 
      headerSent = err.headerSent = true
    

    // 触发error事件,在application.js中创建过监听器
    this.app.emit('error', err, this)

    // nothing we can do here other
    // than delegate to the app-level
    // handler and log.
    if (headerSent) 
      return
    

    const  res  = this

    // 首先取消设置所有header
    /* istanbul ignore else */
    if (typeof res.getHeaderNames === 'function') 
      res.getHeaderNames().forEach(name => res.removeHeader(name))
     else 
      res._headers =  // Node < 7.7
    

    // 然后设置那些指定的
    this.set(err.headers)

    // 强制 text/plain
    this.type = 'text'

    let statusCode = err.status || err.statusCode

    // ENOENT support
    if (err.code === 'ENOENT') statusCode = 404

    // default to 500
    if (typeof statusCode !== 'number' || !statuses[statusCode]) statusCode = 500

    // 响应数据
    const code = statuses[statusCode]
    const msg = err.expose ? err.message : code
    this.status = err.status = statusCode
    this.length = Buffer.byteLength(msg)
    res.end(msg)
  ,

3)属性委托

  在package.json中依赖了一个名为delegates的库,看下面这个示例。

  request是context的一个属性,在调delegate(context, 'request')函数后,就能直接context.querystring这么调用了。

const delegate = require('delegates')
const context = 
  request: 
    querystring: 'a=1&b=2'
  

delegate(context, 'request')
  .access('querystring')
console.log(context.querystring)// a=1&b=2

  在KOA中,ctx可以直接调用request与response的属性和方法就是通过delegates实现的。

delegate(proto, 'request')
  .method('acceptsLanguages')
  .method('acceptsEncodings')
  .method('acceptsCharsets')
  .method('accepts')
  .method('get')
  .method('is')
  .access('querystring')
  .access('idempotent')
  .access('socket')
  .access('search')
  .access('method')
  ...

二、request.js和response.js

  request.js和response.js就是为Node原生的req和res做一层封装。在request.js中都是些HTTP首部、IP、URL、缓存等。

/**
   * 获取 WHATWG 解析的 URL,并缓存起来
   */
  get URL () 
    /* istanbul ignore else */
    if (!this.memoizedURL) 
      const originalUrl = this.originalUrl || '' // avoid undefined in template string
      try 
        this.memoizedURL = new URL(`$this.origin$originalUrl`)
       catch (err) 
        this.memoizedURL = Object.create(null)
      
    
    return this.memoizedURL
  ,
  /**
   * 检查请求是否新鲜(有缓存),也就是
   * If-Modified-Since/Last-Modified 和 If-None-Match/ETag 是否仍然匹配
   */
  get fresh () 
    const method = this.method
    const s = this.ctx.status

    // GET or HEAD for weak freshness validation only
    if (method !== 'GET' && method !== 'HEAD') return false

    // 2xx or 304 as per rfc2616 14.26
    if ((s >= 200 && s < 300) || s === 304) 
      return fresh(this.header, this.response.header)
    

    return false
  ,
  response.js要复杂一点,会配置状态码、响应正文、读取解析的响应内容长度、302重定向等。

  /**
   * 302 重定向
   * Examples:
   *    this.redirect('back');
   *    this.redirect('back', '/index.html');
   *    this.redirect('/login');
   *    this.redirect('http://google.com');
   */
  redirect (url, alt) 
    // location
    if (url === 'back') url = this.ctx.get('Referrer') || alt || '/'
    this.set('Location', encodeUrl(url))

    // status
    if (!statuses.redirect[this.status]) this.status = 302

    // html
    if (this.ctx.accepts('html')) 
      url = escape(url)
      this.type = 'text/html; charset=utf-8'
      this.body = `Redirecting to <a href="$url">$url</a>.`
      return
    

    // text
    this.type = 'text/plain; charset=utf-8'
    this.body = `Redirecting to $url.`
  ,
  /**
   * 设置响应正文
   */
  set body (val) 
    const original = this._body
    this._body = val

    // no content
    if (val == null) 
      if (!statuses.empty[this.status]) 
        if (this.type === 'application/json') 
          this._body = 'null'
          return
        
        this.status = 204
      
      if (val === null) this._explicitNullBody = true
      this.remove('Content-Type')
      this.remove('Content-Length')
      this.remove('Transfer-Encoding')
      return
    

    // set the status
    if (!this._explicitStatus) this.status = 200

    // set the content-type only if not yet set
    const setType = !this.has('Content-Type')

    // string
    if (typeof val === 'string') 
      if (setType) this.type = /^\\s*</.test(val) ? 'html' : 'text'
      this.length = Buffer.byteLength(val)
      return
    

    // buffer
    if (Buffer.isBuffer(val)) 
      if (setType) this.type = 'bin'
      this.length = val.length
      return
    

    // stream
    if (val instanceof Stream) 
      onFinish(this.res, destroy.bind(null, val))
      if (original !== val) 
        val.once('error', err => this.ctx.onerror(err))
        // overwriting
        if (original != null) this.remove('Content-Length')
      

      if (setType) this.type = 'bin'
      return
    

    // json
    this.remove('Content-Length')
    this.type = 'json'
  ,

三、单元测试

  KOA的单元测试做的很细致,每个方法和属性都给出了相应的单测,它内部的写法很容易单测,非常值得借鉴。

  采用的单元测试框架是Jest,HTTP请求的测试库是SuperTest。断言使用了Node默认提供的assert模块。

const request = require('supertest')
const assert = require('assert')
const Koa = require('../..')
// request.js
describe('app.request', () => 
  const app1 = new Koa()
  // 声明request的message属性
  app1.request.message = 'hello'

  it('should merge properties', () => 
    app1.use((ctx, next) => 
      // 判断ctx中的request.message是否就是刚刚赋的那个值
      assert.strictEqual(ctx.request.message, 'hello')
      ctx.status = 204
    )
    // 发起GET请求,地址是首页,期待响应码是204
    return request(app1.listen())
      .get('/')
      .expect(204)
  )
)

转自:Node.js躬行记(20)——KOA源码分析(下) - 咖啡机(K.F.J) - 博客园

以上是关于nodejs的网络请求解析的主要内容,如果未能解决你的问题,请参考以下文章

HTTP权威指南----缓存

计算机网络:从输入网址到网页显示发生了什么?

使用 Nodejs 在未来的准确时间发送 HTTP 请求

HTTP协议入门

(建议收藏)计算机网络:TCP-首部格式,序号确认和连接管理习题解析与拓展

Python入门网络请求与解析