From c180f08501a95634c53c91ef7a28cbfa956574da Mon Sep 17 00:00:00 2001 From: Blake Embrey Date: Fri, 8 Jul 2016 16:57:17 -0700 Subject: [PATCH] Make the response object configurable --- lib/base.ts | 22 ++++++++++++++++------ lib/browser.ts | 8 ++++---- lib/common.ts | 6 +++--- lib/error.ts | 4 ++-- lib/index.ts | 14 +++++++------- lib/plugins/common.ts | 6 +++--- lib/plugins/index.ts | 6 +++--- lib/request.ts | 33 +++++++++++++++------------------ 8 files changed, 53 insertions(+), 46 deletions(-) diff --git a/lib/base.ts b/lib/base.ts index 3c99d4c..0332598 100644 --- a/lib/base.ts +++ b/lib/base.ts @@ -130,7 +130,7 @@ export default class Base { return headers } - set (name: string, value?: string | string[]): Base { + set (name: string, value?: string | string[]): this { this.remove(name) this.append(name, value) @@ -168,15 +168,25 @@ export default class Base { get (name: string): string { const lowered = lowerHeader(name) - let value: string for (let i = 0; i < this.rawHeaders.length; i += 2) { if (lowerHeader(this.rawHeaders[i]) === lowered) { - value = value == null ? this.rawHeaders[i + 1] : `${value}, ${this.rawHeaders[i + 1]}` + return this.rawHeaders[i + 1] + } + } + } + + getAll (name: string): string[] { + const lowered = lowerHeader(name) + const result: string[] = [] + + for (let i = 0; i < this.rawHeaders.length; i += 2) { + if (lowerHeader(this.rawHeaders[i]) === lowered) { + result.push(this.rawHeaders[i + 1]) } } - return value + return result } remove (name: string) { @@ -193,8 +203,8 @@ export default class Base { } type (): string - type (value: string): Base - type (value?: string): any { + type (value: string): this + type (value?: string): string | this { if (arguments.length === 0) { return type(this.get('Content-Type')) } diff --git a/lib/browser.ts b/lib/browser.ts index 6a0a3ea..c80f746 100644 --- a/lib/browser.ts +++ b/lib/browser.ts @@ -9,7 +9,7 @@ import { defaults as use } from './plugins/index' */ export { open, abort, use } -function open (request: Request) { +function open (request: Request) { return new Promise(function (resolve, reject) { const { url, method } = request const responseType = request.options.responseType @@ -22,13 +22,13 @@ function open (request: Request) { const xhr = request._raw = new XMLHttpRequest() function done () { - return resolve({ + return resolve(new Response({ status: xhr.status === 1223 ? 204 : xhr.status, statusText: xhr.statusText, rawHeaders: parseToRawHeaders(xhr.getAllResponseHeaders()), body: responseType ? xhr.response : xhr.responseText, url: xhr.responseURL - }) + })) } xhr.onload = done @@ -94,7 +94,7 @@ function open (request: Request) { /** * Close the current HTTP request. */ -function abort (request: Request) { +function abort (request: Request) { request._raw.abort() } diff --git a/lib/common.ts b/lib/common.ts index 19301cb..29e8bb1 100644 --- a/lib/common.ts +++ b/lib/common.ts @@ -10,11 +10,11 @@ import * as transport from './index' /** * Generate a default popsicle instance. */ -export function defaults (defaultsOptions: DefaultsOptions) { +export function defaults (defaultsOptions: DefaultsOptions) { const defaults = extend({ transport }, defaultsOptions) - return function popsicle (options: RequestOptions | string): Request { - let opts: RequestOptions + return function popsicle (options: RequestOptions | string): Request { + let opts: RequestOptions if (typeof options === 'string') { opts = extend(defaults, { url: options }) diff --git a/lib/error.ts b/lib/error.ts index e9898ac..cef7b9d 100644 --- a/lib/error.ts +++ b/lib/error.ts @@ -4,10 +4,10 @@ import Request from './request' export default class PopsicleError extends makeErrorCause.BaseError { code: string - popsicle: Request + popsicle: Request name = 'PopsicleError' - constructor (message: string, code: string, original: Error, popsicle: Request) { + constructor (message: string, code: string, original: Error, popsicle: Request) { super(message, original) this.code = code diff --git a/lib/index.ts b/lib/index.ts index 53bb7f6..76c5276 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -1,5 +1,5 @@ import { request as httpRequest, IncomingMessage } from 'http' -import { request as httpsRequest } from 'https' +import { request as httpsRequest, RequestOptions } from 'https' import { PassThrough } from 'stream' import urlLib = require('url') import extend = require('xtend') @@ -40,7 +40,7 @@ const REDIRECT_STATUS: { [status: number]: number } = { /** * Open a HTTP request with node. */ -function open (request: Request) { +function open (request: Request) { const { url, method, body, options } = request const maxRedirects = num(options.maxRedirects, 5) const followRedirects = options.followRedirects !== false @@ -136,14 +136,14 @@ function open (request: Request) { rawResponse.pipe(responseStream) - return Promise.resolve({ + return Promise.resolve(new Response({ body: responseStream, status: status, statusText: rawResponse.statusMessage, headers: rawResponse.headers, rawHeaders: rawResponse.rawHeaders, url: url - }) + })) } // Emit a request error. @@ -187,7 +187,7 @@ function open (request: Request) { /** * Close the current HTTP request. */ -function abort (request: Request) { +function abort (request: Request) { request._raw.abort() } @@ -212,7 +212,7 @@ function falsey () { /** * Read cookies from the cookie jar. */ -function getAttachCookies (request: Request): (url: string) => Promise { +function getAttachCookies (request: Request): (url: string) => Promise { const { jar } = request.options const cookie = request.get('Cookie') @@ -242,7 +242,7 @@ function getAttachCookies (request: Request): (url: string) => Promise { /** * Put cookies in the cookie jar. */ -function getStoreCookies (request: Request): (url: string, message: IncomingMessage) => Promise { +function getStoreCookies (request: Request): (url: string, message: IncomingMessage) => Promise { const { jar } = request.options if (!jar) { diff --git a/lib/plugins/common.ts b/lib/plugins/common.ts index c97e157..93d9558 100644 --- a/lib/plugins/common.ts +++ b/lib/plugins/common.ts @@ -20,7 +20,7 @@ export function wrap (value: T): () => T { /** * Remove default headers. */ -export const headers = wrap(function (request: Request, next: () => Promise) { +export const headers = wrap(function (request: Request, next: () => Promise) { // If we have no accept header set already, default to accepting // everything. This is needed because otherwise Firefox defaults to // an accept header of `html/xml`. @@ -37,7 +37,7 @@ export const headers = wrap(function (request: Request, next: () => Promise Promise) { +export const stringify = wrap(function (request: Request, next: () => Promise) { const { body } = request // Convert primitives types into strings. @@ -85,7 +85,7 @@ export const stringify = wrap(function (request: Request, next: () => Promise Promise) { +export const parse = wrap(function (request: Request, next: () => Promise) { return next() .then(function (response) { const { body } = response diff --git a/lib/plugins/index.ts b/lib/plugins/index.ts index ea4bf14..7cd050b 100644 --- a/lib/plugins/index.ts +++ b/lib/plugins/index.ts @@ -10,7 +10,7 @@ import Response from '../response' /** * Support gzipped responses. */ -export const unzip = wrap(function (request: Request, next: () => Promise) { +export const unzip = wrap(function (request: Request, next: () => Promise) { if (!request.get('Accept-Encoding')) { request.set('Accept-Encoding', 'gzip,deflate') } @@ -34,7 +34,7 @@ export const unzip = wrap(function (request: Request, next: () => Promise Promise) { + return function (request: Request, next: () => Promise) { return next() .then(function (response) { return new Promise(function (resolve, reject) { @@ -57,7 +57,7 @@ export function concatStream (encoding: string) { export function headers () { const common = commonHeaders() - return function (request: Request, next: () => Promise) { + return function (request: Request, next: () => Promise) { // Set up common headers. return common(request, function () { // Specify a default user agent in node. diff --git a/lib/request.ts b/lib/request.ts index 55049e6..e635cbc 100644 --- a/lib/request.ts +++ b/lib/request.ts @@ -6,7 +6,7 @@ import Base, { BaseOptions, Headers } from './base' import Response, { ResponseOptions } from './response' import PopsicleError from './error' -export interface DefaultsOptions extends BaseOptions { +export interface DefaultsOptions extends BaseOptions { url?: string method?: string timeout?: number @@ -14,10 +14,10 @@ export interface DefaultsOptions extends BaseOptions { options?: any use?: Middleware[] progress?: ProgressFunction[] - transport?: TransportOptions + transport?: TransportOptions } -export interface RequestOptions extends DefaultsOptions { +export interface RequestOptions extends DefaultsOptions { url: string } @@ -30,24 +30,21 @@ export interface RequestJSON { method: string } -export interface TransportOptions { - open: OpenHandler - abort?: AbortHandler +export interface TransportOptions { + open: (request: Request) => Promise + abort?: (request: Request) => any use?: Middleware[] } -export type Middleware = (request: Request, next: () => Promise) => Response | Promise -export type ProgressFunction = (request: Request) => any +export type Middleware = (request: Request, next: () => Promise) => Response | Promise +export type ProgressFunction = (request: Request) => any -export type OpenHandler = (request: Request) => Promise -export type AbortHandler = (request: Request) => any - -export default class Request extends Base implements Promise { +export default class Request extends Base implements Promise { method: string timeout: number body: any options: any - transport: TransportOptions + transport: TransportOptions middleware: Middleware[] = [] opened = false @@ -64,7 +61,7 @@ export default class Request extends Base implements Promise { private _resolve: (response: Response) => void private _reject: (error: Error) => void - constructor (options: RequestOptions) { + constructor (options: RequestOptions) { super(options) this.timeout = (options.timeout | 0) @@ -104,7 +101,7 @@ export default class Request extends Base implements Promise { return new PopsicleError(message, code, original, this) } - then (onFulfilled: (response?: Response) => any, onRejected?: (error?: PopsicleError) => any) { + then (onFulfilled: (response?: T) => any, onRejected?: (error?: PopsicleError) => any) { return this._promise.then(onFulfilled, onRejected) } @@ -112,13 +109,13 @@ export default class Request extends Base implements Promise { return this._promise.then(null, onRejected) } - exec (cb: (err: PopsicleError, response?: Response) => any) { + exec (cb: (err: PopsicleError, response?: T) => any) { this.then(function (response) { cb(null, response) }, cb) } - toOptions (): RequestOptions { + toOptions (): RequestOptions { return { url: this.url, method: this.method, @@ -229,7 +226,7 @@ export default class Request extends Base implements Promise { // Proxy the transport promise into the current request. return this.transport.open(this) .then( - res => this._resolve(new Response(res)), + res => this._resolve(res), err => this._reject(err) ) }