From 2fdb1815e5e8403f16176d7c15bc0c62348dd5a5 Mon Sep 17 00:00:00 2001 From: Blake Embrey Date: Fri, 17 Mar 2017 13:23:00 -0700 Subject: [PATCH] Add support for sending trailers (#2) --- package.json | 3 +-- src/index.spec.ts | 61 ++++++++++++++++++++++++++++++++++++++--------- src/index.ts | 28 +++++++++++++++++++--- typings.json | 3 --- 4 files changed, 76 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index 6c66613..6ddb1fb 100644 --- a/package.json +++ b/package.json @@ -52,11 +52,10 @@ "express": "^4.15.2", "jest": "^19.0.0", "parseurl": "^1.3.1", - "popsicle": "^9.1.0", - "popsicle-server": "^2.0.0", "rimraf": "^2.5.4", "servie": "^0.2.0", "servie-route": "^0.3.1", + "throwback": "^2.0.0", "ts-jest": "^19.0.0", "tslint": "^4.3.1", "tslint-config-standard": "^4.0.0", diff --git a/src/index.spec.ts b/src/index.spec.ts index df4e03f..a988a4d 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -1,19 +1,58 @@ import { Response } from 'servie' import { get } from 'servie-route' -import { request } from 'popsicle' -import popsicleServer = require('popsicle-server') +import { compose } from 'throwback' +import { createReadStream } from 'fs' +import * as http from 'http' +import { join } from 'path' import { createHandler } from './index' describe('servie-http', () => { - const handler = createHandler(get('/', () => { - return new Response({ body: 'hello world' }) - })) - - it('should work over http', () => { - return request('/') - .use(popsicleServer(handler)) - .then((res) => { - expect(res.body).toBe('hello world') + const handler = createHandler(compose([ + get('/', () => { + return new Response({ body: 'hello world' }) + }), + get('/stream', () => { + return new Response({ + body: createReadStream(join(__dirname, 'index.ts')), + headers: { + 'Trailer': 'Server-Timing' + }, + trailers: { + 'Server-Timing': 'end=100' + } }) + }) + ])) + + const server = http.createServer(handler).listen(0) + + afterAll(() => server.close()) + + it('should work over http', (done) => { + return http.get(`http://localhost:${server.address().port}`, (res) => { + let data = '' + + res.on('data', (chunk: Buffer) => data += chunk.toString('utf8')) + + res.on('end', () => { + expect(res.headers['content-type']).toEqual('text/plain') + expect(res.headers['content-length']).toEqual('11') + expect(data).toBe('hello world') + + return done() + }) + }) + }) + + it('should send trailers', (done) => { + return http.get(`http://localhost:${server.address().port}/stream`, (res) => { + res.resume() + + res.on('end', () => { + expect(res.trailers).toEqual({ 'server-timing': 'end=100' }) + + return done() + }) + }) }) }) diff --git a/src/index.ts b/src/index.ts index e4d664f..8dfc8f6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ -import { Request, Response } from 'servie' +import { Request, Response, Headers } from 'servie' import { IncomingMessage, ServerResponse } from 'http' import { TLSSocket } from 'tls' import { finalhandler } from 'servie-finalhandler' @@ -11,6 +11,9 @@ export interface Options { logError?: (err: any) => void } +/** + * Create a node.js-compatible http handler. + */ export function createHandler (app: App, options: Options = {}) { return function (request: IncomingMessage, response: ServerResponse): void { let responded = false @@ -55,14 +58,20 @@ export function createHandler (app: App, options: Options = {}) { } if (res.bodyBuffered) { + response.addTrailers(toTrailers(res.trailers)) response.end(res.body) } else { - res.stream().pipe(response).on('error', (err: Error) => sendError(err)) + const stream = res.stream() + + stream.on('error', sendError) + stream.on('end', () => response.addTrailers(toTrailers(res.trailers))) + + stream.pipe(response) } } // Handle request and response errors. - req.events.on('error', (err: Error) => sendError(err)) + req.events.on('error', sendError) req.events.on('abort', () => sendResponse(new Response({ status: 444 }))) req.started = true @@ -74,3 +83,16 @@ export function createHandler (app: App, options: Options = {}) { ) } } + +/** + * Convert the trailers object into a list of trailers for node.js. + */ +function toTrailers (trailers: Headers): any { + const result: [string, string][] = new Array(trailers.raw.length / 2) + + for (let i = 0; i < trailers.raw.length; i += 2) { + result[i / 2] = [trailers.raw[i], trailers.raw[i + 1]] + } + + return result +} diff --git a/typings.json b/typings.json index b18d762..3e44a99 100644 --- a/typings.json +++ b/typings.json @@ -2,8 +2,5 @@ "globalDevDependencies": { "jest": "registry:dt/jest#16.0.0+20170112172802", "node": "registry:env/node#6.0.0+20170213133316" - }, - "devDependencies": { - "popsicle": "npm:popsicle" } }