Created
March 22, 2017 07:24
-
-
Save leearmstrong/b5e09b76df484fbdf1f7a47883d38c83 to your computer and use it in GitHub Desktop.
Golang reverse proxy
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"bytes" | |
"io" | |
"io/ioutil" | |
"log" | |
"net" | |
"net/http" | |
"sync" | |
"time" | |
) | |
type servers struct { | |
lastIndex int | |
servers []string | |
mutex sync.RWMutex | |
} | |
var loadBalancer servers | |
func (s *servers) addServer(serverAddress string) { | |
s.mutex.Lock() | |
s.servers = append(s.servers, serverAddress) | |
s.mutex.Unlock() | |
} | |
func (s *servers) getNextServer() string { | |
var currentServer string | |
s.mutex.Lock() | |
currentServer = s.servers[s.lastIndex] | |
s.lastIndex++ | |
if s.lastIndex == len(s.servers) { | |
s.lastIndex = 0 | |
} | |
s.mutex.Unlock() | |
return currentServer | |
} | |
func main() { | |
loadBalancer.addServer("10.0.1.81") | |
loadBalancer.addServer("10.0.1.82") | |
loadBalancer.addServer("10.0.1.83") | |
http.DefaultTransport.(*http.Transport).MaxIdleConnsPerHost = 1000 | |
http.HandleFunc("/", handler) | |
http.ListenAndServe(":80", nil) | |
} | |
func handleDuplicatedRequest(req *http.Request) { | |
} | |
func handler(w http.ResponseWriter, req *http.Request) { | |
_, productionRequest := duplicateRequest(req) | |
//maxTries := 3 | |
// Push the handling of the duplicated request off to another goroutine | |
//go handleDuplicatedRequest(alternateRequest) | |
err := proxyRequest(w, productionRequest) | |
if err != nil { | |
log.Println(err, productionRequest.URL) | |
} | |
} | |
func proxyRequest(w http.ResponseWriter, productionRequest *http.Request) error { | |
// Obtain the next server in the round robin list | |
currentServer := loadBalancer.getNextServer() | |
// Allow this to connect for 1 second! | |
var netTransport = &http.Transport{ | |
Dial: (&net.Dialer{ | |
Timeout: 30 * time.Second, | |
KeepAlive: 30 * time.Second, | |
}).Dial, | |
TLSHandshakeTimeout: 10 * time.Second, | |
ResponseHeaderTimeout: 10 * time.Second, | |
ExpectContinueTimeout: 1 * time.Second, | |
IdleConnTimeout: 10 * time.Second, | |
} | |
netTransport.MaxIdleConnsPerHost = 1000 | |
// Allow this to have 10 seconds to complete a request | |
var netClient = &http.Client{ | |
Transport: netTransport, | |
} | |
productionRequest.URL.Host = currentServer | |
resp, err := netClient.Do(productionRequest) | |
if err != nil { | |
log.Printf("Failed to connect to %s\n", err) | |
return err | |
} | |
defer resp.Body.Close() | |
for k, v := range resp.Header { | |
w.Header()[k] = v | |
} | |
w.WriteHeader(resp.StatusCode) | |
body, _ := ioutil.ReadAll(resp.Body) | |
w.Write(body) | |
return nil | |
} | |
type nopCloser struct { | |
io.Reader | |
} | |
func (nopCloser) Close() error { return nil } | |
func duplicateRequest(request *http.Request) (request1 *http.Request, request2 *http.Request) { | |
b1 := new(bytes.Buffer) | |
b2 := new(bytes.Buffer) | |
w := io.MultiWriter(b1, b2) | |
io.Copy(w, request.Body) | |
defer request.Body.Close() | |
request1 = &http.Request{ | |
Method: request.Method, | |
URL: request.URL, | |
Proto: request.Proto, | |
ProtoMajor: request.ProtoMajor, | |
ProtoMinor: request.ProtoMinor, | |
Header: request.Header, | |
Body: nopCloser{b1}, | |
Host: request.Host, | |
ContentLength: request.ContentLength, | |
Close: true, | |
} | |
request2 = &http.Request{ | |
Method: request.Method, | |
URL: request.URL, | |
Proto: request.Proto, | |
ProtoMajor: request.ProtoMajor, | |
ProtoMinor: request.ProtoMinor, | |
Header: request.Header, | |
Body: nopCloser{b2}, | |
Host: request.Host, | |
ContentLength: request.ContentLength, | |
Close: true, | |
} | |
if request.URL.Scheme == "" { | |
request2.URL.Scheme = "http" | |
request1.URL.Scheme = "http" | |
} | |
request1.Header.Add("X-Forwarded-For", request.RemoteAddr) | |
request2.Header.Add("X-Forwarded-For", request.RemoteAddr) | |
return | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment