astraproxy/handler.go

105 lines
3.1 KiB
Go
Raw Normal View History

2020-05-19 21:53:13 +02:00
package main
import (
"net"
"fmt"
2020-05-19 22:13:51 +02:00
"time"
2020-05-19 21:53:13 +02:00
"net/http"
"strings"
2020-05-19 22:13:51 +02:00
"context"
2020-05-19 21:53:13 +02:00
)
type ProxyHandler struct {
2020-05-19 22:13:51 +02:00
timeout time.Duration
2020-05-19 23:53:32 +02:00
auth Auth
2020-05-19 21:53:13 +02:00
logger *CondLogger
httptransport http.RoundTripper
}
2020-05-19 23:53:32 +02:00
func NewProxyHandler(timeout time.Duration, auth Auth, logger *CondLogger) *ProxyHandler {
2020-05-19 21:53:13 +02:00
httptransport := &http.Transport{}
return &ProxyHandler{
2020-05-19 22:13:51 +02:00
timeout: timeout,
2020-05-19 23:53:32 +02:00
auth: auth,
2020-05-19 21:53:13 +02:00
logger: logger,
httptransport: httptransport,
}
}
func (s *ProxyHandler) HandleTunnel(wr http.ResponseWriter, req *http.Request) {
2020-05-19 22:13:51 +02:00
ctx, _ := context.WithTimeout(req.Context(), s.timeout)
dialer := net.Dialer{}
conn, err := dialer.DialContext(ctx, "tcp", req.RequestURI)
2020-05-19 21:53:13 +02:00
if err != nil {
s.logger.Error("Can't satisfy CONNECT request: %v", err)
http.Error(wr, "Can't satisfy CONNECT request", http.StatusBadGateway)
return
}
defer conn.Close()
2020-05-24 14:24:11 +02:00
if req.ProtoMajor == 0 || req.ProtoMajor == 1 {
// Upgrade client connection
localconn, _, err := hijack(wr)
if err != nil {
s.logger.Error("Can't hijack client connection: %v", err)
http.Error(wr, "Can't hijack client connection", http.StatusInternalServerError)
return
}
defer localconn.Close()
2020-05-19 21:53:13 +02:00
2020-05-24 14:24:11 +02:00
// Inform client connection is built
fmt.Fprintf(localconn, "HTTP/%d.%d 200 OK\r\n\r\n", req.ProtoMajor, req.ProtoMinor)
2020-05-19 21:53:13 +02:00
2020-05-24 14:24:11 +02:00
proxy(req.Context(), localconn, conn)
} else if req.ProtoMajor == 2 {
wr.Header()["Date"] = nil
wr.WriteHeader(http.StatusOK)
flush(wr)
proxyh2(req.Context(), req.Body, wr, conn)
} else {
s.logger.Error("Unsupported protocol version: %s", req.Proto)
http.Error(wr, "Unsupported protocol version.", http.StatusBadRequest)
return
}
2020-05-19 21:53:13 +02:00
}
func (s *ProxyHandler) HandleRequest(wr http.ResponseWriter, req *http.Request) {
req.RequestURI = ""
2020-05-24 14:59:11 +02:00
if req.ProtoMajor == 2 {
req.URL.Scheme = "http" // We can't access :scheme pseudo-header, so assume http
req.URL.Host = req.Host
}
2020-05-19 21:53:13 +02:00
resp, err := s.httptransport.RoundTrip(req)
if err != nil {
s.logger.Error("HTTP fetch error: %v", err)
http.Error(wr, "Server Error", http.StatusInternalServerError)
return
}
defer resp.Body.Close()
s.logger.Info("%v %v %v %v", req.RemoteAddr, req.Method, req.URL, resp.Status)
delHopHeaders(resp.Header)
copyHeader(wr.Header(), resp.Header)
wr.WriteHeader(resp.StatusCode)
2020-05-19 23:53:32 +02:00
flush(wr)
2020-05-22 20:02:08 +02:00
copyBody(wr, resp.Body)
2020-05-19 21:53:13 +02:00
}
func (s *ProxyHandler) ServeHTTP(wr http.ResponseWriter, req *http.Request) {
2020-05-24 14:24:11 +02:00
s.logger.Info("Request: %v %v %v %v", req.RemoteAddr, req.Proto, req.Method, req.URL)
2020-05-24 15:11:50 +02:00
if ((req.URL.Host == "" || req.URL.Scheme == "") && req.ProtoMajor < 2) ||
(req.Host == "" && req.ProtoMajor == 2) {
http.Error(wr, "Bad Request", http.StatusBadRequest)
return
}
2020-05-19 23:53:32 +02:00
if !s.auth.Validate(wr, req) {
return
}
2020-05-19 21:53:13 +02:00
delHopHeaders(req.Header)
if strings.ToUpper(req.Method) == "CONNECT" {
s.HandleTunnel(wr, req)
} else {
s.HandleRequest(wr, req)
}
}