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

Revise http parsing #30

Closed
wants to merge 5 commits into from
Closed
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
89 changes: 54 additions & 35 deletions core/Session.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
const isRequestOfMethodType = require('../lib/isRequestOfMethodType');

const tls = require('tls');
const {EVENTS, DEFAULT_KEYS, STRINGS} = require('../lib/constants');
const parseDataToObject = require('../lib/parseDataToObject');
const net = require('net');
const {request, createServer} = require('http');
const {EVENTS, DEFAULT_KEYS, HTTP_METHODS} = require('../lib/constants');
const {CLOSE, DATA, ERROR} = EVENTS;
const {BLANK, CRLF, LF, SEPARATOR} = STRINGS;
const {CONNECT} = HTTP_METHODS;

/**
* Write data of given socket
Expand Down Expand Up @@ -40,8 +43,6 @@ class Session extends Object {
this.user = null;
this.authenticated = false;
this.isHttps = false;
this._request = {};
this._response = {};
}

/**
Expand Down Expand Up @@ -84,13 +85,48 @@ class Session extends Object {
return this.authenticated;
}

_requestPromise = new Promise(resolve => this._requestPromiseResolve = resolve)

get request() {
return this._requestPromise
}

set request(val) {
this._requestPromiseResolve(val)
}

_responsePromise = new Promise(resolve => this._responsePromiseResolve = resolve)

get response() {
return this._responsePromise
}

set response(val) {
this._responsePromiseResolve(val)
}

/**
* Set the socket that will receive response
* @param {net.Socket} socket
* @returns {Session}
*/
setResponseSocket(socket) {
this._src = socket;
const mirror = new net.Socket()
this._src.prependListener('data', data => {
if (!this.request || this.request.complete) {
this._requestPromise = new Promise(resolve => this._requestPromiseResolve = resolve)
}
if (!this.isHttps || isRequestOfMethodType(CONNECT, data)) {
mirror.emit('data', data)
} else {
this._requestPromiseResolve()
}
})
createServer()
.on('connect', request => this.request = request)
.on('request', request => this.request = request)
.emit('connection', mirror)
return this;
}

