Commit 04d16195 authored by Jacob Vosmaer's avatar Jacob Vosmaer

Move everything into internal/upstream

parent a827a033
......@@ -3,11 +3,13 @@ package main
import (
"./internal/api"
"./internal/helper"
"./internal/upstream"
"fmt"
"net/http"
"net/http/httptest"
"regexp"
"testing"
"time"
)
func okHandler(w http.ResponseWriter, _ *http.Request, _ *api.Response) {
......@@ -25,7 +27,7 @@ func runPreAuthorizeHandler(t *testing.T, suffix string, url *regexp.Regexp, api
if err != nil {
t.Fatal(err)
}
api := newUpstream(ts.URL, "").API
api := upstream.New(ts.URL, "", "123", time.Second).API
response := httptest.NewRecorder()
api.PreAuthorizeHandler(okHandler, suffix)(response, httpRequest)
......
/*
Miscellaneous helpers: logging, errors, subprocesses
*/
package main
import (
"net/http"
"path"
)
func httpError(w http.ResponseWriter, r *http.Request, error string, code int) {
if r.ProtoAtLeast(1, 1) {
// Force client to disconnect if we render request error
w.Header().Set("Connection", "close")
}
http.Error(w, error, code)
}
// Borrowed from: net/http/server.go
// Return the canonical path for p, eliminating . and .. elements.
func cleanURIPath(p string) string {
if p == "" {
return "/"
}
if p[0] != '/' {
p = "/" + p
}
np := path.Clean(p)
// path.Clean removes trailing slash except for root;
// put the trailing slash back if necessary.
if p[len(p)-1] == '/' && np != "/" {
np += "/"
}
return np
}
......@@ -2,7 +2,10 @@ package helper
import (
"net/http/httptest"
"net/http"
"testing"
"regexp"
"log"
)
func AssertResponseCode(t *testing.T, response *httptest.ResponseRecorder, expectedCode int) {
......@@ -22,3 +25,21 @@ func AssertResponseHeader(t *testing.T, response *httptest.ResponseRecorder, hea
t.Fatalf("for HTTP request expected to receive the header %q with %q, got %q", header, expectedValue, response.Header().Get(header))
}
}
func TestServerWithHandler(url *regexp.Regexp, handler http.HandlerFunc) *httptest.Server {
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if url != nil && !url.MatchString(r.URL.Path) {
log.Println("UPSTREAM", r.Method, r.URL, "DENY")
w.WriteHeader(404)
return
}
if version := r.Header.Get("Gitlab-Workhorse"); version == "" {
log.Println("UPSTREAM", r.Method, r.URL, "DENY")
w.WriteHeader(403)
return
}
handler(w, r)
}))
}
\ No newline at end of file
package main
package upstream
import (
"./internal/api"
"../api"
"net/http"
)
......
package main
package upstream
import (
"./internal/helper"
"../helper"
"io/ioutil"
"net/http"
"path/filepath"
......
package main
package upstream
import (
"./internal/helper"
"../helper"
"io/ioutil"
"net/http"
"net/http/httptest"
......
package main
package upstream
import "net/http"
func handleDevelopmentMode(developmentMode *bool, handler http.HandlerFunc) http.HandlerFunc {
func handleDevelopmentMode(developmentMode bool, handler http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if !*developmentMode {
if !developmentMode {
http.NotFound(w, r)
return
}
......
package main
package upstream
import (
"./internal/helper"
"../helper"
"net/http"
"net/http/httptest"
"testing"
......@@ -14,7 +14,7 @@ func TestDevelopmentModeEnabled(t *testing.T) {
w := httptest.NewRecorder()
executed := false
handleDevelopmentMode(&developmentMode, func(_ http.ResponseWriter, _ *http.Request) {
handleDevelopmentMode(developmentMode, func(_ http.ResponseWriter, _ *http.Request) {
executed = true
})(w, r)
if !executed {
......@@ -29,7 +29,7 @@ func TestDevelopmentModeDisabled(t *testing.T) {
w := httptest.NewRecorder()
executed := false
handleDevelopmentMode(&developmentMode, func(_ http.ResponseWriter, _ *http.Request) {
handleDevelopmentMode(developmentMode, func(_ http.ResponseWriter, _ *http.Request) {
executed = true
})(w, r)
if executed {
......
package main
package upstream
import (
"./internal/helper"
"../helper"
"compress/gzip"
"fmt"
"io"
......
package main
package upstream
import (
"./internal/helper"
"../helper"
"bytes"
"compress/gzip"
"fmt"
......
package main
package upstream
import (
"fmt"
......
package main
package upstream
import (
"./internal/errorpage"
"./internal/git"
"./internal/lfs"
"../errorpage"
"../git"
"../lfs"
"net/http"
"regexp"
)
......@@ -27,7 +27,7 @@ const ciAPIPattern = `^/ci/api/`
// see upstream.ServeHTTP
var routes []route
func (u *upstream) compileRoutes() {
func (u *Upstream) compileRoutes() {
u.routes = []route{
// Git Clone
route{"GET", regexp.MustCompile(gitProjectPattern + `info/refs\z`), git.GetInfoRefs(u.API)},
......@@ -59,7 +59,7 @@ func (u *upstream) compileRoutes() {
// Serve assets
route{"", regexp.MustCompile(`^/assets/`),
handleServeFile(u.DocumentRoot, u.urlPrefix, CacheExpireMax,
handleDevelopmentMode(developmentMode,
handleDevelopmentMode(u.DevelopmentMode,
handleDeployPage(u.DocumentRoot,
errorpage.Inject(u.DocumentRoot,
u.Proxy,
......
package main
package upstream
import (
"./internal/helper"
"../helper"
"log"
"net/http"
"os"
......
package main
package upstream
import (
"./internal/helper"
"../helper"
"bytes"
"compress/gzip"
"io/ioutil"
......@@ -12,8 +12,6 @@ import (
"testing"
)
var dummyUpstream = newUpstream("http://localhost", "")
func TestServingNonExistingFile(t *testing.T) {
dir := "/path/to/non/existing/directory"
httpRequest, _ := http.NewRequest("GET", "/file", nil)
......
package main
package upstream
import (
"./internal/helper"
"../helper"
"bytes"
"errors"
"fmt"
......
package main
package upstream
import (
"./internal/helper"
"../helper"
"bytes"
"fmt"
"io"
......@@ -13,17 +13,20 @@ import (
"regexp"
"strings"
"testing"
"time"
)
var nilHandler = http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})
func TestUploadTempPathRequirement(t *testing.T) {
response := httptest.NewRecorder()
request := &http.Request{}
handleFileUploads(dummyUpstream.Proxy).ServeHTTP(response, request)
handleFileUploads(nilHandler).ServeHTTP(response, request)
helper.AssertResponseCode(t, response, 500)
}
func TestUploadHandlerForwardingRawData(t *testing.T) {
ts := testServerWithHandler(regexp.MustCompile(`/url/path\z`), func(w http.ResponseWriter, r *http.Request) {
ts := helper.TestServerWithHandler(regexp.MustCompile(`/url/path\z`), func(w http.ResponseWriter, r *http.Request) {
if r.Method != "PATCH" {
t.Fatal("Expected PATCH request")
}
......@@ -52,9 +55,8 @@ func TestUploadHandlerForwardingRawData(t *testing.T) {
response := httptest.NewRecorder()
httpRequest.Header.Set(tempPathHeader, tempPath)
u := newUpstream(ts.URL, "")
handleFileUploads(u.Proxy).ServeHTTP(response, httpRequest)
handleFileUploads(New(ts.URL, "", "123", time.Second).Proxy).ServeHTTP(response, httpRequest)
helper.AssertResponseCode(t, response, 202)
if response.Body.String() != "RESPONSE" {
t.Fatal("Expected RESPONSE in response body")
......@@ -70,7 +72,7 @@ func TestUploadHandlerRewritingMultiPartData(t *testing.T) {
}
defer os.RemoveAll(tempPath)
ts := testServerWithHandler(regexp.MustCompile(`/url/path\z`), func(w http.ResponseWriter, r *http.Request) {
ts := helper.TestServerWithHandler(regexp.MustCompile(`/url/path\z`), func(w http.ResponseWriter, r *http.Request) {
if r.Method != "PUT" {
t.Fatal("Expected PUT request")
}
......@@ -127,7 +129,7 @@ func TestUploadHandlerRewritingMultiPartData(t *testing.T) {
httpRequest.Header.Set("Content-Type", writer.FormDataContentType())
httpRequest.Header.Set(tempPathHeader, tempPath)
response := httptest.NewRecorder()
u := newUpstream(ts.URL, "")
u := New(ts.URL, "", "123", time.Second)
handleFileUploads(u.Proxy).ServeHTTP(response, httpRequest)
helper.AssertResponseCode(t, response, 202)
......
......@@ -4,29 +4,33 @@ The upstream type implements http.Handler.
In this file we handle request routing and interaction with the authBackend.
*/
package main
package upstream
import (
"./internal/api"
"./internal/proxy"
"../api"
"../proxy"
"fmt"
"log"
"net"
"net/http"
"net/url"
"strings"
"path"
"time"
)
type upstream struct {
type Upstream struct {
Version string
API *api.API
Proxy *proxy.Proxy
DocumentRoot string
DevelopmentMode bool
ResponseHeadersTimeout time.Duration
urlPrefix urlPrefix
routes []route
}
func newUpstream(authBackend string, authSocket string) *upstream {
func New(authBackend string, authSocket string, version string, responseHeadersTimeout time.Duration) *Upstream {
parsedURL, err := url.Parse(authBackend)
if err != nil {
log.Fatalln(err)
......@@ -49,25 +53,25 @@ func newUpstream(authBackend string, authSocket string) *upstream {
Dial: func(_, _ string) (net.Conn, error) {
return dialer.Dial("unix", authSocket)
},
ResponseHeaderTimeout: *responseHeadersTimeout,
ResponseHeaderTimeout: responseHeadersTimeout,
}
}
proxyTransport := proxy.NewRoundTripper(authTransport)
up := &upstream{
up := &Upstream{
API: &api.API{
Client: &http.Client{Transport: proxyTransport},
URL: parsedURL,
Version: Version,
Version: version,
},
Proxy: proxy.NewProxy(parsedURL, proxyTransport, Version),
Proxy: proxy.NewProxy(parsedURL, proxyTransport, version),
urlPrefix: urlPrefix(relativeURLRoot),
}
up.compileRoutes()
return up
}
func (u *upstream) ServeHTTP(ow http.ResponseWriter, r *http.Request) {
func (u *Upstream) ServeHTTP(ow http.ResponseWriter, r *http.Request) {
w := newLoggingResponseWriter(ow)
defer w.Log(r)
......@@ -113,3 +117,30 @@ func (u *upstream) ServeHTTP(ow http.ResponseWriter, r *http.Request) {
ro.handler.ServeHTTP(&w, r)
}
func httpError(w http.ResponseWriter, r *http.Request, error string, code int) {
if r.ProtoAtLeast(1, 1) {
// Force client to disconnect if we render request error
w.Header().Set("Connection", "close")
}
http.Error(w, error, code)
}
// Borrowed from: net/http/server.go
// Return the canonical path for p, eliminating . and .. elements.
func cleanURIPath(p string) string {
if p == "" {
return "/"
}
if p[0] != '/' {
p = "/" + p
}
np := path.Clean(p)
// path.Clean removes trailing slash except for root;
// put the trailing slash back if necessary.
if p[len(p)-1] == '/' && np != "/" {
np += "/"
}
return np
}
\ No newline at end of file
package main
package upstream
import (
"strings"
......
......@@ -14,6 +14,7 @@ In this file we start the web server and hand off to the upstream type.
package main
import (
"./internal/upstream"
"flag"
"fmt"
"log"
......@@ -80,7 +81,9 @@ func main() {
}()
}
upstream := newUpstream(*authBackend, *authSocket)
upstream.DocumentRoot = *documentRoot
log.Fatal(http.Serve(listener, upstream))
up := upstream.New(*authBackend, *authSocket, Version, *responseHeadersTimeout)
up.DocumentRoot = *documentRoot
up.DevelopmentMode = *developmentMode
log.Fatal(http.Serve(listener, up))
}
......@@ -2,6 +2,8 @@ package main
import (
"./internal/api"
"./internal/helper"
"./internal/upstream"
"bytes"
"encoding/json"
"fmt"
......@@ -283,26 +285,8 @@ func newBranch() string {
return fmt.Sprintf("branch-%d", time.Now().UnixNano())
}
func testServerWithHandler(url *regexp.Regexp, handler http.HandlerFunc) *httptest.Server {
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if url != nil && !url.MatchString(r.URL.Path) {
log.Println("UPSTREAM", r.Method, r.URL, "DENY")
w.WriteHeader(404)
return
}
if version := r.Header.Get("Gitlab-Workhorse"); version == "" {
log.Println("UPSTREAM", r.Method, r.URL, "DENY")
w.WriteHeader(403)
return
}
handler(w, r)
}))
}
func testAuthServer(url *regexp.Regexp, code int, body interface{}) *httptest.Server {
return testServerWithHandler(url, func(w http.ResponseWriter, r *http.Request) {
return helper.TestServerWithHandler(url, func(w http.ResponseWriter, r *http.Request) {
// Write pure string
if data, ok := body.(string); ok {
log.Println("UPSTREAM", r.Method, r.URL, code)
......@@ -327,7 +311,7 @@ func testAuthServer(url *regexp.Regexp, code int, body interface{}) *httptest.Se
}
func startWorkhorseServer(authBackend string) *httptest.Server {
u := newUpstream(authBackend, "")
u := upstream.New(authBackend, "", "123", time.Second)
return httptest.NewServer(u)
}
......
......@@ -3,6 +3,7 @@ package main
import (
"./internal/helper"
"./internal/proxy"
"./internal/upstream"
"bytes"
"fmt"
"io"
......@@ -15,8 +16,12 @@ import (
"time"
)
func newUpstream(url string) *upstream.Upstream {
return upstream.New(url, "", "123", time.Second)
}
func TestProxyRequest(t *testing.T) {
ts := testServerWithHandler(regexp.MustCompile(`/url/path\z`), func(w http.ResponseWriter, r *http.Request) {
ts := helper.TestServerWithHandler(regexp.MustCompile(`/url/path\z`), func(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
t.Fatal("Expected POST request")
}
......@@ -42,7 +47,7 @@ func TestProxyRequest(t *testing.T) {
}
httpRequest.Header.Set("Custom-Header", "test")
u := newUpstream(ts.URL, "")
u := newUpstream(ts.URL)
w := httptest.NewRecorder()
u.Proxy.ServeHTTP(w, httpRequest)
helper.AssertResponseCode(t, w, 202)
......@@ -60,7 +65,7 @@ func TestProxyError(t *testing.T) {
}
httpRequest.Header.Set("Custom-Header", "test")
u := newUpstream("http://localhost:655575/", "")
u := newUpstream("http://localhost:655575/")
w := httptest.NewRecorder()
u.Proxy.ServeHTTP(w, httpRequest)
helper.AssertResponseCode(t, w, 502)
......@@ -68,7 +73,7 @@ func TestProxyError(t *testing.T) {
}
func TestProxyReadTimeout(t *testing.T) {
ts := testServerWithHandler(nil, func(w http.ResponseWriter, r *http.Request) {
ts := helper.TestServerWithHandler(nil, func(w http.ResponseWriter, r *http.Request) {
time.Sleep(time.Minute)
})
......@@ -89,7 +94,7 @@ func TestProxyReadTimeout(t *testing.T) {
},
)
u := newUpstream(ts.URL, "")
u := newUpstream(ts.URL)
url, err := url.Parse(ts.URL)
if err != nil {
t.Fatal(err)
......@@ -103,7 +108,7 @@ func TestProxyReadTimeout(t *testing.T) {
}
func TestProxyHandlerTimeout(t *testing.T) {
ts := testServerWithHandler(nil,
ts := helper.TestServerWithHandler(nil,
http.TimeoutHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
time.Sleep(time.Second)
}), time.Millisecond, "Request took too long").ServeHTTP,
......@@ -114,7 +119,7 @@ func TestProxyHandlerTimeout(t *testing.T) {
t.Fatal(err)
}
u := newUpstream(ts.URL, "")
u := newUpstream(ts.URL)
w := httptest.NewRecorder()
u.Proxy.ServeHTTP(w, httpRequest)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment