13493: Move proxy and federation code to their own source files.
authorTom Clegg <tclegg@veritasgenetics.com>
Thu, 28 Jun 2018 14:02:36 +0000 (10:02 -0400)
committerTom Clegg <tclegg@veritasgenetics.com>
Thu, 28 Jun 2018 14:02:36 +0000 (10:02 -0400)
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg@veritasgenetics.com>

lib/controller/federation.go
lib/controller/handler.go
lib/controller/proxy.go [new file with mode: 0644]

index 5ea305d03a94c41d9a601d1cbc11b0d8177a1a72..008e506874289d69363d22cec884f69554a29774 100644 (file)
@@ -8,10 +8,46 @@ import (
        "bytes"
        "io/ioutil"
        "net/http"
+       "net/url"
+       "regexp"
 
        "git.curoverse.com/arvados.git/sdk/go/auth"
+       "git.curoverse.com/arvados.git/sdk/go/httpserver"
 )
 
+var wfRe = regexp.MustCompile(`^/arvados/v1/workflows/([0-9a-z]{5})-[^/]+$`)
+
+func (h *Handler) proxyRemoteCluster(w http.ResponseWriter, req *http.Request, next http.Handler) {
+       m := wfRe.FindStringSubmatch(req.URL.Path)
+       if len(m) < 2 || m[1] == h.Cluster.ClusterID {
+               next.ServeHTTP(w, req)
+               return
+       }
+       remoteID := m[1]
+       remote, ok := h.Cluster.RemoteClusters[remoteID]
+       if !ok {
+               httpserver.Error(w, "no proxy available for cluster "+remoteID, http.StatusNotFound)
+               return
+       }
+       scheme := remote.Scheme
+       if scheme == "" {
+               scheme = "https"
+       }
+       urlOut := &url.URL{
+               Scheme:   scheme,
+               Host:     remote.Host,
+               Path:     req.URL.Path,
+               RawPath:  req.URL.RawPath,
+               RawQuery: req.URL.RawQuery,
+       }
+       err := h.saltAuthToken(req, remoteID)
+       if err != nil {
+               httpserver.Error(w, err.Error(), http.StatusBadRequest)
+               return
+       }
+       h.proxy(w, req, urlOut)
+}
+
 // Extract the auth token supplied in req, and replace it with a
 // salted token for the remote cluster.
 func (h *Handler) saltAuthToken(req *http.Request, remote string) error {
index ab6c72735e0a7b606ca65d5110b9e3b6f9d047bf..feccbe60844154fef9666e547e6b86bc2738fdbc 100644 (file)
@@ -5,15 +5,11 @@
 package controller
 
 import (
-       "context"
-       "io"
        "net"
        "net/http"
        "net/url"
-       "regexp"
        "strings"
        "sync"
-       "time"
 
        "git.curoverse.com/arvados.git/sdk/go/arvados"
        "git.curoverse.com/arvados.git/sdk/go/health"
@@ -53,19 +49,6 @@ func (h *Handler) setup() {
        h.handlerStack = mux
 }
 
-// headers that shouldn't be forwarded when proxying. See
-// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers
-var dropHeaders = map[string]bool{
-       "Connection":          true,
-       "Keep-Alive":          true,
-       "Proxy-Authenticate":  true,
-       "Proxy-Authorization": true,
-       "TE":                true,
-       "Trailer":           true,
-       "Transfer-Encoding": true,
-       "Upgrade":           true,
-}
-
 type middlewareFunc func(http.ResponseWriter, *http.Request, http.Handler)
 
 func prepend(next http.Handler, middleware middlewareFunc) http.Handler {
@@ -74,39 +57,6 @@ func prepend(next http.Handler, middleware middlewareFunc) http.Handler {
        })
 }
 
-var wfRe = regexp.MustCompile(`^/arvados/v1/workflows/([0-9a-z]{5})-[^/]+$`)
-
-func (h *Handler) proxyRemoteCluster(w http.ResponseWriter, req *http.Request, next http.Handler) {
-       m := wfRe.FindStringSubmatch(req.URL.Path)
-       if len(m) < 2 || m[1] == h.Cluster.ClusterID {
-               next.ServeHTTP(w, req)
-               return
-       }
-       remoteID := m[1]
-       remote, ok := h.Cluster.RemoteClusters[remoteID]
-       if !ok {
-               httpserver.Error(w, "no proxy available for cluster "+remoteID, http.StatusNotFound)
-               return
-       }
-       scheme := remote.Scheme
-       if scheme == "" {
-               scheme = "https"
-       }
-       urlOut := &url.URL{
-               Scheme:   scheme,
-               Host:     remote.Host,
-               Path:     req.URL.Path,
-               RawPath:  req.URL.RawPath,
-               RawQuery: req.URL.RawQuery,
-       }
-       err := h.saltAuthToken(req, remoteID)
-       if err != nil {
-               httpserver.Error(w, err.Error(), http.StatusBadRequest)
-               return
-       }
-       h.proxy(w, req, urlOut)
-}
-
 func (h *Handler) proxyRailsAPI(w http.ResponseWriter, req *http.Request, next http.Handler) {
        urlOut, err := findRailsAPI(h.Cluster, h.NodeProfile)
        if err != nil {
@@ -123,52 +73,6 @@ func (h *Handler) proxyRailsAPI(w http.ResponseWriter, req *http.Request, next h
        h.proxy(w, req, urlOut)
 }
 
-func (h *Handler) proxy(w http.ResponseWriter, reqIn *http.Request, urlOut *url.URL) {
-       // Copy headers from incoming request, then add/replace proxy
-       // headers like Via and X-Forwarded-For.
-       hdrOut := http.Header{}
-       for k, v := range reqIn.Header {
-               if !dropHeaders[k] {
-                       hdrOut[k] = v
-               }
-       }
-       xff := reqIn.RemoteAddr
-       if xffIn := reqIn.Header.Get("X-Forwarded-For"); xffIn != "" {
-               xff = xffIn + "," + xff
-       }
-       hdrOut.Set("X-Forwarded-For", xff)
-       hdrOut.Add("Via", reqIn.Proto+" arvados-controller")
-
-       ctx := reqIn.Context()
-       if timeout := h.Cluster.HTTPRequestTimeout; timeout > 0 {
-               var cancel context.CancelFunc
-               ctx, cancel = context.WithDeadline(ctx, time.Now().Add(time.Duration(timeout)))
-               defer cancel()
-       }
-
-       reqOut := (&http.Request{
-               Method: reqIn.Method,
-               URL:    urlOut,
-               Header: hdrOut,
-               Body:   reqIn.Body,
-       }).WithContext(ctx)
-       resp, err := arvados.InsecureHTTPClient.Do(reqOut)
-       if err != nil {
-               httpserver.Error(w, err.Error(), http.StatusInternalServerError)
-               return
-       }
-       for k, v := range resp.Header {
-               for _, v := range v {
-                       w.Header().Add(k, v)
-               }
-       }
-       w.WriteHeader(resp.StatusCode)
-       n, err := io.Copy(w, resp.Body)
-       if err != nil {
-               httpserver.Logger(reqIn).WithError(err).WithField("bytesCopied", n).Error("error copying response body")
-       }
-}
-
 // For now, findRailsAPI always uses the rails API running on this
 // node.
 func findRailsAPI(cluster *arvados.Cluster, np *arvados.NodeProfile) (*url.URL, error) {
diff --git a/lib/controller/proxy.go b/lib/controller/proxy.go
new file mode 100644 (file)
index 0000000..91949f0
--- /dev/null
@@ -0,0 +1,75 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+package controller
+
+import (
+       "context"
+       "io"
+       "net/http"
+       "net/url"
+       "time"
+
+       "git.curoverse.com/arvados.git/sdk/go/arvados"
+       "git.curoverse.com/arvados.git/sdk/go/httpserver"
+)
+
+// headers that shouldn't be forwarded when proxying. See
+// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers
+var dropHeaders = map[string]bool{
+       "Connection":          true,
+       "Keep-Alive":          true,
+       "Proxy-Authenticate":  true,
+       "Proxy-Authorization": true,
+       "TE":                true,
+       "Trailer":           true,
+       "Transfer-Encoding": true,
+       "Upgrade":           true,
+}
+
+func (h *Handler) proxy(w http.ResponseWriter, reqIn *http.Request, urlOut *url.URL) {
+       // Copy headers from incoming request, then add/replace proxy
+       // headers like Via and X-Forwarded-For.
+       hdrOut := http.Header{}
+       for k, v := range reqIn.Header {
+               if !dropHeaders[k] {
+                       hdrOut[k] = v
+               }
+       }
+       xff := reqIn.RemoteAddr
+       if xffIn := reqIn.Header.Get("X-Forwarded-For"); xffIn != "" {
+               xff = xffIn + "," + xff
+       }
+       hdrOut.Set("X-Forwarded-For", xff)
+       hdrOut.Add("Via", reqIn.Proto+" arvados-controller")
+
+       ctx := reqIn.Context()
+       if timeout := h.Cluster.HTTPRequestTimeout; timeout > 0 {
+               var cancel context.CancelFunc
+               ctx, cancel = context.WithDeadline(ctx, time.Now().Add(time.Duration(timeout)))
+               defer cancel()
+       }
+
+       reqOut := (&http.Request{
+               Method: reqIn.Method,
+               URL:    urlOut,
+               Header: hdrOut,
+               Body:   reqIn.Body,
+       }).WithContext(ctx)
+       resp, err := arvados.InsecureHTTPClient.Do(reqOut)
+       if err != nil {
+               httpserver.Error(w, err.Error(), http.StatusInternalServerError)
+               return
+       }
+       for k, v := range resp.Header {
+               for _, v := range v {
+                       w.Header().Add(k, v)
+               }
+       }
+       w.WriteHeader(resp.StatusCode)
+       n, err := io.Copy(w, resp.Body)
+       if err != nil {
+               httpserver.Logger(reqIn).WithError(err).WithField("bytesCopied", n).Error("error copying response body")
+       }
+}