Skip to content

Commit

Permalink
Add socks5 proxy
Browse files Browse the repository at this point in the history
  • Loading branch information
alchen committed Jun 2, 2018
1 parent 642e62c commit ce64328
Show file tree
Hide file tree
Showing 7 changed files with 647 additions and 81 deletions.
47 changes: 0 additions & 47 deletions lib/helpers.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
var querystring = require('querystring');
var request = require('request');

var endpoints = require('./endpoints');

Expand Down Expand Up @@ -80,49 +79,3 @@ exports.makeTwitError = function (message) {
err.twitterReply = null
return err
}

/**
* Get a bearer token for OAuth2
* @param {String} consumer_key
* @param {String} consumer_secret
* @param {Function} cb
*
* Calls `cb` with Error, String
*
* Error (if it exists) is guaranteed to be Twit error-formatted.
* String (if it exists) is the bearer token received from Twitter.
*/
exports.getBearerToken = function (consumer_key, consumer_secret, cb) {
// use OAuth 2 for app-only auth (Twitter requires this)
// get a bearer token using our app's credentials
var b64Credentials = new Buffer(consumer_key + ':' + consumer_secret).toString('base64');
request.post({
url: endpoints.API_HOST + 'oauth2/token',
headers: {
'Authorization': 'Basic ' + b64Credentials,
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
},
body: 'grant_type=client_credentials',
json: true,
}, function (err, res, body) {
if (err) {
var error = exports.makeTwitError(err.toString());
exports.attachBodyInfoToError(error, body);
return cb(error, body, res);
}

if ( !body ) {
var error = exports.makeTwitError('Not valid reply from Twitter upon obtaining bearer token');
exports.attachBodyInfoToError(error, body);
return cb(error, body, res);
}

if (body.token_type !== 'bearer') {
var error = exports.makeTwitError('Unexpected reply from Twitter upon obtaining bearer token');
exports.attachBodyInfoToError(error, body);
return cb(error, body, res);
}

return cb(err, body.access_token);
})
}
23 changes: 23 additions & 0 deletions lib/request.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const request = require('request');
const socks = require('socksv5');

module.exports = function (config) {
var proxyConfig = config.proxy;

if (proxyConfig) {
return request.defaults({
agentClass: socks.HttpsAgent,
agentOptions: {
proxyHost: proxyConfig.host,
proxyPort: proxyConfig.port,
auths: [
proxyConfig.auth ?
socks.auth.UserPassword(proxyConfig.auth.username, proxyConfig.auth.password) :
socks.auth.None()
]
}
});
}

return request;
};
9 changes: 4 additions & 5 deletions lib/streaming-api-connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@ var util = require('util');

var helpers = require('./helpers')
var Parser = require('./parser');
var request = require('request');
var request = require('./request');

var STATUS_CODES_TO_ABORT_ON = require('./settings').STATUS_CODES_TO_ABORT_ON

var StreamingAPIConnection = function (reqOpts, twitOptions) {
this.reqOpts = reqOpts
this.twitOptions = twitOptions
var StreamingAPIConnection = function (config) {
this._twitter_time_minus_local_time_ms = 0
this._request = request(config)
EventEmitter.call(this)
}

Expand Down Expand Up @@ -66,7 +65,7 @@ StreamingAPIConnection.prototype._startPersistentConnection = function () {
self._resetStallAbortTimeout();
self._setOauthTimestamp();
this.reqOpts.encoding = 'utf8'
self.request = request.post(this.reqOpts);
this.request = self._request.post(this.reqOpts);
self.emit('connect', self.request);
self.request.on('response', function (response) {
self._updateOauthTimestampOffsetFromResponse(response)
Expand Down
100 changes: 77 additions & 23 deletions lib/twitter.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//
var assert = require('assert');
var Promise = require('bluebird');
var request = require('request');
var request = require('./request');
var util = require('util');
var endpoints = require('./endpoints');
var FileUploader = require('./file_uploader');
Expand Down Expand Up @@ -57,6 +57,7 @@ var Twitter = function (config) {

this._validateConfigOrThrow(config);
this.config = config;
this._request = request(config);
this._twitter_time_minus_local_time_ms = 0;
}

Expand Down Expand Up @@ -104,23 +105,9 @@ Twitter.prototype.request = function (method, path, params, callback) {
self._updateClockOffsetFromResponse(resp);
var peerCertificate = resp && resp.socket && resp.socket.getPeerCertificate();

if (self.config.trusted_cert_fingerprints && peerCertificate) {
if (!resp.socket.authorized) {
// The peer certificate was not signed by one of the authorized CA's.
var authErrMsg = resp.socket.authorizationError.toString();
var err = helpers.makeTwitError('The peer certificate was not signed; ' + authErrMsg);
_returnErrorToUser(err);
return;
}
var fingerprint = peerCertificate.fingerprint;
var trustedFingerprints = self.config.trusted_cert_fingerprints;
if (trustedFingerprints.indexOf(fingerprint) === -1) {
var errMsg = util.format('Certificate untrusted. Trusted fingerprints are: %s. Got fingerprint: %s.',
trustedFingerprints.join(','), fingerprint);
var err = new Error(errMsg);
_returnErrorToUser(err);
return;
}
if (err) {
_returnErrorToUser(err);
return;
}

if (callback && typeof callback === 'function') {
Expand Down Expand Up @@ -317,7 +304,8 @@ Twitter.prototype._buildReqOpts = function (method, path, params, isStreaming, c
* @return {Undefined}
*/
Twitter.prototype._doRestApiRequest = function (reqOpts, twitOptions, method, callback) {
var request_method = request[method.toLowerCase()];
var self = this;
var request_method = this._request[method.toLowerCase()];
var req = request_method(reqOpts);

var body = '';
Expand Down Expand Up @@ -352,8 +340,28 @@ Twitter.prototype._doRestApiRequest = function (reqOpts, twitOptions, method, ca
callback(err, body, response)
}

req.on('response', function (res) {
response = res
req.on('response', function (resp) {
response = resp

if (self.config.trusted_cert_fingerprints) {
if (!resp.socket.authorized) {
// The peer certificate was not signed by one of the authorized CA's.
var authErrMsg = resp.socket.authorizationError.toString();
var err = helpers.makeTwitError('The peer certificate was not signed; ' + authErrMsg);
callback(err, body, response);
return;
}
var fingerprint = resp.socket.getPeerCertificate().fingerprint;
var trustedFingerprints = self.config.trusted_cert_fingerprints;
if (trustedFingerprints.indexOf(fingerprint) === -1) {
var errMsg = util.format('Certificate untrusted. Trusted fingerprints are: %s. Got fingerprint: %s.',
trustedFingerprints.join(','), fingerprint);
var err = new Error(errMsg);
callback(err, body, response);
return;
}
}

// read data from `request` object which contains the decompressed HTTP response body,
// `response` is the unmodified http.IncomingMessage object which may contain compressed data
req.on('data', function (chunk) {
Expand Down Expand Up @@ -397,7 +405,7 @@ Twitter.prototype.stream = function (path, params) {
var self = this;
var twitOptions = (params && params.twit_options) || {};

var streamingConnection = new StreamingAPIConnection()
var streamingConnection = new StreamingAPIConnection(this.config)
self._buildReqOpts('POST', path, params, true, function (err, reqOpts) {
if (err) {
// we can get an error if we fail to obtain a bearer token or construct reqOpts
Expand Down Expand Up @@ -429,7 +437,53 @@ Twitter.prototype._getBearerToken = function (callback) {
return callback(null, self._bearerToken)
}

helpers.getBearerToken(self.config.consumer_key, self.config.consumer_secret,
/**
* Get a bearer token for OAuth2
* @param {String} consumer_key
* @param {String} consumer_secret
* @param {Function} cb
*
* Calls `cb` with Error, String
*
* Error (if it exists) is guaranteed to be Twit error-formatted.
* String (if it exists) is the bearer token received from Twitter.
*/
var getBearerToken = function (consumer_key, consumer_secret, cb) {
// use OAuth 2 for app-only auth (Twitter requires this)
// get a bearer token using our app's credentials
var b64Credentials = new Buffer(consumer_key + ':' + consumer_secret).toString('base64');
request.post({
url: endpoints.API_HOST + 'oauth2/token',
headers: {
'Authorization': 'Basic ' + b64Credentials,
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
},
body: 'grant_type=client_credentials',
json: true,
}, function (err, res, body) {
if (err) {
var error = exports.makeTwitError(err.toString());
exports.attachBodyInfoToError(error, body);
return cb(error, body, res);
}

if ( !body ) {
var error = exports.makeTwitError('Not valid reply from Twitter upon obtaining bearer token');
exports.attachBodyInfoToError(error, body);
return cb(error, body, res);
}

if (body.token_type !== 'bearer') {
var error = exports.makeTwitError('Unexpected reply from Twitter upon obtaining bearer token');
exports.attachBodyInfoToError(error, body);
return cb(error, body, res);
}

return cb(err, body.access_token);
})
}

getBearerToken(self.config.consumer_key, self.config.consumer_secret,
function (err, bearerToken) {
if (err) {
// return the fully-qualified Twit Error object to caller
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"dependencies": {
"bluebird": "^3.1.5",
"mime": "^1.3.4",
"request": "^2.68.0"
"request": "^2.68.0",
"socksv5": "0.0.6"
},
"devDependencies": {
"async": "0.2.9",
Expand Down
6 changes: 1 addition & 5 deletions tests/rest.js
Original file line number Diff line number Diff line change
Expand Up @@ -640,9 +640,6 @@ describe('REST API', function () {
assert(err.message.match(/token/))
assert(err.twitterReply)
assert(err.allErrors)
assert(res)
assert(res.headers)
assert.equal(res.statusCode, 401)
done()
})
})
Expand Down Expand Up @@ -680,8 +677,7 @@ describe('REST API', function () {
return fakeRequest
}

var request = require('request')
var stubGet = sinon.stub(request, 'get', stubGet)
var stubGet = sinon.stub(twit._request, 'get', stubGet)

twit.get('account/verify_credentials', function (err, reply, res) {
assert(err === fakeError)
Expand Down
Loading

0 comments on commit ce64328

Please sign in to comment.