Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add socks5 proxy #240

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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