├── .gitignore ├── .travis.yml ├── Makefile ├── README.md ├── example ├── tor-request.js └── tor.js ├── index.js ├── lib └── Agent.js ├── package.json └── test └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "8.1" 5 | - "6.11" 6 | 7 | script: make test 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | test: index.js lib/*.js node_modules 2 | ./node_modules/.bin/mocha \ 3 | --reporter dot \ 4 | --check-leaks \ 5 | --ui tdd 6 | 7 | node_modules: package.json 8 | npm install 9 | touch $@ 10 | 11 | .PHONY: test 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SOCKS5 HTTP Client # 2 | 3 | [![Build Status](https://travis-ci.org/mattcg/socks5-http-client.png?branch=master)](https://travis-ci.org/mattcg/socks5-http-client) 4 | 5 | SOCKS v5 HTTP client implementation in JavaScript for Node.js. 6 | 7 | ```js 8 | var shttp = require('socks5-http-client'); 9 | 10 | shttp.get('http://www.google.com/', function(res) { 11 | res.setEncoding('utf8'); 12 | res.on('readable', function() { 13 | console.log(res.read()); // Log response to console. 14 | }); 15 | }); 16 | ``` 17 | 18 | URLs are parsed using `url.parse`. You may also pass an options hash as the first argument to `get` or `request`. 19 | 20 | ## Options ## 21 | 22 | Specify the `socksHost` and `socksPort` options if your SOCKS server isn't running on `localhost:1080`. Tor runs its SOCKS server on port `9050` by default, for example. 23 | 24 | Specify a username and password using `socksUsername` and `socksPassword`. 25 | 26 | ## Using with Tor ## 27 | 28 | Works great for making HTTP requests through [Tor](https://www.torproject.org/). 29 | 30 | Make sure a Tor server is running locally and run `node example/tor http://en.wikipedia.org/wiki/SOCKS` to test. 31 | 32 | ## Using with Request ## 33 | 34 | To use with [Request](https://github.com/mikeal/request), just pass a reference to the `Agent` constructor. 35 | 36 | ```js 37 | var Agent = require('socks5-http-client/lib/Agent'); 38 | 39 | request({ 40 | url: 'http://en.wikipedia.org/wiki/SOCKS', 41 | agentClass: Agent, 42 | agentOptions: { 43 | socksHost: 'my-tor-proxy-host', // Defaults to 'localhost'. 44 | socksPort: 9050 // Defaults to 1080. 45 | } 46 | }, function(err, res) { 47 | console.log(err || res.body); 48 | }); 49 | ``` 50 | 51 | ## HTTPS ## 52 | 53 | This client only provides support for making HTTP requests. See [socks5-https-client](https://github.com/mattcg/socks5-https-client) for an HTTPS implementation. 54 | 55 | ## License ## 56 | 57 | Copyright © 2013 [Matthew Caruana Galizia](http://twitter.com/mcaruanagalizia), licensed under an [MIT license](http://mattcg.mit-license.org/). 58 | -------------------------------------------------------------------------------- /example/tor-request.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /*jshint node:true*/ 4 | 5 | var request = require('request'); 6 | 7 | var Agent = require('../lib/Agent'); 8 | 9 | request({ 10 | url: process.argv[2], 11 | agentClass: Agent, 12 | agentOptions: { 13 | socksPort: 9050 // Defaults to 1080. 14 | } 15 | }, function(err, res) { 16 | console.log(res.body); 17 | }); 18 | -------------------------------------------------------------------------------- /example/tor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /*jshint node:true*/ 4 | 5 | var url = require('url'); 6 | var shttp = require('../'); 7 | 8 | var options = url.parse(process.argv[2]); 9 | 10 | options.socksPort = 9050; // Tor default port. 11 | 12 | var req = shttp.get(options, function(res) { 13 | res.setEncoding('utf8'); 14 | 15 | res.on('readable', function() { 16 | var data = res.read(); 17 | 18 | // Check for the end of stream signal. 19 | if (null === data) { 20 | process.stdout.write('\n'); 21 | return; 22 | } 23 | 24 | process.stdout.write(data); 25 | }); 26 | }); 27 | 28 | req.on('error', function(e) { 29 | console.error('Problem with request: ' + e.message); 30 | }); 31 | 32 | // GET request, so end without sending any data. 33 | req.end(); 34 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @overview 3 | * @author Matthew Caruana Galizia 4 | * @license MIT 5 | * @copyright Copyright (c) 2013, Matthew Caruana Galizia 6 | * @preserve 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /*jshint node:true*/ 12 | 13 | var http = require('http'); 14 | var url = require('url'); 15 | 16 | var Agent = require('./lib/Agent'); 17 | 18 | exports.request = function(options, cb) { 19 | if (typeof options === 'string') { 20 | options = url.parse(options); 21 | } 22 | 23 | if (!options.port) { 24 | options.port = 80; 25 | } 26 | 27 | options.agent = new Agent(options); 28 | return http.request(options, cb); 29 | }; 30 | 31 | exports.Agent = Agent; 32 | 33 | exports.get = function(options, cb) { 34 | var req = exports.request(options, cb); 35 | 36 | req.end(); 37 | 38 | return req; 39 | }; 40 | -------------------------------------------------------------------------------- /lib/Agent.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @overview 3 | * @author Matthew Caruana Galizia 4 | * @license MIT 5 | * @copyright Copyright (c) 2013, Matthew Caruana Galizia 6 | * @preserve 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /*jshint node:true*/ 12 | 13 | var http = require('http'); 14 | var inherits = require('util').inherits; 15 | var socksClient = require('socks5-client'); 16 | 17 | function Agent(options) { 18 | http.Agent.call(this, options); 19 | this.createConnection = socksClient.createConnection; 20 | } 21 | 22 | inherits(Agent, http.Agent); 23 | 24 | module.exports = Agent; 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "socks5-http-client", 3 | "description": "SOCKS v5 HTTP client.", 4 | "version": "1.0.4", 5 | "main": "index.js", 6 | "homepage": "https://github.com/mattcg/socks5-http-client", 7 | "implements": [ 8 | "CommonJS/Modules/1.0" 9 | ], 10 | "author": { 11 | "name": "Matthew Caruana Galizia", 12 | "email": "mattcg@gmail.com" 13 | }, 14 | "keywords": [ 15 | "socks5", 16 | "socksv5", 17 | "socks", 18 | "v5", 19 | "http", 20 | "tor", 21 | "client" 22 | ], 23 | "bugs": { 24 | "url": "https://github.com/mattcg/socks5-http-client/issues" 25 | }, 26 | "repository": { 27 | "type": "git", 28 | "url": "https://github.com/mattcg/socks5-http-client.git" 29 | }, 30 | "dependencies": { 31 | "socks5-client": "~1.2.6" 32 | }, 33 | "devDependencies": { 34 | "mocha": "~3.1.2", 35 | "node-socks": "~0.1.0" 36 | }, 37 | "scripts": { 38 | "test": "./node_modules/.bin/mocha --reporter dot --check-leaks --ui tdd" 39 | }, 40 | "engines": { 41 | "node": ">= 6.4.0" 42 | }, 43 | "engineStrict": true, 44 | "license": "MIT" 45 | } 46 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @overview 3 | * @author Matthew Caruana Galizia 4 | * @copyright Copyright (c) 2013, Matthew Caruana Galizia 5 | * @license MIT 6 | * @preserve 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /*jshint node:true*/ 12 | /*global test, suite, setup, teardown*/ 13 | 14 | var assert = require('assert'); 15 | var net = require('net'); 16 | var socks = require('node-socks/socks.js'); 17 | var http = require('../'); 18 | 19 | suite('socks5-http-client tests', function() { 20 | var server, timeout; 21 | 22 | this.timeout(5000); 23 | 24 | setup(function(done) { 25 | server = socks.createServer(function(socket, port, address, proxyReady) { 26 | var proxy; 27 | 28 | proxy = net.createConnection(port, address, proxyReady); 29 | 30 | proxy.on('data', function(data) { 31 | if (timeout) { 32 | setTimeout(function() { 33 | socket.write(data); 34 | }, timeout); 35 | } else { 36 | socket.write(data); 37 | } 38 | }); 39 | 40 | socket.on('data', function(data) { 41 | proxy.write(data); 42 | }); 43 | 44 | proxy.on('close', function() { 45 | socket.end(); 46 | }); 47 | 48 | socket.on('close', function() { 49 | proxy.removeAllListeners('data'); 50 | proxy.end(); 51 | }); 52 | }); 53 | 54 | server.listen(1080, 'localhost', 511, function() { 55 | done(); 56 | }); 57 | 58 | server.on('error', function(err) { 59 | throw err; 60 | }); 61 | }); 62 | 63 | teardown(function(done) { 64 | server.close(done); 65 | 66 | timeout = null; 67 | }); 68 | 69 | test('simple request', function(done) { 70 | var req; 71 | 72 | req = http.request('http://www.example.com/', function(res, err) { 73 | var data = ''; 74 | 75 | assert.ifError(err); 76 | assert.equal(res.statusCode, 200); 77 | 78 | res.setEncoding('utf8'); 79 | res.on('readable', function() { 80 | data += res.read(); 81 | }); 82 | 83 | res.on('end', function() { 84 | assert(-1 !== data.indexOf('')); 86 | 87 | done(); 88 | }); 89 | }); 90 | 91 | req.on('error', function(err) { 92 | assert.fail(err); 93 | }); 94 | 95 | // GET request, so end without sending any data. 96 | req.end(); 97 | }); 98 | 99 | test('timeout with setTimeout()', function(done) { 100 | var req; 101 | 102 | timeout = 2; 103 | 104 | req = http.get('http://www.example.com/'); 105 | 106 | req.setTimeout(timeout / 2, function() { 107 | done(); 108 | }); 109 | 110 | req.on('error', function(err) { 111 | assert.fail(err); 112 | }); 113 | }); 114 | 115 | test('timeout with on()', function(done) { 116 | var req; 117 | 118 | timeout = 2; 119 | 120 | req = http.get('http://www.example.com/'); 121 | 122 | req.setTimeout(timeout / 2); 123 | req.once('timeout', function() { 124 | done(); 125 | }); 126 | 127 | req.on('error', function(err) { 128 | assert.fail(err); 129 | }); 130 | }); 131 | }); 132 | --------------------------------------------------------------------------------