Last active
January 2, 2016 11:19
-
-
Save nimolix/8295422 to your computer and use it in GitHub Desktop.
Reverse proxy in Go in 200 lines of code
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 | |
// (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