Skip to content

Instantly share code, notes, and snippets.

@nimolix
Last active January 2, 2016 11:19
Show Gist options
  • Save nimolix/8295422 to your computer and use it in GitHub Desktop.
Save nimolix/8295422 to your computer and use it in GitHub Desktop.
Reverse proxy in Go in 200 lines of code
package main
// (c) http://habrahabr.ru/users/pyra/ BSD license
import (
// "encoding/json"
"fmt"
// "io"
"io/ioutil"
"log"
"time"
"net/http"
"net/url"
// "os"
// "sort"
"strconv"
"strings"
// "time"
"errors"
)
func main() {
//http.HandleFunc("/r", handle_redir)
//http.Handle("/extrahtml/", http.FileServer(http.Dir("./extrahtml/")))
http.HandleFunc("/googleXXXXXXXXXXXX.html", handle_google)
http.HandleFunc("/", handle_def)
https1 := &http.Server{
Addr: ":8443",
Handler: nil,
ReadTimeout: 20 * time.Second,
WriteTimeout: 20 * time.Second,
MaxHeaderBytes: 1 << 15,
}
go func() {
log.Fatal(https1.ListenAndServeTLS("device.crt", "device.key"))
}()
http1 := &http.Server{
Addr: ":8080",
Handler: nil,
ReadTimeout: 20 * time.Second,
WriteTimeout: 20 * time.Second,
MaxHeaderBytes: 1 << 15,
}
http1.ListenAndServe()
}
var reqcntr int
var opencntr int
func redirectPolicyFunc(req *http.Request, via []*http.Request) error {
e := errors.New("redirect")
return e
}
func handle_google(w http.ResponseWriter, r *http.Request) {
fmt.Println("google")
b, _ := ioutil.ReadFile("googleXXXXXXXXXXXXXXXX.html")
fmt.Println(len(b))
w.Write(b)
}
func handle_def(w http.ResponseWriter, r *http.Request) {
// считаем количество открытых одновременно соединений
opencntr++
defer func() {
opencntr--
}()
client := &http.Client{
CheckRedirect: redirectPolicyFunc,
}
ip := strings.Split(r.RemoteAddr, ":")[0]
// считаем примерно запросы (без мютексов)
reqcntr++
q := r.URL.RawQuery
//fmt.Println("def ", r.Method, reqcntr, q)
//r.Form, _ = url.ParseQuery(r.URL.RawQuery)
//io.WriteString(w, r.URL.Path)
path := r.URL.Path
// ID пользователя будем хранить тут
var cid int64
// пометим если запрос к ПХП странице
breporting := false
// ПХП будем обрабатывать особым образом
if strings.HasSuffix(path, ".php") {
// fmt.Println("breporting = true")
breporting = true
}
// через прокси будем запрашивать другую ПХП страницу
if path == "/ajax/main.php" {
path = "/ajax/main_hide1777.php"
}
cid = -2
if strings.HasPrefix(path, "/ajax/") || strings.HasPrefix(path, "/im/") {
// файлы в след папках содержат ID пользователя
m, err := url.ParseQuery(q)
if err == nil {
id := m.Get("xid")
if id == "" {
id = m.Get("aid")
if id == "" {
id = m.Get("cid")
if id == "" {
id = m.Get("bid")
}
}
}
cid, err = strconv.ParseInt(id, 10, 64)
if err != nil {
cid = -1
}
}
} else if strings.HasPrefix(path, "/avatar/") {
// аватарка тоже содержит ИД /avatar/1234.gif
// ID - 1234
cid = -1
pos1 := strings.Index(path[8:], ".")
if pos1 != -1 {
id := path[6 : pos1+6]
var err error
cid, err = strconv.ParseInt(id, 10, 64)
if err != nil {
cid = -1
}
}
}
// тут был ещё один else где хитро декриптились куки для определения целевого сервера
// переводим ID пользователя в домен сервера обрабатающего его запросы
//host := "test000.cloud"
host := "login.yahoo.com" // для интереса можно посматреть чем заголовок балансировшика отличается от оригинала
if cid > 1000 && cid < 5000 {
host = "prod002.cloud"
} else if cid >= 5000 && cid < 7000 {
host = "prod003.cloud"
} else if cid >= 7000 && cid < 15000 {
host = "prod005.cloud"
} else if cid >= 15000 && cid < 16000 {
host = "prod006.cloud"
} else if cid >= 25000 && cid < 34000 {
host = "prod011.cloud"
}
url := ""
// передаем реальный IP в урле так как PHP 5.3 FastCGI не умеет хедеры читать
if breporting {
url = "https://" + host + path + "?" + q + "&HEHE_IP="+ip+"&HEHE_SECRET=B87BVf5"
}else{
url = "https://" + host + path + "?" + q
}
fmt.Println(url)
// проксируем только GET запросы
if r.Method == "GET" {
req1, err := http.NewRequest("GET", url, nil)
if err != nil {
fmt.Println("Error1: ", err)
// тут чего то не хватает
}
req1.Header = r.Header
req1.Header.Add("XHEHE_REMOTE_IP", ip)
resp, _ := client.Do(req1)
StatusCode := resp.StatusCode
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error2: ", err)
// и тут ))))
// это обычно выстреливает когда редирект
}
fmt.Println("def ", r.Method, reqcntr, opencntr, url, len(body), StatusCode)
for k1, v1 := range resp.Header {
for _, v2 := range v1 {
w.Header().Add(k1, v2)
}
}
w.WriteHeader(StatusCode)
w.Write(body)
}
} // 190 строк с комментами
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment