Skip to content

Commit

Permalink
-
Browse files Browse the repository at this point in the history
  • Loading branch information
geosoft1 committed Apr 11, 2019
1 parent 5155e76 commit 1a70535
Show file tree
Hide file tree
Showing 6 changed files with 346 additions and 66 deletions.
3 changes: 3 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
1.0.1
- some minor bugs corrections

2.0.0
- code rewritten
- https server added
91 changes: 66 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,54 +1,95 @@
reverseproxy
====
[![version](https://img.shields.io/badge/version-1.0.1-blue.svg)](https://github.com/geosoft1/reverseproxy/archive/master.zip)
[![version](https://img.shields.io/badge/version-2.0.0-blue.svg)](https://github.com/geosoft1/reverseproxy/archive/master.zip)
[![license](https://img.shields.io/badge/license-gpl-blue.svg)](https://github.com/geosoft1/reverseproxy/blob/master/LICENSE)

Simple [reverse proxy](https://en.wikipedia.org/wiki/Reverse_proxy) server. Useful for accessing web applications on various servers (or VMs) through a single domain.

### How it works?
## How it works?

![reverseproxy](https://user-images.githubusercontent.com/6298396/36028867-5e549ea4-0da9-11e8-8ecf-62546e95ca5c.png)

Just complete the `conf.json` file and run the server. Example:

{
"ip":"",
"port":"8080",
"routes":{
"/upload":"192.168.88.160:8080",
"/Downloads/":"192.168.88.164:8000",
"#":"the pattern / matches all paths not matched by other registered patterns",
"/":"192.168.88.161"
}
}
{
"routes": {
"#": "the pattern / matches all paths not matched by other registered patterns",
"/": "http://192.168.88.250",
"/wrong": "192.168.88.250:8080",
"/upload": "http://192.168.88.250:8080",
"/hello": "https://192.168.88.250:8090",
"/static/": "http://192.168.88.250:8080",
"#/disabled": "192.168.88.250:8080"
}
}

## Configuration details
## Getting started

"ip":"",
To compile the reverse proxy server use

No ip mean `localhost` on hosting server. Is no need to change this.
go build

"port":"8080",
If you still want just an HTTP reverse proxy, compile with

The server listening on this port. Remeber to forward the port `80` to this port if your connection pass through a router. No root right are required if you run on big ports (eg. `8080`).
go build http.go

or for HTTPS

go build https.go

Parameters

### `-conf`

Start program with a certain configuration file. Default `conf.json`.

### `-http`

Listening address and port for HTTP server. Default `:8080`.

### `-https`

Listening address and port for HTTPS server. Default `:8090`.

### `-https-enabled`

Enable HTTPS server. Default `false`.

### `-verbose`

Enable verbose mode for middleware.

## Routes

Routes has the folowing structure

"path":"target"
"path":"host"

The path is what you request and the host is what you get. The reverse proxy always add the path to the host (eg. if your host address is `example.com` then the path `/` mean `example.com/` and `/upload` mean `example.com/upload`).

Paths starting with `#` are comments and are not added to routes.

A path like `/name/` match any request starting with `name` (eg. `/api/` match also `/api/bla` and so on).

Hosts must be a complete url address and port.

Do not repeat the routes because the server will take always the last route to a host.

## Testing the server

The path is what you request and the target is what you get (eg. if your domain is `example.com` then `/` mean `example.com/` and `/upload` mean `example.com/upload`).
curl --verbose http://localhost:8080/hello

`#` path mean a comment and is not added to routes. Put the text in target. `#something` don't mean a comment.
For HTTPS use

The reverse proxy add your path to the target, so be prepared to handle this path. For example the folowing will get an error page.
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout server.key -out server.crt
curl --insecure --verbose https://localhost:8090/hello

"/upload":"google.com"
## Faq

Use `/` path for main site which have index page on `/`. Use sufixes for other web services which have the sufix as main page.
### Why the HTTPS is not enabled by default?

Remeber that a route like `/name/` mean match any starting with `name` (eg. `/api/` match also `/api/bla` and so on).
HTTPS server need some valid certificates which you may not have. If you need only a HTTP server is no reason to generate cerificates just to run the program.

Do not repeat the routes because the server will take always the last route to a target.
### Should I use http or https in the host address?

Yes, prefixes are mandatory to tell the server in which chain to put the route. Omitting that will skip the route.
21 changes: 10 additions & 11 deletions conf.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
{
"ip":"",
"port":"8080",
"routes":{
"/upload":"192.168.88.160:8080",
"#/files/":"192.168.88.160:8080",
"/Downloads/":"192.168.88.164:8000",
"/img":"192.168.88.161:8081",
"#":"the pattern / matches all paths not matched by other registered patterns",
"/":"192.168.88.161"
}
}
"routes": {
"#": "the pattern / matches all paths not matched by other registered patterns",
"/": "http://192.168.88.250",
"/wrong": "192.168.88.250:8080",
"/upload": "http://192.168.88.250:8080",
"/hello": "https://192.168.88.250:8090",
"/static/": "http://192.168.88.250:8080",
"#/disabled": "192.168.88.250:8080"
}
}
99 changes: 99 additions & 0 deletions http.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// simple http reverse proxy
// Copyright (C) 2017-2019 geosoft1 geosoft1@gmail.com
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

// +build http

package main

import (
"encoding/json"
"flag"
"fmt"
"log"
"net/http"
"net/http/httputil"
"net/url"
"os"
"path/filepath"
"strings"
)

var configFile = flag.String("conf", "conf.json", "configuration file")
var httpAddress = flag.String("http", ":8080", "http address")
var verbose = flag.Bool("verbose", false, "explain what is being done")

var config map[string]interface{}

func NewReverseProxy(scheme, host string) *httputil.ReverseProxy {
return httputil.NewSingleHostReverseProxy(&url.URL{
Scheme: scheme,
Host: host,
})
}

func Register(p *httputil.ReverseProxy) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
if *verbose {
log.Printf("request %s%s", r.RemoteAddr, r.RequestURI)
}
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Headers", "X-Requested-With")
p.ServeHTTP(w, r)
}
}

func main() {
flag.Usage = func() {
fmt.Printf("usage: %s [options]\n", filepath.Base(os.Args[0]))
flag.PrintDefaults()
}
flag.Parse()
log.SetFlags(log.LstdFlags | log.Lshortfile)

folder, err := filepath.Abs(filepath.Dir(os.Args[0]))
if err != nil {
log.Fatalln(err)
}

file, err := os.Open(filepath.Join(folder, *configFile))
if err != nil {
log.Fatalln(err)
}

if err := json.NewDecoder(file).Decode(&config); err != nil {
log.Fatalln(err)
}

for path, host := range config["routes"].(map[string]interface{}) {
log.Printf("%s -> %s", path, host)
if strings.HasPrefix(path, "#") {
// skip comments
continue
}
u, err := url.Parse(host.(string))
if err != nil {
// skip invalid hosts
log.Println(err)
continue
}
http.HandleFunc(path, Register(NewReverseProxy(u.Scheme, u.Host)))
}

log.Printf("start http server on %s", *httpAddress)
if err := http.ListenAndServe(*httpAddress, nil); err != nil {
log.Fatalln(err)
}
}
103 changes: 103 additions & 0 deletions https.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// simple https reverse proxy
// Copyright (C) 2017-2019 geosoft1 geosoft1@gmail.com
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

// +build https

package main

import (
"crypto/tls"
"encoding/json"
"flag"
"fmt"
"log"
"net/http"
"net/http/httputil"
"net/url"
"os"
"path/filepath"
"strings"
)

var configFile = flag.String("conf", "conf.json", "configuration file")
var httpsAddress = flag.String("https", ":8090", "https address")
var verbose = flag.Bool("verbose", false, "explain what is being done")

var config map[string]interface{}

func NewReverseProxy(scheme, host string) *httputil.ReverseProxy {
return httputil.NewSingleHostReverseProxy(&url.URL{
Scheme: scheme,
Host: host,
})
}

func Register(p *httputil.ReverseProxy) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
if *verbose {
log.Printf("request %s%s", r.RemoteAddr, r.RequestURI)
}
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Headers", "X-Requested-With")
p.ServeHTTP(w, r)
}
}

func main() {
flag.Usage = func() {
fmt.Printf("usage: %s [options]\n", filepath.Base(os.Args[0]))
flag.PrintDefaults()
}
flag.Parse()
log.SetFlags(log.LstdFlags | log.Lshortfile)

folder, err := filepath.Abs(filepath.Dir(os.Args[0]))
if err != nil {
log.Fatalln(err)
}

file, err := os.Open(filepath.Join(folder, *configFile))
if err != nil {
log.Fatalln(err)
}

if err := json.NewDecoder(file).Decode(&config); err != nil {
log.Fatalln(err)
}

for path, host := range config["routes"].(map[string]interface{}) {
log.Printf("%s -> %s", path, host)
if strings.HasPrefix(path, "#") {
// skip comments
continue
}
u, err := url.Parse(host.(string))
if err != nil {
// skip invalid hosts
log.Println(err)
continue
}
http.HandleFunc(path, Register(NewReverseProxy(u.Scheme, u.Host)))
}

// allow you to use self signed certificates
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
// openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout server.key -out server.crt
log.Printf("start https server on %s", *httpsAddress)
if err := http.ListenAndServeTLS(*httpsAddress, filepath.Join(folder, "server.crt"), filepath.Join(folder, "server.key"), nil); err != nil {
log.Fatalln(err)
}
}
Loading

0 comments on commit 1a70535

Please sign in to comment.