Skip to content

Instantly share code, notes, and snippets.

@fionera
Created September 17, 2021 13:50
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fionera/1cbcba5b24a5acf796014713fccf30ba to your computer and use it in GitHub Desktop.
Save fionera/1cbcba5b24a5acf796014713fccf30ba to your computer and use it in GitHub Desktop.
package main
import (
"bufio"
"crypto/tls"
"io"
"log"
"net"
"net/url"
"os"
"reflect"
"unsafe"
"github.com/valyala/fasthttp"
)
var (
upstreamURL string
hopHeaders = []string{
"Connection",
"Proxy-Connection", // non-standard but still sent by libcurl and rejected by e.g. google
"Keep-Alive",
"Proxy-Authenticate",
"Proxy-Authorization",
"Te", // canonicalized version of "TE"
"Trailer", // not Trailers per URL above; https://www.rfc-editor.org/errata_search.php?eid=4522
"Transfer-Encoding",
"Upgrade",
}
client = &fasthttp.Client{}
)
func init() {
upstream, err := url.Parse(os.Getenv("UPSTREAM"))
if err != nil {
panic(err)
}
if upstream.Scheme == "https" {
client.TLSConfig = &tls.Config{
InsecureSkipVerify: true,
}
}
upstreamURL = upstream.String()
}
func handler(requestCtx *fasthttp.RequestCtx) {
sourceReq := fasthttp.AcquireRequest()
defer fasthttp.ReleaseRequest(sourceReq)
sourceResp := fasthttp.AcquireResponse()
requestCtx.Request.Header.CopyTo(&sourceReq.Header)
for _, h := range hopHeaders {
sourceReq.Header.Del(h)
}
ip := requestCtx.RemoteAddr().(*net.TCPAddr).IP.String()
sourceReq.Header.Set("Cf-Connecting-Ip", ip)
sourceReq.Header.Add("X-Forwarded-For", ip)
sourceReq.Header.Set("X-Real-Ip", ip)
sourceReq.SetBody(requestCtx.PostBody())
sourceReq.SetRequestURIBytes(append(s2b(upstreamURL[:len(upstreamURL)-1]), requestCtx.RequestURI()...))
sourceResp.ReturnBodyReader = true
if err := client.Do(sourceReq, sourceResp); err != nil {
requestCtx.Error(err.Error(), fasthttp.StatusBadGateway)
return
}
sourceResp.Header.CopyTo(&requestCtx.Response.Header)
for _, h := range hopHeaders {
requestCtx.Response.Header.Del(h)
}
requestCtx.Response.SetBodyStreamWriter(func(w *bufio.Writer) {
_, err := io.Copy(w, sourceResp.BodyReader)
if err != nil {
if err != io.EOF {
return
}
log.Println(err)
return
}
if err := w.Flush(); err != nil {
log.Println(err)
}
if err := sourceResp.BodyReader.Close(); err != nil {
return
}
fasthttp.ReleaseBodyReader(sourceResp.BodyReader)
fasthttp.ReleaseResponse(sourceResp)
})
}
func s2b(s string) []byte {
sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
bh := reflect.SliceHeader{
Data: sh.Data,
Len: sh.Len,
Cap: sh.Len,
}
return *(*[]byte)(unsafe.Pointer(&bh))
}
func main() {
listenAddr := os.Getenv("LISTEN_ADDR")
listenPort := os.Getenv("LISTEN_PORT")
if err := fasthttp.ListenAndServe(listenAddr+":"+listenPort, handler); err != nil {
panic(err)
}
}
module fastproxy
go 1.17
require (
github.com/klauspost/compress v1.13.4 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.30.0
)
replace github.com/valyala/fasthttp v1.30.0 => github.com/erikdubbelboer/fasthttp v0.0.0-20181218124854-68eb43fe4409
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment