Skip to content

Commit

Permalink
Merge branch 'release/v0.3.1'
Browse files Browse the repository at this point in the history
  • Loading branch information
crodas committed Jul 30, 2015
2 parents 3ac2df1 + d6ba2d5 commit 529c898
Show file tree
Hide file tree
Showing 11 changed files with 256 additions and 37 deletions.
22 changes: 22 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
The MIT License (MIT)

Copyright (c) 2015 UberProxy

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

23 changes: 13 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,21 @@ In the cloud era we need smart proxies.
Having a proxy makes really easy to scale up or down our applications in a matter of seconds. `UberProxy` makes it possible to add and remove more workers to your application.

## Installation

```bash
npm install -g uberproxy
```

git clone git@github.com:uberproxy/uberproxy.git
cd uberproxy
npm install
## Configuration

# Create a secret token
node index.js setup
To create a new configuration you need to run the following command

## Configuration
```bash
# Create a secret token
uberproxy setup
```

`setup` will create a default configuration (usually `config.yml`).
That will generate a `config.yml` (you can override that with `-c config.json`) that would look like this:

```yaml
ssl:
Expand All @@ -61,11 +65,10 @@ port: 80
secret: 8e0c5e97f91e1a8dde85702ffadff48e8488fda46c457712920aa835dabe25c8
```
It accept an optional parameter with the path of the configuration which is `config.yml` by default. We support `YAML` and `JSON`. You can switch from YAML to JSON like this:
In order to run the server you should execute this command:
```bash
node index.js setup -c foo.json
node index.js server -c foo.json
uberproxy server [-c config.yml]
```

### Parts
Expand Down
34 changes: 34 additions & 0 deletions cli/plugins.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
var NPM = require('npm');
var Fs = require('fs');

exports.setup = function(parser) {
};

var packages = [];
function checkPackage(pkg) {
if (pkg['uberproxy-plugin']) {
packages.push([pkg.name.replace(/^uberproxy-?/i, ''), pkg.realPath]);
}
};

exports.main = function(argv) {
NPM.load(function() {
NPM.commands.list([], true, function(err, npm) {
NPM.config.set("global", true);
for (var i in npm.dependencies) {
if (npm.dependencies.hasOwnProperty(i)) {
checkPackage(npm.dependencies[i]);
}
}
NPM.commands.list([], true, function(err, npm) {
for (var i in npm.dependencies) {
if (npm.dependencies.hasOwnProperty(i)) {
checkPackage(npm.dependencies[i]);
}
}
Fs.writeFileSync(__dirname + "/../plugins/npm.js", "exports.plugin = " + JSON.stringify(packages));
console.log("Updated npm.js");
});
});
});
};
18 changes: 13 additions & 5 deletions cli/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ function main(argv) {
var Commands = require('../lib/commands');
var Config = require('../lib/config');
var Conn = require('../lib/connection');
var Plugin = require('../lib/plugin');

Config.parseArgv(argv);

Expand All @@ -60,7 +61,11 @@ function main(argv) {
console.log('worker ' + worker.process.pid + ' died');
Cluster.fork();
});


Cluster.on('online', function(worker) {
console.log("New worker [" + worker.process.pid + "]");
});

console.log("Http-Listening in port " + Config.port);
console.log("Https-Listening in port " + Config.ssl.port);
return;
Expand All @@ -69,8 +74,8 @@ function main(argv) {
console.log("Https-Listening in port " + Config.ssl.port);
}

