antongolub/reqresnext

View on GitHub
src/main/js/request.js

Summary

Maintainability
A
1 hr
Test Coverage
A
100%
// @flow

import type {
  IAny,
  IResponse,
  IRequest,
  IRawOptions,
  IApp,
  IConnection,
  IData,
  IQuery,
  IParamsMap,
  IHeadersMap,
  IHeaderSetter,
  IUrl,
  ICookieSetter,
  ISocket
} from './interface'

import express from 'express'
import { noop } from 'lodash'
import url from 'url'
import {
  assign,
  setprototypeof,
  appendAdditionalProps,
  convertKeysToLowerCase
} from './util'
import DEFAULT_APP from './app'

// $FlowFixMe
const { request, response } = express

/**
 * Request implementation.
 */
export default class Request implements IRequest {
  $key: string

  $value: IAny

  cookie: ICookieSetter

  header: IHeaderSetter

  app: IApp | Object

  res: IResponse | Object

  connection: IConnection

  body: ?IData

  query: ?IQuery

  params: ?IParamsMap

  headers: IHeadersMap

  _flush: Function

  _readableState: IAny

  unpipe: Function

  socket: ISocket

  constructor (input: ?IRawOptions) {
    setprototypeof(this, request)
    const opts = new ReqOptions(input || {})

    this._flush = noop
    this._readableState = {}
    this.unpipe = noop
    this.socket = opts.socket
    this.app = opts.app
    this.res = opts.res
    this.headers = opts.headers ? convertKeysToLowerCase(opts.headers) : {}
    this.body = opts.body
    this.params = opts.params
    this.connection = opts.connection

    // Passes additional props
    appendAdditionalProps(this, opts.raw)
  }
}

/**
 */
export class ReqOptions {
  raw: IRawOptions

  headers: IHeadersMap

  app: IApp | Object

  res: IResponse | Object

  connection: IConnection

  body: ?IData

  query: ?IQuery

  params: ?IParamsMap

  socket: ISocket

  constructor (input: IRawOptions) {
    this.res = input.res || {}
    this.app = input.app || DEFAULT_APP

    const headers = {}
    const urlStr: string = input.url || '/'
    const urlOpts = input.host
      ? input
      : url.parse(urlStr) // eslint-disable-line

    // NOTE host & protocol are required by URL constructor
    urlOpts.host = urlOpts.host || 'localhost'
    urlOpts.protocol = urlOpts.protocol || 'http'

    const urlData: IUrl = new url.URL(url.format(urlOpts))
    const connection = assign(
      { encrypted: urlData.protocol === 'https:' },
      input.connection || {}
    )
    const context = {
      set (k: string, v: string): void { headers[k] = v }
    }
    response.header.call(context, input.headers)
    headers.host = headers.host || urlData.host

    this.headers = headers
    // $FlowFixMe
    this.query = urlData.query
    this.body = input.body
    this.params = input.params || {}
    this.connection = connection
    this.raw = input
    this.socket = input.socket || {}
  }
}