-
-
Save yowu/f7dc34bd4736a65ff28d to your computer and use it in GitHub Desktop.
package main | |
import ( | |
"flag" | |
"io" | |
"log" | |
"net" | |
"net/http" | |
"strings" | |
) | |
// Hop-by-hop headers. These are removed when sent to the backend. | |
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html | |
var hopHeaders = []string{ | |
"Connection", | |
"Keep-Alive", | |
"Proxy-Authenticate", | |
"Proxy-Authorization", | |
"Te", // canonicalized version of "TE" | |
"Trailers", | |
"Transfer-Encoding", | |
"Upgrade", | |
} | |
func copyHeader(dst, src http.Header) { | |
for k, vv := range src { | |
for _, v := range vv { | |
dst.Add(k, v) | |
} | |
} | |
} | |
func delHopHeaders(header http.Header) { | |
for _, h := range hopHeaders { | |
header.Del(h) | |
} | |
} | |
func appendHostToXForwardHeader(header http.Header, host string) { | |
// If we aren't the first proxy retain prior | |
// X-Forwarded-For information as a comma+space | |
// separated list and fold multiple headers into one. | |
if prior, ok := header["X-Forwarded-For"]; ok { | |
host = strings.Join(prior, ", ") + ", " + host | |
} | |
header.Set("X-Forwarded-For", host) | |
} | |
type proxy struct { | |
} | |
func (p *proxy) ServeHTTP(wr http.ResponseWriter, req *http.Request) { | |
log.Println(req.RemoteAddr, " ", req.Method, " ", req.URL) | |
if req.URL.Scheme != "http" && req.URL.Scheme != "https" { | |
msg := "unsupported protocal scheme "+req.URL.Scheme | |
http.Error(wr, msg, http.StatusBadRequest) | |
log.Println(msg) | |
return | |
} | |
client := &http.Client{} | |
//http: Request.RequestURI can't be set in client requests. | |
//http://golang.org/src/pkg/net/http/client.go | |
req.RequestURI = "" | |
delHopHeaders(req.Header) | |
if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil { | |
appendHostToXForwardHeader(req.Header, clientIP) | |
} | |
resp, err := client.Do(req) | |
if err != nil { | |
http.Error(wr, "Server Error", http.StatusInternalServerError) | |
log.Fatal("ServeHTTP:", err) | |
} | |
defer resp.Body.Close() | |
log.Println(req.RemoteAddr, " ", resp.Status) | |
delHopHeaders(resp.Header) | |
copyHeader(wr.Header(), resp.Header) | |
wr.WriteHeader(resp.StatusCode) | |
io.Copy(wr, resp.Body) | |
} | |
func main() { | |
var addr = flag.String("addr", "127.0.0.1:8080", "The addr of the application.") | |
flag.Parse() | |
handler := &proxy{} | |
log.Println("Starting proxy server on", *addr) | |
if err := http.ListenAndServe(*addr, handler); err != nil { | |
log.Fatal("ListenAndServe:", err) | |
} | |
} |
cool gist :3
but does not work for https?
curl -x http://127.0.0.1:8080 https://google.com/
curl: (56) Received HTTP code 400 from proxy after CONNECT
btw i fork this and merge with caching http://github.com/kokizzu/tiny-http-proxy
@kokizzu or anyone else managed to make it worked with HTTPS ?
no @HopHouse, i ended using 3proxy
, polipo2
, squid3
, and ssh -vND
after tried a bunch of other alternatives, but apparently it's quite hard to find correct squid configuration/other working software that could cache https requests.
您好,请教个问题,如果我要用这个proxy解决浏览器跨域问题的话,是不是也要把静态页面部署到和这个proxy同样的端口上?不然依然访问不了这个proxy啊,求指教
不需要, 添加跨域访问CORS中间件即可.
TL;DR: https traffic is encrypted, a proxy is just a dumb intermediary between the client and server in this case.
https traffic is encrypted with the session key at the client (and server) side. Therefore the traffic in the different
sessions is always different, even if they are transferring the same content. In order to serve something from
the cache a proxy should have decrypted the traffic. Being able to decrypt the intercepted https traffic means
a successful attack on https. Sometimes this is possible (google for OpenSSL heartbleed
).
However in general it's not feasible. And in most cases it's considered illegal.
TL;DR: https traffic is encrypted, a proxy is just a dumb intermediary between the client and server in this case.
https traffic is encrypted with the session key at the client (and server) side. Therefore the traffic in the different
sessions is always different, even if they are transferring the same content. In order to serve something from
the cache a proxy should have decrypted the traffic. Being able to decrypt the intercepted https traffic means
a successful attack on https. Sometimes this is possible (google forOpenSSL heartbleed
).
However in general it's not feasible. And in most cases it's considered illegal.
So just do the HTTPS Proxy without cache.
Mind... blown...
awesome guys!
Great gist! I have one question. Why do you create a new instance of http.Client for each request? As stated in Golang documentation for http Client, it is concurrent safe and can be used concurrently by multiple goroutines?
Is there some downside of having a global http Client?
if req.URL.Scheme != "http" && req.URL.Scheme != "https" {
msg := "unsupported protocal scheme " + req.URL.Scheme
http.Error(wr, msg, http.StatusBadRequest)
log.Println(msg)
return
}
i think this part is redundant since you are using ListenAndServe
hopHeaders needs Content-Length i think?
The req.URL.Scheme is nonfunctional - req.URL will always have a blank Scheme, as discussed in golang/go#28940. So this will always error out.
VERSION = $(shell git describe --tags --always --dirty)
LDFLAGS=-ldflags "-X main.version=$(VERSION)"
OSARCH=$(shell go env GOHOSTOS)-$(shell go env GOHOSTARCH)
SIMPLEPROXYTOOL=\
goproxytool-darwin-amd64 \
goproxytool-linux-amd64 \
goproxytool-windows-amd64.exe
proxytool: goproxytool-$(OSARCH)
$(SIMPLEPROXYTOOL):
GOOS=$(word 2,$(subst -, ,$@)) GOARCH=$(word 3,$(subst -, ,$(subst .exe,,$@))) go build $(LDFLAGS) -o $@ ./$<
%-$(VERSION).zip: %.exe
rm -f $@
zip $@ $<
%-$(VERSION).zip: %
rm -f $@
zip $@ $<
clean:
rm -f goproxytool-*
release:
$(foreach bin,$(SIMPLEPROXYTOOL),$(subst .exe,,$(bin))-$(VERSION).zip)
rm:
$(foreach bin,$(SIMPLEPROXYTOOL),$(subst .exe,,$(bin))-$(VERSION).zip)
build: proxytool $(SIMPLEPROXYTOOL) clean rm
.PHONY: build
all: build
A MakeFile following this link
您好,请教个问题,如果我要用这个proxy解决浏览器跨域问题的话,是不是也要把静态页面部署到和这个proxy同样的端口上?不然依然访问不了这个proxy啊,求指教