function handleRequest(req, res) {
var conn = new Conn(req, res);
function handleRequest(protocol, req, res) {
var conn = new Conn(protocol, req, res);
if (conn.done) {
/* already handled! */
return;
Expand All @@ -81,8 +86,11 @@ function main(argv) {
}
}

Http.createServer(handleRequest).listen(Config.port);
Https.createServer(require('../lib/https').options, handleRequest).listen(Config.ssl.port);
Http.createServer(handleRequest.bind(null, 'http:')).listen(Config.port);
Https.createServer(
require('../lib/https').options,
handleRequest.bind(null, 'https:')
).listen(Config.ssl.port);
};

exports.main = main;
Expand Down
29 changes: 29 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# UberProxy

UberProxy is a **reverse proxy** written in NodeJS. It was designed to be fast and easy to configure.

## Features

1. Highly configurable
- Provides a [RESTful interface](usage/#restful-interface) to add and remove workers, SSL domains, etc.
2. Easy to extend.
- [Write plugins](plugins) in Javascript!
- Everything is a plugin internally
- [Redirect](plugins/redirect.js)
- [Logging](plugins/logs.js)
3. Fast (NodeJS is neat handling lots of I/O)
4. Efficient uploads
- The proxy buffer to disk a file upload
- When it's ready it forwards to the worker
- The Proxy deal much better with slow connections
5. Throttle connections to workers (by default 20 per worker)
6. SSL support
7. URL sanitization
- `//foobar///` will be rewrite to `/foobar` before forwarding the app
8. The workers are in control of everything:
- Rewrite hostname
- Rewrite URL
- Expose URL (with regular expressions) they can work
- If a worker can serve `^/(foo|bar)/.+`, any other request will generate a `404 Error page` in the proxy itself.
- They can choose which plugins to use (Global plugins may apply any ways)

28 changes: 28 additions & 0 deletions docs/setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Setup

## Instalation

UberProxy can be installed easily with npm.

```bash
npm install -g uberproxy
```

If you wish to have the latest code you can install it from github as well:

```bash
git clone https://github.com/uberproxy/uberproxy.git
cd uberproxy
npm install
ln -s `pwd`/index.js /usr/local/bin/uberproxy
```

## Configuration

UberProxy needs a **configuration file**. To make things simple `uberproxy setup` will create a configuration file in the current directory.

```bash
uberproxy setup
# or
ubeproxy setup -c configuration.json
```
66 changes: 66 additions & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Usage

## Run

```bash
uberproxy server
```

It will run a bare proxy, most requests will return a `Not Found` error. We must add workers for it to work.

## RESTful interface

The protocol is pretty simple, it can be impemented in any language which has a `http` client library.

```
POST /_uberproxy/register HTTP/1.1
Host: foobar.com
X-Auth: sha256($secret + "\0" + "register" + "\0" + "{json_data}")
{json_data}
```

The `X-Auth` is a `sha256` hash which ensures the URI and `json_data` are legit. The `$secret` value is defined in the configuration file (usually `config.yml`).

### PHP Client
Our [PHP](https://github.com/uberproxy/php-sdk) client implements the restful interface and provides a set of really simple classes to configurate UberProxy on demand. Think of it as a `Makefile` for deploying your application, once your application is ready, you execute a PHP file which describes your application to the proxy.

#### Installation

```bash
composer require uberproxy/php-client
```

#### Usage

```php
$client = new UberProxy\Client("my.proxy.com", $secret);

$client->register()
->worker($my_ip_address)
->addHost('myrealappdomain.com')
->enablePlugin('redirect')
->extra([
'redirectTo' => ['http://www.myrealappdomain.com', 301],
])->send();

$client->register()
->worker($my_ip_address)
->addHost('www.myrealappdomain.com')
->addRoute("/")
->addRoute("/about")
->addRegexRoute('/(foo|bar)/.+')
->rewriteRoute("/(foo|bar)/(.+)", "index.php?action=$1&extra=$2")
->send();
```

The first register is listening for requests for `myrealappdomain.com` and enables the plugin `redirect`. In the extra section it gives the data the plugin needs (in this case, redirect to `www.myrealappdomain.com` with a 301 code).

The second register is registering an application for `www.myrealappdomain.com` with the following descriptions:

1. Forward only routes `/`, `/about` and `/(foo|bar)/.+`. Any other request would return a `404` error page in the proxy itself.
2. Rewrite `/foo/something/extra?q=1` to `index.php?q=1&action=foo&extra=something/extra`




24 changes: 14 additions & 10 deletions lib/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ var Fs = require('fs');
var Http = require('http');
var Proxy = require('./proxy');

function Connection(req, res, proxy)
function Connection(protocol, req, res, proxy)
{
this.protocol = protocol;
this.headers = req.headers;
this.slotid = 0xffff;
this.reqid = UUID.v4();
Expand All @@ -17,6 +18,7 @@ function Connection(req, res, proxy)
this.ip = req.connection.remoteAddress;
this.req = req;
this.res = res;
this.client_res = res;
this.done = false;
this.sanitizeUrl();

Expand All @@ -32,7 +34,7 @@ Connection.prototype._err_handler = function(e) {
this.statusCode = 500;
this.headers = {};
this._emit_response();
this.res.end(Proxy.htmls['error'] || "Error");
this.client_res.end(Proxy.htmls['error'] || "Error");
this._end_request();
if (++this.worker.errors >= 10) {
// so long and thanks for all the fish!
Expand All @@ -44,8 +46,9 @@ Connection.prototype._req_handler = function(_res) {
_res.on('end', this._end_request.bind(this));
this.statusCode = _res.statusCode;
this.headers = _res.headers;
this.res = _res;
this._emit_response();
_res.pipe(this.res);
_res.pipe(this.client_res);
};

Connection.prototype.getHttpClient = function() {
Expand Down Expand Up @@ -100,9 +103,10 @@ Connection.prototype.forwardTo = function(worker) {

Connection.prototype.sanitizeUrl = function() {
var url = URL.parse(this.req.url);
this.pathname = url.pathname.replace(/\/+/g, '/').replace(/\/$/, '');
this.url = this.pathname + (url.search||"");
this.req.url = this.url
this.pathname = url.pathname.replace(/\/+/g, '/').replace(/\/$/, '');
this.url = this.pathname + (url.search||"");
this.req.url = this.url
this.URL = this.protocol + "//" + this.host + this.url;
};

Connection.prototype._emit_response = function() {
Expand All @@ -113,22 +117,22 @@ Connection.prototype._emit_response = function() {
this.worker.emit('response', this);
}
Proxy.emit('response', this);
this.res.writeHeader(this.statusCode, this.headers);
return this.res;
this.client_res.writeHeader(this.statusCode, this.headers);
return this.client_res;
};

Connection.prototype.response = function(code, headers) {
this.headers = headers || {};
this.statusCode = code || 200;
this._emit_response();
return this.res;
return this.client_res;
};

Connection.prototype.json = function(obj, code) {
this.statusCode = code || 200;
this.headers = {};
this._emit_response();
this.res.end(JSON.stringify(obj));
this.client_res.end(JSON.stringify(obj));
};

module.exports = Connection;
Loading

0 comments on commit 529c898

Please sign in to comment.