Skip to content

Commit

Permalink
Merge pull request #123 from ooogway/master
Browse files Browse the repository at this point in the history
Add support for Unix sockets
  • Loading branch information
geek committed Mar 24, 2016
2 parents 0a9e799 + 5af4e77 commit 96e1e9d
Show file tree
Hide file tree
Showing 3 changed files with 190 additions and 10 deletions.
22 changes: 13 additions & 9 deletions README.md
Expand Up @@ -23,7 +23,7 @@ Wreck.get('https://google.com/', function (err, res, payload) {
var Wreck = require('wreck');

var method = 'GET'; // GET, POST, PUT, DELETE
var uri = 'https://google.com/';
var uri = '/';
var readableStream = Wreck.toReadableStream('foo=bar');

var wreck = Wreck.defaults({
Expand All @@ -37,9 +37,7 @@ var wreckWithTimeout = wreck.defaults({

// all attributes are optional
var options = {
baseUrl: fully qualified uri string used as the base url. Most useful with `request.defaults`, for example when you want to do many requests to the same domain.
If `baseUrl` is `https://example.com/api/`, then requesting `/end/point?test=true` will fetch `https://example.com/api/end/point?test=true`. Any
querystring in the `baseUrl` will be overwritten with the querystring in the `uri` When `baseUrl` is given, `uri` must also be a string.
baseUrl: "https://www.example.com",
payload: readableStream || 'foo=bar' || new Buffer('foo=bar'),
headers: { /* http headers */ },
redirects: 3,
Expand Down Expand Up @@ -79,11 +77,12 @@ Initiate an HTTP request.
- `options` - An optional configuration object. To omit this argument but still
use a callback, pass `null` in this position. The options object supports the
following optional keys:
- `baseUrl` - fully qualified uri string used as the base url. Most useful with `request.defaults`, for example when you want to do many requests to the same domain.
If `baseUrl` is `https://example.com/api/`, then requesting `/end/point?test=true` will fetch `https://example.com/api/end/point?test=true`. Any
querystring in the `baseUrl` will be overwritten with the querystring in the `uri` When `baseUrl` is given, `uri` must also be a string.
- `socketPath` - `/path/to/unix/socket` for Server.
- `payload` - The request body as string, Buffer, or Readable Stream.
- `headers` - An object containing request headers.
- `rejectUnauthorized` - [TLS](http://nodejs.org/api/tls.html) flag indicating
whether the client should reject a response from a server with invalid certificates. This cannot be set at the
same time as the `agent` option is set.
- `redirects` - The maximum number of redirects to follow.
- `beforeRedirect` - A callback function that is called before a redirect is triggered, using the signature function (redirectMethod, statusCode, location, redirectOptions) where:
- `redirectMethod` - A string specifying the redirect method.
Expand All @@ -94,10 +93,15 @@ Initiate an HTTP request.
- `statusCode` - HTTP status code of the response that triggered the redirect.
- `location` - The redirected location string.
- `req` - The new [ClientRequest](http://nodejs.org/api/http.html#http_class_http_clientrequest) object which replaces the one initially returned.
- `agent` - Node Core [http.Agent](http://nodejs.org/api/http.html#http_class_http_agent).
Defaults to either `wreck.agents.http` or `wreck.agents.https`. Setting to `false` disables agent pooling.
- `timeout` - The number of milliseconds to wait without receiving a response
before aborting the request. Defaults to unlimited.
- `maxBytes` - maximum size for response payload. Defaults to unlimited.
- `rejectUnauthorized` - [TLS](http://nodejs.org/api/tls.html) flag indicating
whether the client should reject a response from a server with invalid certificates. This cannot be set at the
same time as the `agent` option is set.
- `downstreamRes`: downstream Resource dependency.
- `agent` - Node Core [http.Agent](http://nodejs.org/api/http.html#http_class_http_agent).
Defaults to either `wreck.agents.http` or `wreck.agents.https`. Setting to `false` disables agent pooling.
- `secureProtocol` - [TLS](http://nodejs.org/api/tls.html) flag indicating the SSL method to use, e.g. `SSLv3_method`
to force SSL version 3. The possible values depend on your installation of OpenSSL. Read the official OpenSSL docs
for possible [SSL_METHODS](http://www.openssl.org/docs/ssl/ssl.html#DEALING_WITH_PROTOCOL_METHODS).
Expand Down
6 changes: 6 additions & 0 deletions lib/index.js
Expand Up @@ -87,6 +87,12 @@ internals.Client.prototype.request = function (method, url, options, callback, _
}

const uri = Url.parse(url);

if (options.socketPath) {
uri.socketPath = options.socketPath;
delete options.socketPath;
}

uri.method = method.toUpperCase();
uri.headers = options.headers || {};
const hasContentLength = Object.keys(uri.headers).some((key) => {
Expand Down
172 changes: 171 additions & 1 deletion test/index.js
Expand Up @@ -17,7 +17,8 @@ const Wreck = require('../');
// Declare internals

const internals = {
payload: new Array(1640).join('0123456789') // make sure we have a payload larger than 16384 bytes for chunking coverage
payload: new Array(1640).join('0123456789'), // make sure we have a payload larger than 16384 bytes for chunking coverage
socket: __dirname + '/server.sock'
};


Expand Down Expand Up @@ -197,6 +198,175 @@ describe('request()', () => {
});
});

describe('unix socket', () => {

it('requests a resource with callback', (done) => {

const server = Http.createServer((req, res) => {

res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end(internals.payload);
});

server.listen(internals.socket, () => {

Wreck.request('get', '/', { socketPath: internals.socket }, (err, res) => {

expect(err).to.not.exist();
Wreck.read(res, null, (err, body) => {

expect(err).to.not.exist();
expect(Buffer.isBuffer(body)).to.equal(true);
expect(body.toString()).to.equal(internals.payload);
server.close();
done();
});
});
});
});

it('requests a POST resource', (done) => {

const server = Http.createServer((req, res) => {

expect(req.headers['content-length']).to.equal('16390');
res.writeHead(200, { 'Content-Type': 'text/plain' });
req.pipe(res);
});

server.listen(internals.socket, () => {

Wreck.request('post', '/', { socketPath: internals.socket, payload: internals.payload }, (err, res) => {

expect(err).to.not.exist();
Wreck.read(res, null, (err, body) => {

expect(err).to.not.exist();
expect(body.toString()).to.equal(internals.payload);
server.close();
done();
});
});
});
});

it('requests a POST resource with unicode characters in payload', (done) => {

const server = Http.createServer((req, res) => {

expect(req.headers['content-length']).to.equal('14');
res.writeHead(200, { 'Content-Type': 'text/plain' });
req.pipe(res);
});

server.listen(internals.socket, () => {

const unicodePayload = JSON.stringify({ field: 'ć' });
Wreck.request('post', '/', { socketPath: internals.socket, payload: unicodePayload }, (err, res) => {

expect(err).to.not.exist();
Wreck.read(res, null, (err, body) => {

expect(err).to.not.exist();
expect(body.toString()).to.equal(unicodePayload);
server.close();
done();
});
});
});
});

it('should not overwrite content-length if it is already in the headers', (done) => {

const server = Http.createServer((req, res) => {

expect(req.headers['content-length']).to.equal('16390');
res.writeHead(200, { 'Content-Type': 'text/plain' });
req.pipe(res);
});

server.listen(internals.socket, () => {

const options = { socketPath: internals.socket, payload: internals.payload, headers: { 'Content-Length': '16390' } };
Wreck.request('post', '/', options, (err, res) => {

expect(err).to.not.exist();
Wreck.read(res, null, (err, body) => {

expect(err).to.not.exist();
expect(body.toString()).to.equal(internals.payload);
server.close();
done();
});
});
});
});

it('requests a POST resource with headers', (done) => {

const server = Http.createServer((req, res) => {

res.writeHead(200, { 'Content-Type': 'text/plain' });
req.pipe(res);
});

server.listen(internals.socket, () => {

Wreck.request('post', '/', { socketPath: internals.socket, headers: { 'user-agent': 'wreck' }, payload: internals.payload }, (err, res) => {

expect(err).to.not.exist();
Wreck.read(res, null, (err, body) => {

expect(err).to.not.exist();
expect(body.toString()).to.equal(internals.payload);
server.close();
done();
});
});
});
});

it('requests a POST resource with stream payload', (done) => {

const server = Http.createServer((req, res) => {

res.writeHead(200, { 'Content-Type': 'text/plain' });
req.pipe(res);
});

server.listen(internals.socket, () => {

Wreck.request('post', '/', { socketPath: internals.socket, payload: Wreck.toReadableStream(internals.payload) }, (err, res) => {

expect(err).to.not.exist();
Wreck.read(res, null, (err, body) => {

expect(err).to.not.exist();
expect(body.toString()).to.equal(internals.payload);
server.close();
done();
});
});
});
});

it('requests a resource without callback', (done) => {

const server = Http.createServer((req, res) => {

res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end(internals.payload);
server.close();
done();
});

server.listen(internals.socket, () => {

Wreck.request('get', '/', { socketPath: internals.socket });
});
});
});

it('cannot set agent and rejectUnauthorized at the same time', (done) => {

expect(() => {
Expand Down

0 comments on commit 96e1e9d

Please sign in to comment.