反相代理可用于解决多个服务器,或者隐藏服务器。
例如之前考虑的穷人版分布式:我在全国部署了多台迷你服务器,通过中心服务器(反向代理)的中转,确定需要提供的服务器,让真正服务器与客户端连接。
package main
import (
"bytes"
"encoding/json"
"io/ioutil"
"log"
"net/http"
"net/http/httputil"
"net/url"
"strings"
)
const PORT = "1330"
const A_CONDITION_URL = "http://localhost:1331"
const B_CONDITION_URL = "http://localhost:1332"
const DEFAULT_CONDITION_URL = "http://localhost:1333"
type requestPayloadStruct struct {
ProxyCondition string `json:"proxy_condition"`
}
// Get the port to listen on
func getListenAddress() string {
return ":" + PORT
}
// Log the env variables required for a reverse proxy
func logSetup() {
a_condtion_url := A_CONDITION_URL
b_condtion_url := B_CONDITION_URL
default_condtion_url := DEFAULT_CONDITION_URL
log.Printf("Server will run on: %s\n", getListenAddress())
log.Printf("Redirecting to A url: %s\n", a_condtion_url)
log.Printf("Redirecting to B url: %s\n", b_condtion_url)
log.Printf("Redirecting to Default url: %s\n", default_condtion_url)
}
// Get a json decoder for a given requests body
func requestBodyDecoder(request *http.Request) *json.Decoder {
// Read body to buffer
body, err := ioutil.ReadAll(request.Body)
if err != nil {
log.Printf("Error reading body: %v", err)
panic(err)
}
// Because go lang is a pain in the ass if you read the body then any susequent calls
// are unable to read the body again....
request.Body = ioutil.NopCloser(bytes.NewBuffer(body))
return json.NewDecoder(ioutil.NopCloser(bytes.NewBuffer(body)))
}
// Parse the requests body
func parseRequestBody(request *http.Request) requestPayloadStruct {
decoder := requestBodyDecoder(request)
var requestPayload requestPayloadStruct
err := decoder.Decode(&requestPayload)
if err != nil {
panic(err)
}
return requestPayload
}
// Log the typeform payload and redirect url
func logRequestPayload(requestionPayload requestPayloadStruct, proxyUrl string) {
log.Printf("proxy_condition: %s, proxy_url: %s\n", requestionPayload.ProxyCondition, proxyUrl)
}
// Get the url for a given proxy condition
func getProxyUrl(proxyConditionRaw string) string {
proxyCondition := strings.ToUpper(proxyConditionRaw)
a_condtion_url := A_CONDITION_URL
b_condtion_url := B_CONDITION_URL
default_condtion_url := DEFAULT_CONDITION_URL
if proxyCondition == "A" {
return a_condtion_url
}
if proxyCondition == "B" {
return b_condtion_url
}
return default_condtion_url
}
// Serve a reverse proxy for a given url
func serveReverseProxy(target string, res http.ResponseWriter, req *http.Request) {
// parse the url
url, _ := url.Parse(target)
// create the reverse proxy
proxy := httputil.NewSingleHostReverseProxy(url)
// Update the headers to allow for SSL redirection
//req.URL.Host = url.Host
//req.URL.Scheme = url.Scheme
//req.Header.Set("X-Forwarded-Host", req.Header.Get("Host"))
//req.Host = url.Host
// Note that ServeHttp is non blocking and uses a go routine under the hood
proxy.ServeHTTP(res, req)
}
// Given a request send it to the appropriate url
func handleRequestAndRedirect(res http.ResponseWriter, req *http.Request) {
requestPayload := parseRequestBody(req)
url := getProxyUrl(requestPayload.ProxyCondition)
logRequestPayload(requestPayload, url)
serveReverseProxy(url, res, req)
}
func main() {
// Log setup values
logSetup()
// start server
http.HandleFunc("/", handleRequestAndRedirect)
if err := http.ListenAndServe(getListenAddress(), nil); err != nil {
panic(err)
}
}