commit 0ced1cc32497f3b47a614ef4a526af7925447252 Author: Arlo Breault abreault@wikimedia.org Date: Thu May 20 08:31:30 2021 -0400
Move http handlers to a separate file --- broker/broker.go | 196 ---------------------------------------------------- broker/http.go | 205 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 205 insertions(+), 196 deletions(-)
diff --git a/broker/broker.go b/broker/broker.go index 58f3955..437a4d1 100644 --- a/broker/broker.go +++ b/broker/broker.go @@ -8,10 +8,8 @@ package main import ( "container/heap" "crypto/tls" - "errors" "flag" "io" - "io/ioutil" "log" "net/http" "os" @@ -21,17 +19,12 @@ import ( "syscall" "time"
- "git.torproject.org/pluggable-transports/snowflake.git/common/messages" "git.torproject.org/pluggable-transports/snowflake.git/common/safelog" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" "golang.org/x/crypto/acme/autocert" )
-const ( - readLimit = 100000 // Maximum number of bytes to be read from an HTTP request -) - type BrokerContext struct { snowflakes *SnowflakeHeap restrictedSnowflakes *SnowflakeHeap @@ -69,38 +62,6 @@ func NewBrokerContext(metricsLogger *log.Logger) *BrokerContext { } }
-// Implements the http.Handler interface -type SnowflakeHandler struct { - *IPC - handle func(*IPC, http.ResponseWriter, *http.Request) -} - -// Implements the http.Handler interface -type MetricsHandler struct { - logFilename string - handle func(string, http.ResponseWriter, *http.Request) -} - -func (sh SnowflakeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("Access-Control-Allow-Headers", "Origin, X-Session-ID") - // Return early if it's CORS preflight. - if "OPTIONS" == r.Method { - return - } - sh.handle(sh.IPC, w, r) -} - -func (mh MetricsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("Access-Control-Allow-Headers", "Origin, X-Session-ID") - // Return early if it's CORS preflight. - if "OPTIONS" == r.Method { - return - } - mh.handle(mh.logFilename, w, r) -} - // Proxies may poll for client offers concurrently. type ProxyPoll struct { id string @@ -178,169 +139,12 @@ func (ctx *BrokerContext) AddSnowflake(id string, proxyType string, natType stri return snowflake }
-/* -For snowflake proxies to request a client from the Broker. -*/ -func proxyPolls(i *IPC, w http.ResponseWriter, r *http.Request) { - body, err := ioutil.ReadAll(http.MaxBytesReader(w, r.Body, readLimit)) - if err != nil { - log.Println("Invalid data.") - w.WriteHeader(http.StatusBadRequest) - return - } - - arg := messages.Arg{ - Body: body, - RemoteAddr: r.RemoteAddr, - NatType: "", - } - - var response []byte - err = i.ProxyPolls(arg, &response) - switch { - case err == nil: - case errors.Is(err, messages.ErrBadRequest): - w.WriteHeader(http.StatusBadRequest) - return - case errors.Is(err, messages.ErrInternal): - fallthrough - default: - log.Println(err) - w.WriteHeader(http.StatusInternalServerError) - return - } - - if _, err := w.Write(response); err != nil { - log.Printf("proxyPolls unable to write offer with error: %v", err) - } -} - // Client offer contains an SDP and the NAT type of the client type ClientOffer struct { natType string sdp []byte }
-/* -Expects a WebRTC SDP offer in the Request to give to an assigned -snowflake proxy, which responds with the SDP answer to be sent in -the HTTP response back to the client. -*/ -func clientOffers(i *IPC, w http.ResponseWriter, r *http.Request) { - body, err := ioutil.ReadAll(http.MaxBytesReader(w, r.Body, readLimit)) - if err != nil { - log.Printf("Error reading client request: %s", err.Error()) - w.WriteHeader(http.StatusBadRequest) - return - } - - arg := messages.Arg{ - Body: body, - RemoteAddr: "", - NatType: r.Header.Get("Snowflake-NAT-Type"), - } - - var response []byte - err = i.ClientOffers(arg, &response) - switch { - case err == nil: - case errors.Is(err, messages.ErrUnavailable): - w.WriteHeader(http.StatusServiceUnavailable) - return - case errors.Is(err, messages.ErrTimeout): - w.WriteHeader(http.StatusGatewayTimeout) - return - default: - log.Println(err) - w.WriteHeader(http.StatusInternalServerError) - return - } - - if _, err := w.Write(response); err != nil { - log.Printf("clientOffers unable to write answer with error: %v", err) - } -} - -/* -Expects snowflake proxes which have previously successfully received -an offer from proxyHandler to respond with an answer in an HTTP POST, -which the broker will pass back to the original client. -*/ -func proxyAnswers(i *IPC, w http.ResponseWriter, r *http.Request) { - body, err := ioutil.ReadAll(http.MaxBytesReader(w, r.Body, readLimit)) - if err != nil { - log.Println("Invalid data.") - w.WriteHeader(http.StatusBadRequest) - return - } - - arg := messages.Arg{ - Body: body, - RemoteAddr: "", - NatType: "", - } - - var response []byte - err = i.ProxyAnswers(arg, &response) - switch { - case err == nil: - case errors.Is(err, messages.ErrBadRequest): - w.WriteHeader(http.StatusBadRequest) - return - case errors.Is(err, messages.ErrInternal): - fallthrough - default: - log.Println(err) - w.WriteHeader(http.StatusInternalServerError) - return - } - - if _, err := w.Write(response); err != nil { - log.Printf("proxyAnswers unable to write answer response with error: %v", err) - } -} - -func debugHandler(i *IPC, w http.ResponseWriter, r *http.Request) { - var response string - - err := i.Debug(new(interface{}), &response) - if err != nil { - log.Println(err) - w.WriteHeader(http.StatusInternalServerError) - return - } - - if _, err := w.Write([]byte(response)); err != nil { - log.Printf("writing proxy information returned error: %v ", err) - } -} - -func robotsTxtHandler(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "text/plain; charset=utf-8") - if _, err := w.Write([]byte("User-agent: *\nDisallow: /\n")); err != nil { - log.Printf("robotsTxtHandler unable to write, with this error: %v", err) - } -} - -func metricsHandler(metricsFilename string, w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "text/plain; charset=utf-8") - - if metricsFilename == "" { - http.NotFound(w, r) - return - } - metricsFile, err := os.OpenFile(metricsFilename, os.O_RDONLY, 0644) - if err != nil { - log.Println("Error opening metrics file for reading") - http.NotFound(w, r) - return - } - - if _, err := io.Copy(w, metricsFile); err != nil { - log.Printf("copying metricsFile returned error: %v", err) - } -} - func main() { var acmeEmail string var acmeHostnamesCommas string diff --git a/broker/http.go b/broker/http.go new file mode 100644 index 0000000..6555d7a --- /dev/null +++ b/broker/http.go @@ -0,0 +1,205 @@ +package main + +import ( + "errors" + "io" + "io/ioutil" + "log" + "net/http" + "os" + + "git.torproject.org/pluggable-transports/snowflake.git/common/messages" +) + +const ( + readLimit = 100000 // Maximum number of bytes to be read from an HTTP request +) + +// Implements the http.Handler interface +type SnowflakeHandler struct { + *IPC + handle func(*IPC, http.ResponseWriter, *http.Request) +} + +func (sh SnowflakeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Access-Control-Allow-Headers", "Origin, X-Session-ID") + // Return early if it's CORS preflight. + if "OPTIONS" == r.Method { + return + } + sh.handle(sh.IPC, w, r) +} + +// Implements the http.Handler interface +type MetricsHandler struct { + logFilename string + handle func(string, http.ResponseWriter, *http.Request) +} + +func (mh MetricsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Access-Control-Allow-Headers", "Origin, X-Session-ID") + // Return early if it's CORS preflight. + if "OPTIONS" == r.Method { + return + } + mh.handle(mh.logFilename, w, r) +} + +func robotsTxtHandler(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/plain; charset=utf-8") + if _, err := w.Write([]byte("User-agent: *\nDisallow: /\n")); err != nil { + log.Printf("robotsTxtHandler unable to write, with this error: %v", err) + } +} + +func metricsHandler(metricsFilename string, w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/plain; charset=utf-8") + + if metricsFilename == "" { + http.NotFound(w, r) + return + } + metricsFile, err := os.OpenFile(metricsFilename, os.O_RDONLY, 0644) + if err != nil { + log.Println("Error opening metrics file for reading") + http.NotFound(w, r) + return + } + + if _, err := io.Copy(w, metricsFile); err != nil { + log.Printf("copying metricsFile returned error: %v", err) + } +} + +func debugHandler(i *IPC, w http.ResponseWriter, r *http.Request) { + var response string + + err := i.Debug(new(interface{}), &response) + if err != nil { + log.Println(err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + if _, err := w.Write([]byte(response)); err != nil { + log.Printf("writing proxy information returned error: %v ", err) + } +} + +/* +For snowflake proxies to request a client from the Broker. +*/ +func proxyPolls(i *IPC, w http.ResponseWriter, r *http.Request) { + body, err := ioutil.ReadAll(http.MaxBytesReader(w, r.Body, readLimit)) + if err != nil { + log.Println("Invalid data.") + w.WriteHeader(http.StatusBadRequest) + return + } + + arg := messages.Arg{ + Body: body, + RemoteAddr: r.RemoteAddr, + NatType: "", + } + + var response []byte + err = i.ProxyPolls(arg, &response) + switch { + case err == nil: + case errors.Is(err, messages.ErrBadRequest): + w.WriteHeader(http.StatusBadRequest) + return + case errors.Is(err, messages.ErrInternal): + fallthrough + default: + log.Println(err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + if _, err := w.Write(response); err != nil { + log.Printf("proxyPolls unable to write offer with error: %v", err) + } +} + +/* +Expects a WebRTC SDP offer in the Request to give to an assigned +snowflake proxy, which responds with the SDP answer to be sent in +the HTTP response back to the client. +*/ +func clientOffers(i *IPC, w http.ResponseWriter, r *http.Request) { + body, err := ioutil.ReadAll(http.MaxBytesReader(w, r.Body, readLimit)) + if err != nil { + log.Printf("Error reading client request: %s", err.Error()) + w.WriteHeader(http.StatusBadRequest) + return + } + + arg := messages.Arg{ + Body: body, + RemoteAddr: "", + NatType: r.Header.Get("Snowflake-NAT-Type"), + } + + var response []byte + err = i.ClientOffers(arg, &response) + switch { + case err == nil: + case errors.Is(err, messages.ErrUnavailable): + w.WriteHeader(http.StatusServiceUnavailable) + return + case errors.Is(err, messages.ErrTimeout): + w.WriteHeader(http.StatusGatewayTimeout) + return + default: + log.Println(err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + if _, err := w.Write(response); err != nil { + log.Printf("clientOffers unable to write answer with error: %v", err) + } +} + +/* +Expects snowflake proxes which have previously successfully received +an offer from proxyHandler to respond with an answer in an HTTP POST, +which the broker will pass back to the original client. +*/ +func proxyAnswers(i *IPC, w http.ResponseWriter, r *http.Request) { + body, err := ioutil.ReadAll(http.MaxBytesReader(w, r.Body, readLimit)) + if err != nil { + log.Println("Invalid data.") + w.WriteHeader(http.StatusBadRequest) + return + } + + arg := messages.Arg{ + Body: body, + RemoteAddr: "", + NatType: "", + } + + var response []byte + err = i.ProxyAnswers(arg, &response) + switch { + case err == nil: + case errors.Is(err, messages.ErrBadRequest): + w.WriteHeader(http.StatusBadRequest) + return + case errors.Is(err, messages.ErrInternal): + fallthrough + default: + log.Println(err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + if _, err := w.Write(response); err != nil { + log.Printf("proxyAnswers unable to write answer response with error: %v", err) + } +}
tor-commits@lists.torproject.org