├── README.md ├── index.js ├── package.json └── tests ├── test-compress.js ├── test-error.js ├── test-files.js ├── test-html.js ├── test-json.js ├── test-location.js ├── test-viewmapping-status.js └── test-views.js /README.md: -------------------------------------------------------------------------------- 1 | ## Quick Example 2 | 3 | response.json({result:'error',missing_keys:['email']}).status(400).pipe(res) 4 | 5 | // headers are { 'content-type': 'application/json', 6 | // date: 'Mon, 12 May 2014 12:57:31 GMT', 7 | // connection: 'keep-alive', 8 | // 'transfer-encoding': 'chunked' } 9 | // statusCode is 400 10 | // body is { result: 'error', missing_keys: [ 'email' ] } 11 | 12 | 13 | ## Response 14 | 15 | The basic idea is to build [request](https://github.com/mikeal/request) for HTTP Responses. 16 | 17 | This whole package is still beta. 18 | 19 | ### files 20 | 21 | ```javascript 22 | var server = http.createServer(function (req, res) { 23 | var f = fs.createReadStream('file.js') 24 | if (req.url === '/test.js') return f.pipe(response()).pipe(res) 25 | }) 26 | ``` 27 | 28 | When pipeing files to `response` it will lookup the mime type and set the propert content-type header for whatever file extension you send it. 29 | 30 | ### html, json, txt 31 | 32 | ```javascript 33 | var server = http.createServer(function (req, res) { 34 | if (req.url === '/') return response.html('Hello World').pipe(res) 35 | if (req.url === '/sitemap.html') { 36 | var f = fs.createReadStream('sitemap') 37 | return f.pipe(response.html()).pipe(res) 38 | } 39 | if (req.url === '/something.json') return response.json({test:1}).pipe(res) 40 | if (req.url === '/something.txt') return response.txt('some test').pipe(res) 41 | }) 42 | ``` 43 | 44 | ### .error(err[, statusCode]) 45 | 46 | ```javascript 47 | r.error(new Error('Uh Oh!')).pipe(res) 48 | ``` 49 | 50 | ```javascript 51 | r.error(555).pipe(res) 52 | ``` 53 | 54 | ```javascript 55 | r.error(new Error('Uh Oh!'), 501).pipe(res) 56 | ``` 57 | 58 | In addition, errors emitted on the stream piped to `response` will be passed through the same API and are accesssible in `views`. 59 | 60 | ## gzip and deflate compression 61 | 62 | The `compress` and `gzip` keys in an options object are used for compression. 63 | 64 | ```javascript 65 | var server = http.createServer(function (req, res) { 66 | var f = fs.createReadStream('file.js') 67 | if (req.url === '/file.js') return f.pipe(response({compress:req})).pipe(res) 68 | }) 69 | ``` 70 | 71 | You can pass an HTTP Request object and the best compression, if any, will be chosen for you. Alternatively you can pass `"gzip"` or `"deflate"` to forcce compression of the response stream. 72 | 73 | This compression option is compatible with every other feature in `response` and will work whether you do file streaming, html, json, or even using views. When passing a view, string or buffer to `response` the second argument is used as the options object. 74 | 75 | ```javascript 76 | var server = http.createServer(function (req, res) { 77 | if (req.url === '/') return response.html('Nope', {compress:req}).pipe(res) 78 | }) 79 | ``` 80 | 81 | ## status codes and headers 82 | 83 | `response` also has an extended version of node core's HTTP Response API. 84 | 85 | All headers setting and checking is done caseless while preserving the original casing when first set. This way you never accidentally send two of the same header but can still support broken clients that check for specific caseing. 86 | 87 | #### .statusCode 88 | 89 | Set the statusCode property to send the HTTP status code. This is a non-destructive way to send the status code. 90 | 91 | ```javascript 92 | var r = response() 93 | r.statusCode = 500 94 | r.html('Error') 95 | ``` 96 | 97 | #### .setHeader(key, value[, clobber=true]) 98 | 99 | Defaults to clobbering (overwritting) existing values but when disabled will concatenate values. 100 | 101 | ```javascript 102 | r.setHeader('X-Blah', 'somehost.com') 103 | ``` 104 | 105 | #### .setHeader(headers) 106 | 107 | Set multiple headers by passing an object. 108 | 109 | ```javascript 110 | r.setHeader({'x-blah': 'somehost', 'x-blah2': 'anotherhost.com'}) 111 | ``` 112 | 113 | #### .getHeader(key) 114 | 115 | You can retreive a header by its key, use this method instead of directly accessing the headers object to avoid caseing constraints. 116 | 117 | ```javascript 118 | r.getHeader('content-type') 119 | ``` 120 | 121 | #### .hasHeader(key) 122 | 123 | Check if a header is already set. If one is set the header key will be returned (which is important because it may have different caseing). 124 | 125 | ```javascript 126 | r.hasHeader('content-type') 127 | ``` 128 | 129 | ### views (very experimental) 130 | 131 | ```javascript 132 | function view (e, data, cb) { 133 | if (e) return cb(e) 134 | cb(null, '' + data + '') 135 | } 136 | 137 | var server = http.createServer(function (req, res) { 138 | var r = response(view) 139 | r.pipe(res) 140 | if (req.url === '/test1') return r.html('test') 141 | }) 142 | ``` 143 | 144 | This is how you would easily support something like a template system. TODO: example. 145 | 146 | ### Credits 147 | 148 | Mad props to @marak who handed over the "response" package in npm that he registered way back in the day. 149 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var stream = require('stream') 2 | , mimeTypes = require('mime-types') 3 | , util = require('util') 4 | , bl = require('bl') 5 | , caseless = require('caseless') 6 | , bestencoding = require('best-encoding') 7 | , zlib = require('zlib') 8 | ; 9 | 10 | // This could eventually be its own module. 11 | function mutations (src, dest) { 12 | if (src.headers && dest.setHeader) { 13 | for (var i in src.headers) dest.setHeader(i, src.headers[i]) 14 | } 15 | if (src.path && 16 | (!src.headers || (src.getHeader ? !src.getHeader('content-type') : !src.headers['content-type'])) && 17 | dest.setHeader) { 18 | src.on('error', function (e) { 19 | dest.statusCode = 404 20 | dest.setHeader('content-type', 'text/plain') 21 | dest.write('Not Found') 22 | dest.end() 23 | }) 24 | dest.setHeader('content-type', mimeTypes.lookup(src.path)) 25 | } 26 | if (src.statusCode) dest.statusCode = src.statusCode 27 | } 28 | 29 | function Response (view, opts) { 30 | var self = this 31 | if (typeof view !== 'string' && typeof view !== 'function' && !Buffer.isBuffer(view)) { 32 | opts = view 33 | view = null 34 | } 35 | self.opts = opts || {} 36 | if (self.opts.gzip) self.opts.compress = gzip 37 | self.view = view 38 | self.buffering = bl() 39 | self.headers = {} 40 | self.dests = [] 41 | stream.Transform.call(self) 42 | self.on('pipe', function (src) { 43 | mutations(src, self) 44 | src.on('error', function (e) { 45 | // TODO: Handle 404 errors 46 | self.emit('error', e) 47 | }) 48 | }) 49 | self.on('error', self.error.bind(self)) 50 | if (view) { 51 | if (typeof view === 'string' || Buffer.isBuffer(view)) { 52 | process.nextTick(function () { 53 | self.end(view) 54 | }) 55 | } 56 | } 57 | caseless.httpify(this, this.headers) 58 | if (self.opts.compress) { 59 | var encoding 60 | if (self.opts.compress.headers) { 61 | encoding = bestencoding(self.opts.compress) 62 | } else if (typeof self.opts.compress === 'string') { 63 | encoding = self.opts.compress 64 | } else { 65 | encoding = 'gzip' 66 | } 67 | 68 | if (encoding && encoding !== 'identity') { 69 | if (encoding !== 'gzip' && encoding !== 'deflate') throw new Error("I don't know this encoding.") 70 | self.statusCode = 200 71 | self.setHeader('content-encoding', encoding) 72 | if (encoding === 'gzip') self.compressor = zlib.createGzip() 73 | if (encoding === 'deflate') self.compressor = zlib.createDeflate() 74 | stream.Transform.prototype.pipe.call(this, self.compressor) 75 | } 76 | } 77 | } 78 | util.inherits(Response, stream.Transform) 79 | 80 | Response.prototype._transform = function (chunk, encoding, cb) { 81 | if (this._pipe) { 82 | this._pipe() 83 | this._pipe = null 84 | } 85 | if (typeof this.view === 'function') this.buffering.append(chunk) 86 | else this.push(chunk) 87 | cb(null) 88 | } 89 | Response.prototype.pipe = function () { 90 | this.dests.push(arguments[0]) 91 | if (this.compressor) this.compressor.pipe.apply(this.compressor, arguments) 92 | else stream.Transform.prototype.pipe.apply(this, arguments) 93 | } 94 | Response.prototype._pipe = function () { 95 | var self = this 96 | this.dests.forEach(function (dest) { 97 | mutations(self, dest) 98 | }) 99 | } 100 | Response.prototype.error = function (e, status) { 101 | var self = this 102 | 103 | if (!status && typeof e === 'number') { 104 | self.statusCode = e 105 | self.end() 106 | return 107 | } 108 | 109 | self.statusCode = status || 500 110 | if (self._erroring) return 111 | self._erroring = true 112 | if (typeof self.view === 'function') { 113 | self.view.call(self, e, null, function (e, data) { 114 | if (e) return self.end('Internal Server Error') 115 | self.end(data) 116 | }) 117 | } else { 118 | // TODO: Default tracebacks on errors. 119 | self.end( e ? e.message || 'Error' : 'Error') 120 | } 121 | } 122 | Response.prototype.status = function(statusCode) { 123 | this.statusCode = statusCode; 124 | return this 125 | } 126 | Response.prototype.location = function(location) { 127 | this.setHeader('Location', location) 128 | return this 129 | } 130 | Response.prototype.end = function (data) { 131 | var a = arguments 132 | , self = this 133 | ; 134 | if (data) this.write(data) 135 | if (typeof self.view === 'function') { 136 | self.view.call(self, null, self.buffering, function (e, data) { 137 | if (e) self.error(e) 138 | if (self.compressor) { 139 | self.compressor.write(data) 140 | } else { 141 | self.dests.forEach(function (dest) { 142 | dest.write(data) 143 | }) 144 | } 145 | stream.Transform.prototype.end.apply(self) 146 | }) 147 | } else { 148 | stream.Transform.prototype.end.apply(self) 149 | } 150 | } 151 | 152 | function response (view, opts) { 153 | return new Response(view, opts) 154 | } 155 | 156 | var typemap = {}; 157 | typemap['txt'] = function(view) { 158 | return (typeof view != 'string') ? view.toString() : view 159 | } 160 | typemap['json'] = function(view) { 161 | return JSON.stringify(view) 162 | } 163 | 164 | Object.keys(mimeTypes.types).forEach(function (mimeName) { 165 | 166 | // set mime convenience methods 167 | function _response (view, opts) { 168 | view = (typemap[mimeName]) ? typemap[mimeName](view) : view 169 | var r = response(view, opts) 170 | r.setHeader('content-type', mimeTypes.types[mimeName]) 171 | return r 172 | } 173 | response[mimeName] = _response 174 | // set mime methods 175 | Response.prototype[mimeName] = function (view) { 176 | view = (typemap[mimeName]) ? typemap[mimeName](view) : view 177 | var self = this 178 | self.setHeader('content-type', mimeTypes.types[mimeName]) 179 | process.nextTick(function () { 180 | self.end(view) 181 | }) 182 | return this 183 | } 184 | }) 185 | 186 | response.error = function () { 187 | var r = response() 188 | r.error.apply(r, arguments) 189 | return r 190 | } 191 | 192 | // TODO alias express methods 193 | // .charset 194 | // .send 195 | // .jsonp 196 | 197 | // TODO redirect api 198 | // TODO error, notfound 199 | 200 | module.exports = response 201 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "response", 3 | "version": "0.18.2", 4 | "description": "Streaming and mutation API for HTTP responses.", 5 | "main": "index.js", 6 | "directories": { 7 | "test": "tests" 8 | }, 9 | "dependencies": { 10 | "best-encoding": "^0.1.1", 11 | "bl": "~2.0.1", 12 | "caseless": "^0.12.0", 13 | "mime-types": "^2.1.20" 14 | }, 15 | "devDependencies": { 16 | "queuelib": "^2.0.1", 17 | "request": "~2.88.0", 18 | "tape": "~4.9.1" 19 | }, 20 | "scripts": { 21 | "test": "tape tests/*.js" 22 | }, 23 | "repository": { 24 | "type": "git", 25 | "url": "https://github.com/mikeal/response" 26 | }, 27 | "keywords": [ 28 | "http", 29 | "response", 30 | "request" 31 | ], 32 | "author": "Mikeal Rogers ", 33 | "contributors": [ 34 | "Satana Charuwichitratana " 35 | ], 36 | "license": "Apache-2.0", 37 | "bugs": { 38 | "url": "https://github.com/mikeal/response/issues" 39 | }, 40 | "homepage": "https://github.com/mikeal/response" 41 | } 42 | -------------------------------------------------------------------------------- /tests/test-compress.js: -------------------------------------------------------------------------------- 1 | var response = require('../') 2 | , http = require('http') 3 | , request = require('request') 4 | , fs = require('fs') 5 | , path = require('path') 6 | , tape = require('tape') 7 | , zlib = require('zlib') 8 | ; 9 | 10 | function f (name) { 11 | return fs.createReadStream(path.join(__dirname, name)) 12 | } 13 | function fr (name) { 14 | return fs.readFileSync(path.join(__dirname, name)).toString() 15 | } 16 | function ungz (body, cb) { 17 | zlib.gunzip(body, function (e, d) { 18 | if (e) return cb(e) 19 | cb(e, d.toString()) 20 | }) 21 | } 22 | function infl (body, cb) { 23 | zlib.inflate(body, function (e, d) { 24 | if (e) return cb(e) 25 | cb(e, d.toString()) 26 | }) 27 | } 28 | 29 | var server = http.createServer(function (req, res) { 30 | var r 31 | if (req.url === '/testfile') { 32 | r = response({compress:req}) 33 | return f('test-files.js').pipe(r).pipe(res) 34 | } else if (req.url === '/testgzip') { 35 | r = response({compress:'gzip'}) 36 | return f('test-files.js').pipe(r).pipe(res) 37 | } else if (req.url === '/testdeflate') { 38 | r = response({compress:'deflate'}) 39 | return f('test-files.js').pipe(r).pipe(res) 40 | } else if (req.url === '/testhtml') { 41 | return response.html('blah', {compress:req}).pipe(res) 42 | } else if (req.url === '/testjson') { 43 | return response.json({test:'asdf'}, {compress:req}).pipe(res) 44 | } else if (req.url === '/testview') { 45 | function view (e, data, cb) { 46 | if (e) { 47 | this.statusCode = 555 48 | return cb(e) 49 | } 50 | cb(null, '' + data + '') 51 | } 52 | var r = response(view, {compress:req}) 53 | r.pipe(res) 54 | return r.html('test') 55 | } 56 | }) 57 | 58 | 59 | var gzipopts = {headers:{'accept-encoding':'gzip'}, encoding:null} 60 | , deflateopts = {headers:{'accept-encoding':'deflate'}, encoding:null} 61 | ; 62 | 63 | server.listen(8085, function () { 64 | tape.test('js file gzip', function (t) { 65 | t.plan(4) 66 | var r = request('http://localhost:8085/testfile', gzipopts, function (e, resp, body) { 67 | if (e) return t.error(e) 68 | t.equal(resp.statusCode, 200) 69 | t.equal(resp.headers['content-type'], 'application/javascript') 70 | t.equal(resp.headers['content-encoding'], 'gzip') 71 | 72 | ungz(body, function (e, str) { 73 | if (e) return t.error(e) 74 | t.equal(str, fr('test-files.js')) 75 | }) 76 | }) 77 | }) 78 | 79 | tape.test('js file inflate', function (t) { 80 | t.plan(4) 81 | request('http://localhost:8085/testfile', deflateopts, function (e, resp, body) { 82 | if (e) return t.error(e) 83 | t.equal(resp.statusCode, 200) 84 | t.equal(resp.headers['content-type'], 'application/javascript') 85 | t.equal(resp.headers['content-encoding'], 'deflate') 86 | infl(body, function (e, str) { 87 | if (e) return t.error(e) 88 | t.equal(str, fr('test-files.js')) 89 | }) 90 | }) 91 | }) 92 | 93 | tape.test('js file dont', function (t) { 94 | t.plan(4) 95 | request('http://localhost:8085/testfile', function (e, resp, body) { 96 | if (e) return t.error(e) 97 | t.equal(resp.statusCode, 200) 98 | t.equal(resp.headers['content-type'], 'application/javascript') 99 | t.equal(resp.headers['content-encoding'], undefined) 100 | t.equal(body, fr('test-files.js')) 101 | }) 102 | }) 103 | 104 | tape.test('js file explicit gzip', function (t) { 105 | t.plan(4) 106 | request('http://localhost:8085/testgzip', {encoding:null}, function (e, resp, body) { 107 | if (e) return t.error(e) 108 | t.equal(resp.statusCode, 200) 109 | t.equal(resp.headers['content-type'], 'application/javascript') 110 | t.equal(resp.headers['content-encoding'], 'gzip') 111 | ungz(body, function (e, str) { 112 | if (e) return t.error(e) 113 | t.equal(str, fr('test-files.js')) 114 | }) 115 | }) 116 | }) 117 | 118 | tape.test('js file explicit deflate', function (t) { 119 | t.plan(4) 120 | request('http://localhost:8085/testdeflate', {encoding:null}, function (e, resp, body) { 121 | if (e) return t.error(e) 122 | t.equal(resp.statusCode, 200) 123 | t.equal(resp.headers['content-type'], 'application/javascript') 124 | t.equal(resp.headers['content-encoding'], 'deflate') 125 | infl(body, function (e, str) { 126 | if (e) return t.error(e) 127 | t.equal(str, fr('test-files.js')) 128 | }) 129 | }) 130 | }) 131 | 132 | tape.test('html string', function (t) { 133 | t.plan(4) 134 | request('http://localhost:8085/testhtml', gzipopts, function (e, resp, body) { 135 | if (e) return t.error(e) 136 | t.equal(resp.statusCode, 200) 137 | t.equal(resp.headers['content-type'], 'text/html') 138 | t.equal(resp.headers['content-encoding'], 'gzip') 139 | ungz(body, function (e, str) { 140 | if (e) return t.error(e) 141 | t.equal(str, 'blah') 142 | }) 143 | }) 144 | }) 145 | 146 | tape.test('json', function (t) { 147 | t.plan(4) 148 | request('http://localhost:8085/testjson', gzipopts, function (e, resp, body) { 149 | if (e) return t.error(e) 150 | t.equal(resp.statusCode, 200) 151 | t.equal(resp.headers['content-type'], 'application/json') 152 | t.equal(resp.headers['content-encoding'], 'gzip') 153 | ungz(body, function (e, str) { 154 | if (e) return t.error(e) 155 | t.equal(str, '{"test":"asdf"}') 156 | }) 157 | }) 158 | }) 159 | 160 | tape.test('view', function (t) { 161 | t.plan(4) 162 | request('http://localhost:8085/testview', gzipopts, function (e, resp, body) { 163 | if (e) return t.error(e) 164 | t.equal(resp.statusCode, 200) 165 | t.equal(resp.headers['content-type'], 'text/html') 166 | t.equal(resp.headers['content-encoding'], 'gzip') 167 | ungz(body, function (e, str) { 168 | if (e) return t.error(e) 169 | t.equal(str, 'test') 170 | }) 171 | }) 172 | }) 173 | 174 | tape.test('cleanup', function (t) { t.end(); server.close() }) 175 | }) 176 | -------------------------------------------------------------------------------- /tests/test-error.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeal/response/a158237cca15cfd1814a0e64e1b7f40779fd59a8/tests/test-error.js -------------------------------------------------------------------------------- /tests/test-files.js: -------------------------------------------------------------------------------- 1 | var response = require('../') 2 | , http = require('http') 3 | , request = require('request') 4 | , fs = require('fs') 5 | , path = require('path') 6 | , tape = require('tape') 7 | ; 8 | 9 | function f (name) { 10 | return fs.createReadStream(path.join(__dirname, name)) 11 | } 12 | function fr (name) { 13 | return fs.readFileSync(path.join(__dirname, name)).toString() 14 | } 15 | 16 | var server = http.createServer(function (req, res) { 17 | if (req.url === '/test1') return f('test-files.js').pipe(response()).pipe(res) 18 | }) 19 | 20 | 21 | server.listen(8080, function () { 22 | tape.test('js file', function (t) { 23 | t.plan(3) 24 | request('http://localhost:8080/test1', function (e, resp, body) { 25 | if (e) return t.error(e) 26 | t.equal(resp.statusCode, 200) 27 | t.equal(resp.headers['content-type'], 'application/javascript') 28 | t.equal(body, fr('test-files.js')) 29 | }) 30 | }) 31 | 32 | tape.test('cleanup', function (t) { t.end(); server.close() }) 33 | }) 34 | -------------------------------------------------------------------------------- /tests/test-html.js: -------------------------------------------------------------------------------- 1 | var response = require('../') 2 | , http = require('http') 3 | , request = require('request') 4 | , fs = require('fs') 5 | , path = require('path') 6 | , tape = require('tape') 7 | ; 8 | 9 | function f (name) { 10 | return fs.createReadStream(path.join(__dirname, name)) 11 | } 12 | function fr (name) { 13 | return fs.readFileSync(path.join(__dirname, name)).toString() 14 | } 15 | 16 | var server = http.createServer(function (req, res) { 17 | if (req.url === '/test1') return response.html('').pipe(res) 18 | }) 19 | 20 | server.listen(8081, function () { 21 | tape.test('html string', function (t) { 22 | t.plan(3) 23 | request('http://localhost:8081/test1', function (e, resp, body) { 24 | if (e) return t.error(e) 25 | t.equal(resp.statusCode, 200) 26 | t.equal(resp.headers['content-type'], 'text/html') 27 | t.equal(body, '') 28 | }) 29 | }) 30 | 31 | tape.test('cleanup', function (t) { t.end(); server.close() }) 32 | }) 33 | -------------------------------------------------------------------------------- /tests/test-json.js: -------------------------------------------------------------------------------- 1 | var response = require('../') 2 | , http = require('http') 3 | , request = require('request') 4 | , fs = require('fs') 5 | , path = require('path') 6 | , tape = require('tape') 7 | ; 8 | 9 | function f (name) { 10 | return fs.createReadStream(path.join(__dirname, name)) 11 | } 12 | function fr (name) { 13 | return fs.readFileSync(path.join(__dirname, name)).toString() 14 | } 15 | 16 | var server = http.createServer(function (req, res) { 17 | if (req.url === '/test1') return response.json({test:1}).pipe(res) 18 | if (req.url === '/test2') { 19 | var r = response(); 20 | r.statusCode = 302; 21 | r.json({test:2}).pipe(res) 22 | } 23 | }) 24 | 25 | server.listen(8082, function () { 26 | tape.test('basic json', function (t) { 27 | t.plan(3) 28 | request('http://localhost:8082/test1', {json:true}, function (e, resp, body) { 29 | if (e) return t.error(e) 30 | t.equal(resp.statusCode, 200) 31 | t.equal(resp.headers['content-type'], 'application/json') 32 | t.deepEqual(body, {test:1}) 33 | }) 34 | }) 35 | tape.test('basic json status code', function (t) { 36 | t.plan(3) 37 | request('http://localhost:8082/test2', {json:true}, function (e, resp, body) { 38 | if (e) return t.error(e) 39 | t.equal(resp.statusCode, 302) 40 | t.equal(resp.headers['content-type'], 'application/json') 41 | t.deepEqual(body, {test:2}) 42 | }) 43 | }) 44 | 45 | tape.test('cleanup', function (t) { t.end(); server.close() }) 46 | }) 47 | -------------------------------------------------------------------------------- /tests/test-location.js: -------------------------------------------------------------------------------- 1 | var response = require('../') 2 | , http = require('http') 3 | , request = require('request') 4 | , fs = require('fs') 5 | , path = require('path') 6 | , ql = require('queuelib'); 7 | ; 8 | var tape = require('tape'); 9 | 10 | function f (name) { 11 | return fs.createReadStream(path.join(__dirname, name)) 12 | } 13 | function fr (name) { 14 | return fs.readFileSync(path.join(__dirname, name)).toString() 15 | } 16 | 17 | var server = http.createServer(function (req, res) { 18 | if (req.url == '/') 19 | response.json({test:1}).location('/foo').status(302).pipe(res) 20 | else if (req.url = '/foo') 21 | response.txt('bar').pipe(res) 22 | }) 23 | 24 | var q = new ql; 25 | tape.test('testing new view mapping, convenience functions, and member functions',function(t) { 26 | t.plan(2) 27 | q.series([ 28 | function(lib) { 29 | server.listen(8084,function(){ 30 | lib.done() 31 | }); 32 | }, 33 | function(lib) { 34 | request('http://localhost:8084/', {json:true}, function (e, resp, body) { 35 | t.equal(resp.headers['content-type'],'text/plain') 36 | t.equal(body,'bar') 37 | lib.done(); 38 | }) 39 | }, 40 | function(lib) { 41 | tape.test('cleanup',function(t) { 42 | t.end() 43 | server.close() 44 | lib.done() 45 | }); 46 | } 47 | ]); 48 | }) 49 | -------------------------------------------------------------------------------- /tests/test-viewmapping-status.js: -------------------------------------------------------------------------------- 1 | var response = require('../') 2 | , http = require('http') 3 | , request = require('request') 4 | , fs = require('fs') 5 | , path = require('path') 6 | , ql = require('queuelib'); 7 | ; 8 | var tape = require('tape'); 9 | 10 | function f (name) { 11 | return fs.createReadStream(path.join(__dirname, name)) 12 | } 13 | function fr (name) { 14 | return fs.readFileSync(path.join(__dirname, name)).toString() 15 | } 16 | 17 | var server = http.createServer(function (req, res) { 18 | console.log(req.method + " "+ req.url); 19 | switch (req.url) { 20 | case '/test1a' : 21 | response.json({test:1}).pipe(res) 22 | break; 23 | case '/test1b' : 24 | var x = response(); 25 | x.json({test:1}).pipe(res) 26 | break; 27 | case '/test2a' : 28 | response.json({test:1}).status(302).pipe(res) 29 | break; 30 | case '/test2b' : 31 | var x = response(); 32 | x.json({test:1}).status(302).pipe(res) 33 | break; 34 | case '/test4a' : 35 | response.json(23423).pipe(res) 36 | break; 37 | case '/test4b' : 38 | var x = response(); 39 | x.json(23423).pipe(res) 40 | break; 41 | case '/test6a' : 42 | response.txt(23423).pipe(res) 43 | break; 44 | case '/test6b' : 45 | var x = response(); 46 | x.txt(23423).pipe(res); 47 | break; 48 | default: 49 | break; 50 | } 51 | }) 52 | 53 | var q = new ql; 54 | tape.test('testing new view mapping, convenience functions, and member functions',function(t) { 55 | t.plan(18) 56 | q.series([ 57 | function(lib) { 58 | server.listen(8090,function(){ 59 | lib.done() 60 | }); 61 | }, 62 | function(lib) { 63 | request('http://localhost:8090/test1a', {json:true}, function (e, resp, body) { 64 | t.equal(resp.headers['content-type'],'application/json') 65 | t.deepEqual(body,{test:1}); 66 | lib.done(); 67 | }) 68 | }, 69 | function(lib) { 70 | request('http://localhost:8090/test1b', {json:true}, function (e, resp, body) { 71 | t.equal(resp.headers['content-type'],'application/json') 72 | t.deepEqual(body,{test:1}); 73 | lib.done(); 74 | }) 75 | }, 76 | function(lib) { 77 | request('http://localhost:8090/test2a', {json:true}, function (e, resp, body) { 78 | t.equal(resp.statusCode, 302, 'status code should be 302') 79 | t.equal(resp.headers['content-type'],'application/json') 80 | t.deepEqual(body,{test:1}); 81 | lib.done(); 82 | }) 83 | }, 84 | function(lib) { 85 | request('http://localhost:8090/test2b', {json:true}, function (e, resp, body) { 86 | t.equal(resp.statusCode, 302, 'status code should be 302') 87 | t.equal(resp.headers['content-type'],'application/json') 88 | t.deepEqual(body,{test:1}); 89 | lib.done(); 90 | }) 91 | }, 92 | function(lib) { 93 | request('http://localhost:8090/test4a', {json:true}, function (e, resp, body) { 94 | t.equal(resp.headers['content-type'],'application/json') 95 | t.equal(body,23423); 96 | lib.done(); 97 | }) 98 | }, 99 | function(lib) { 100 | request('http://localhost:8090/test4b', {json:true}, function (e, resp, body) { 101 | t.equal(resp.headers['content-type'],'application/json') 102 | t.equal(body,23423); 103 | lib.done(); 104 | }) 105 | }, 106 | function(lib) { 107 | request('http://localhost:8090/test6a', {json:true}, function (e, resp, body) { 108 | t.equal(resp.headers['content-type'],'text/plain') 109 | t.equal(body,23423); 110 | lib.done(); 111 | }) 112 | }, 113 | function(lib) { 114 | request('http://localhost:8090/test6b', {json:true}, function (e, resp, body) { 115 | t.equal(resp.headers['content-type'],'text/plain') 116 | t.equal(body,23423); 117 | lib.done(); 118 | }) 119 | }, 120 | function(lib) { 121 | tape.test('cleanup',function(t) { 122 | server.on('close',function() { 123 | t.end() 124 | lib.done() 125 | }); 126 | server.close() 127 | }); 128 | } 129 | ]); 130 | }) 131 | -------------------------------------------------------------------------------- /tests/test-views.js: -------------------------------------------------------------------------------- 1 | var response = require('../') 2 | , http = require('http') 3 | , request = require('request') 4 | , fs = require('fs') 5 | , path = require('path') 6 | , tape = require('tape') 7 | ; 8 | 9 | function f (name) { 10 | return fs.createReadStream(path.join(__dirname, name)) 11 | } 12 | function fr (name) { 13 | return fs.readFileSync(path.join(__dirname, name)).toString() 14 | } 15 | 16 | function view (e, data, cb) { 17 | if (e) { 18 | this.statusCode = 555 19 | return cb(e) 20 | } 21 | cb(null, '' + data + '') 22 | } 23 | 24 | var server = http.createServer(function (req, res) { 25 | var r = response(view) 26 | r.pipe(res) 27 | if (req.url === '/test1') return r.html('test') 28 | if (req.url === '/test2') return r.error(new Error()) 29 | }) 30 | 31 | server.listen(8083, function () { 32 | tape.test('basic html view', function (t) { 33 | t.plan(3) 34 | request('http://localhost:8083/test1', {json:true}, function (e, resp, body) { 35 | if (e) return t.error(e) 36 | t.equal(resp.statusCode, 200) 37 | t.equal(resp.headers['content-type'], 'text/html') 38 | t.equal(body, 'test') 39 | }) 40 | }) 41 | 42 | // tape.test('basic error view', function (t) { 43 | // t.plan(3) 44 | // request('http://localhost:8083/test1', {json:true}, function (e, resp, body) { 45 | // if (e) return t.error(e) 46 | // t.equal(resp.statusCode, 555) 47 | // // t.equal(resp.headers['content-type'], 'text/plain') // TODO 48 | // t.deepEqual(body, 'Internal Server Error') 49 | // }) 50 | // }) 51 | 52 | tape.test('cleanup', function (t) { t.end(); server.close() }) 53 | }) 54 | --------------------------------------------------------------------------------