Expand All @@ -101,6 +137,19 @@ class Session extends Object {
*/
setRequestSocket(socket) {
this._dst = socket;
const mirror = new net.Socket()
if (!this.isHttps) {
this._dst.prependListener('data', data => {
if (!this.response || this.response.complete) {
this._responsePromise = new Promise(resolve => this._responsePromiseResolve = resolve)
}
mirror.emit('data', data);
})
request({ createConnection: () => mirror })
.on('response', response => this.response = response);
} else {
this._responsePromiseResolve()
}
return this;
}

Expand All @@ -112,36 +161,6 @@ class Session extends Object {
return this._id;
}

set request(buffer) {
const parsedRequest = parseDataToObject(buffer);
if (parsedRequest.headers) {
this._request = parsedRequest;
}
return this._request;
}

get request() {
return this._request;
}

set response(buffer) {
// const indexOfChunkEnd = buffer.toString().indexOf(LF + CRLF);
// this._response.complete = indexOfChunkEnd; //TODO find a way to recognize last chunk

const parsedResponse = parseDataToObject(buffer, true, !!this._response.body);
if (this._response.body
&& parsedResponse.body) {
parsedResponse.body = this._response.body + parsedResponse.body;
}
this._response = {...this._response, ...parsedResponse};

return this._response;
}

get response() {
return this._response;
}

/**
* @param {string} username
* @returns {Session}
Expand Down
53 changes: 22 additions & 31 deletions core/onConnectedClientHandling.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ const getConnectionOptions = require('./getConnectionOptions');
const rebuildHeaders = require('../lib/rebuildHeaders');
const isFunction = require('../lib/isFunction');
const usingUpstreamToProxy = require('../lib/usingUpstreamToProxy');
const getFirstHeaderRow = require('../lib/getFirstHeaderRow');
const isRequestOfMethodType = require('../lib/isRequestOfMethodType');

const {
EVENTS,
Expand All @@ -13,7 +15,7 @@ const {
} = require('../lib/constants');

const {CLOSE, DATA, ERROR, EXIT} = EVENTS;
const {ETIMEDOUT, ENOTFOUND, EPIPE, EPROTO} = ERROR_CODES;
const {ETIMEDOUT, ENOTFOUND, EPIPE} = ERROR_CODES;
const {CONNECT} = HTTP_METHODS;
const {AUTH_REQUIRED, OK, NOT_OK, TIMED_OUT, NOT_FOUND} = HTTP_RESPONSES;
const {BLANK, CRLF, EMPTY, SEPARATOR, PROXY_AUTH, PROXY_AUTH_BASIC} = STRINGS;
Expand Down Expand Up @@ -84,8 +86,6 @@ module.exports = function onConnectedClientHandling(clientSocket, bridgedConnect
async function onDataFromUpstream(dataFromUpStream) {
const thisTunnel = bridgedConnections[remoteID];
if (thisTunnel) {
thisTunnel.response = dataFromUpStream;

const responseData = isFunction(injectResponse)
? await injectResponse(dataFromUpStream, thisTunnel)
: dataFromUpStream;
Expand All @@ -102,8 +102,6 @@ module.exports = function onConnectedClientHandling(clientSocket, bridgedConnect
async function onDirectConnectionOpen(srcData) {
const thisTunnel = bridgedConnections[remoteID];
if (thisTunnel) {
thisTunnel.request = srcData;

const requestData = isFunction(injectData)
? await injectData(srcData, thisTunnel)
: srcData;
Expand Down Expand Up @@ -133,9 +131,9 @@ module.exports = function onConnectedClientHandling(clientSocket, bridgedConnect
* @param {boolean} isConnectMethod - false as default.
* @returns Promise{boolean|{host: string, port: number, protocol: string, credentials: string, upstreamed: boolean}}
*/
async function prepareTunnel(data, firstHeaderRow, isConnectMethod = false) {
async function prepareTunnel(data, isConnectMethod = false) {
const thisTunnel = bridgedConnections[remoteID];
const upstreamHost = firstHeaderRow.split(BLANK)[1];
const upstreamHost = getFirstHeaderRow(data).toString().split(BLANK)[1];
const initOpt = getConnectionOptions(false, upstreamHost);

thisTunnel.setTunnelOpt(initOpt); //settings opt before callback
Expand All @@ -161,13 +159,13 @@ module.exports = function onConnectedClientHandling(clientSocket, bridgedConnect
/**
* @param {Error} connectionError
*/
function onTunnelHTTPConnectionOpen(connectionError) {
async function onTunnelHTTPConnectionOpen(connectionError) {
if (connectionError) {
return onClose(connectionError);
}

if (connectionOpt.credentials) {
const headers = thisTunnel.request.headers;
const { headers } = await thisTunnel.request;
const basedCredentials = Buffer.from(connectionOpt.credentials)
.toString('base64'); //converting to base64
headers[PROXY_AUTH.toLowerCase()] = PROXY_AUTH_BASIC + BLANK + basedCredentials;
Expand All @@ -189,7 +187,7 @@ module.exports = function onConnectedClientHandling(clientSocket, bridgedConnect
}
if (connectionOpt.upstreamed) {
if (connectionOpt.credentials) {
const headers = thisTunnel.request.headers;
const { headers } = await thisTunnel.request;
const basedCredentials = Buffer.from(connectionOpt.credentials).toString('base64'); //converting to base64
headers[PROXY_AUTH.toLowerCase()] = PROXY_AUTH_BASIC + BLANK + basedCredentials;
const newData = rebuildHeaders(headers, data);
Expand Down Expand Up @@ -224,19 +222,17 @@ module.exports = function onConnectedClientHandling(clientSocket, bridgedConnect
}

/**
* @param {Array<string>} split
* @param {buffer} data
*/
function handleProxyTunnel(split, data) {
const firstHeaderRow = split[0];
function handleProxyTunnel(data) {
const thisTunnel = bridgedConnections[remoteID];
const isConnectMethod = isRequestOfMethodType(CONNECT, data);

if (~firstHeaderRow.indexOf(CONNECT)) { //managing HTTP-Tunnel(upstream) & HTTPs
return prepareTunnel(data, firstHeaderRow, true);
if (isConnectMethod) { //managing HTTP-Tunnel(upstream) & HTTPs
return prepareTunnel(data, true);
}
else if (firstHeaderRow.indexOf(CONNECT) === -1
&& !thisTunnel._dst) { // managing http
return prepareTunnel(data, firstHeaderRow);
else if (!isConnectMethod && !thisTunnel._dst) { // managing http
return prepareTunnel(data);
}
else if (thisTunnel && thisTunnel._dst) {
return onDirectConnectionOpen(data);
Expand All @@ -248,18 +244,13 @@ module.exports = function onConnectedClientHandling(clientSocket, bridgedConnect
* @returns {Promise<Session|void>}
*/
async function onDataFromClient(data) {
const thisTunnel = bridgedConnections[remoteID];
thisTunnel.request = data;

const dataString = data.toString();

try {
if (dataString && dataString.length > 0) {
const headers = thisTunnel.request.headers;
const split = dataString.split(CRLF);

if (isFunction(auth)
if (data.length > 0) {
const thisTunnel = bridgedConnections[remoteID];
if (isRequestOfMethodType(CONNECT, data)
&& isFunction(auth)
&& !thisTunnel.isAuthenticated()) {
const { headers } = await thisTunnel.request;
const proxyAuth = headers[PROXY_AUTH.toLowerCase()];
if (proxyAuth) {
const credentials = proxyAuth
Expand All @@ -276,7 +267,7 @@ module.exports = function onConnectedClientHandling(clientSocket, bridgedConnect

if (isLogged) {
thisTunnel.setUserAuthentication(username);
return handleProxyTunnel(split, data);
return handleProxyTunnel(data);
}
else {
//return auth-error and close all
Expand All @@ -289,7 +280,7 @@ module.exports = function onConnectedClientHandling(clientSocket, bridgedConnect
}
}
else {
return handleProxyTunnel(split, data);
return handleProxyTunnel(data);
}
}
}
Expand All @@ -306,4 +297,4 @@ module.exports = function onConnectedClientHandling(clientSocket, bridgedConnect
.on(CLOSE, onClose)
.on(EXIT, onClose)
);
};
};
10 changes: 10 additions & 0 deletions lib/getFirstHeaderRow.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const {STRINGS} = require('./constants');
const {CRLF} = STRINGS;

/**
* @param {buffer} data
* @returns {buffer}
*/
module.exports = function getFirstHeaderRow(buffer) {
return buffer.subarray(0, buffer.indexOf(CRLF));
};
10 changes: 10 additions & 0 deletions lib/isRequestOfMethodType.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const getFirstHeaderRow = require('./getFirstHeaderRow');

/**
* @param {string} method
* @param {buffer} data
* @returns {boolean}
*/
module.exports = function isRequestOfMethodType(method, buffer) {
return getFirstHeaderRow(buffer).indexOf(method) > -1;
};
59 changes: 0 additions & 59 deletions lib/parseDataToObject.js

This file was deleted.