From e7b3dd138a51c742b40bb5e8071c69c1f5b2d7cb Mon Sep 17 00:00:00 2001 From: LeafHacker Date: Wed, 18 Sep 2019 19:13:33 +0100 Subject: [PATCH] Reorganise stuff into separate echo servers TODO fix localhost hostname matching --- go.sum | 1 + src/router.go | 14 ----- src/{web/s3proxy.go => s3proxy/server.go} | 25 +++++---- src/server.go | 49 +++++++++--------- src/util/proxy.go | 30 +++++++++++ src/util/proxy_test.go | 62 +++++++++++++++++++++++ src/web/changelog.go | 30 ++--------- src/web/changelog_test.go | 56 +------------------- src/web/server.go | 21 ++++++++ 9 files changed, 160 insertions(+), 128 deletions(-) delete mode 100644 src/router.go rename src/{web/s3proxy.go => s3proxy/server.go} (64%) create mode 100644 src/util/proxy.go create mode 100644 src/util/proxy_test.go create mode 100644 src/web/server.go diff --git a/go.sum b/go.sum index 0487eef..bc871b8 100644 --- a/go.sum +++ b/go.sum @@ -44,6 +44,7 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/src/router.go b/src/router.go deleted file mode 100644 index 0025869..0000000 --- a/src/router.go +++ /dev/null @@ -1,14 +0,0 @@ -package main - -import ( - "net/http" - - "github.com/ImpactDevelopment/ImpactServer/src/web" - "github.com/labstack/echo" -) - -func Router(e *echo.Echo) { - e.Match([]string{http.MethodHead, http.MethodGet}, "/changelog", web.Changelog) - e.Use(web.S3Proxy) - e.Any("/Impact/*", web.ImpactRedirect) -} diff --git a/src/web/s3proxy.go b/src/s3proxy/server.go similarity index 64% rename from src/web/s3proxy.go rename to src/s3proxy/server.go index dadbaee..a813c87 100644 --- a/src/web/s3proxy.go +++ b/src/s3proxy/server.go @@ -1,8 +1,10 @@ package web import ( + "github.com/ImpactDevelopment/ImpactServer/src/util" + "github.com/labstack/echo/middleware" + "net/http" "net/url" - "strings" "time" "github.com/aws/aws-sdk-go/aws" @@ -13,17 +15,18 @@ import ( var awsSess = session.Must(session.NewSession(&aws.Config{Region: aws.String("us-east-1")})) -func S3Proxy(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) error { - hostStr := c.Request().Host - if strings.HasPrefix(hostStr, "static.") { - return S3Handle(c) - } - return next(c) - } +func Server() (e *echo.Echo) { + e = echo.New() + + e.Match([]string{http.MethodHead, http.MethodGet}, "/*", proxyHandler) + + e.Use(middleware.Logger()) + e.Use(middleware.Recover()) + + return } -func S3Handle(c echo.Context) error { +func proxyHandler(c echo.Context) error { file := c.Request().URL.Path s3Req, _ := s3.New(awsSess).GetObjectRequest(&s3.GetObjectInput{ @@ -41,6 +44,6 @@ func S3Handle(c echo.Context) error { return err } - doProxy(c, target) + util.Proxy(c, target) return nil } diff --git a/src/server.go b/src/server.go index 5e05f54..8fe3b81 100644 --- a/src/server.go +++ b/src/server.go @@ -1,13 +1,14 @@ package main import ( - "net/http" - "os" - "strconv" - mid "github.com/ImpactDevelopment/ImpactServer/src/middleware" + "github.com/ImpactDevelopment/ImpactServer/src/s3proxy" + "github.com/ImpactDevelopment/ImpactServer/src/web" "github.com/labstack/echo" "github.com/labstack/echo/middleware" + "net/http" + "os" + "strconv" ) var port = 3000 @@ -20,36 +21,38 @@ func init() { } func main() { - // Echo is cool https://echo.labstack.com - server := echo.New() - AddMiddleware(server) - Router(server) - - // Fall back to static files, after none of the routes have matched - server.Use(middleware.StaticWithConfig(middleware.StaticConfig{ - Root: "static", // Root directory from where the static content is served. - Browse: false, // Don't enable directory browsing. - HTML5: false, // Don't forward everything to root (would allow client-side routing) - })) + hosts := map[string]*echo.Echo{ + "impactclient.net": web.Server(), + "files.impactclient.net": s3proxy.Server(), + } - // Start the server - server.Logger.Fatal(StartServer(server, port)) -} + e := echo.New() -func AddMiddleware(e *echo.Echo) { // Enforce URL style // We don't need to do any http->https stuff here 'cos cloudflare e.Pre(middleware.NonWWWRedirect()) e.Pre(middleware.RemoveTrailingSlash()) e.Pre(mid.RemoveIndexHTML(http.StatusMovedPermanently)) - // Compression not required because the CDN does that for us + e.Any("/*", func(c echo.Context) (err error) { + req := c.Request() + res := c.Response() + host := hosts[req.Host] - // Log all the things TODO formatting https://echo.labstack.com/middleware/logger - e.Use(middleware.Logger()) + if host == nil { + err = echo.ErrNotFound + } else { + host.ServeHTTP(res, req) + } + + return + }) - // Don't crash + e.Use(middleware.Logger()) e.Use(middleware.Recover()) + + // Start the server + e.Logger.Fatal(StartServer(e, port)) } func StartServer(e *echo.Echo, port int) error { diff --git a/src/util/proxy.go b/src/util/proxy.go new file mode 100644 index 0000000..f0f8f9a --- /dev/null +++ b/src/util/proxy.go @@ -0,0 +1,30 @@ +package util + +import ( + "github.com/labstack/echo" + "net/http" + "net/http/httputil" + "net/url" +) + +// var func to allow overriding in tests +var serveProxy = func(proxy *httputil.ReverseProxy, req *http.Request, res http.ResponseWriter) { + proxy.ServeHTTP(res, req) +} + +func Proxy(c echo.Context, target *url.URL) { + proxy := &httputil.ReverseProxy{ + Director: func(req *http.Request) { + // Change the URL + req.URL = target + req.Header.Set("X-Forwarded-Host", req.Host) + req.Host = target.Host + + // Don't send our cookies to github + req.Header.Del(echo.HeaderCookie) + req.Header.Del(echo.HeaderAuthorization) + }, + } + + serveProxy(proxy, c.Request(), c.Response()) +} diff --git a/src/util/proxy_test.go b/src/util/proxy_test.go new file mode 100644 index 0000000..13a433f --- /dev/null +++ b/src/util/proxy_test.go @@ -0,0 +1,62 @@ +package util + +import ( + "github.com/labstack/echo" + "github.com/stretchr/testify/assert" + "net/http" + "net/http/httptest" + "net/http/httputil" + "net/url" + "testing" +) + +func TestProxy(t *testing.T) { + // Override serveProxy and store what's passed into it + var ( + servedCount = 0 + servedProxy *httputil.ReverseProxy + servedReq *http.Request + servedRes http.ResponseWriter + ) + serveProxy = func(proxy *httputil.ReverseProxy, req *http.Request, res http.ResponseWriter) { + servedCount++ + servedProxy = proxy + servedReq = req + servedRes = res + } + + // Setup the request + e := echo.New() + req := httptest.NewRequest(http.MethodGet, "http://foobar.host/changelog", nil) + rec := httptest.NewRecorder() + c := e.NewContext(req, rec) + c.SetPath("/changelog") + + target, _ := url.Parse("https://impactdevelopment.github.io/Impact/changelog") + + // Run the handler + Proxy(c, target) + // Basic checks + assert.Equal(t, 1, servedCount) + assert.NotNil(t, servedProxy) + assert.NotNil(t, servedReq) + assert.NotNil(t, servedRes) + + // Request should be unchanged + assert.Equal(t, "", servedReq.Header.Get("X-Forwarded-Host")) + assert.Equal(t, "foobar.host", servedReq.Host) + assert.Equal(t, "foobar.host", servedReq.URL.Host) + assert.Equal(t, "/changelog", servedReq.URL.Path) + assert.Equal(t, "", servedReq.URL.RawQuery) + assert.Equal(t, "http://foobar.host/changelog", servedReq.URL.String()) + + // The Director function should mutate the request + servedProxy.Director(servedReq) + assert.Equal(t, "foobar.host", servedReq.Header.Get("X-Forwarded-Host")) + assert.Equal(t, "impactdevelopment.github.io", servedReq.Host) + assert.Equal(t, "https", servedReq.URL.Scheme) + assert.Equal(t, "impactdevelopment.github.io", servedReq.URL.Host) + assert.Equal(t, "/Impact/changelog", servedReq.URL.Path) + assert.Equal(t, "", servedReq.URL.RawQuery) + assert.Equal(t, target.String(), servedReq.URL.String()) +} diff --git a/src/web/changelog.go b/src/web/changelog.go index ea7acab..63d9de1 100644 --- a/src/web/changelog.go +++ b/src/web/changelog.go @@ -1,8 +1,8 @@ package web import ( + "github.com/ImpactDevelopment/ImpactServer/src/util" "net/http" - "net/http/httputil" "net/url" "github.com/labstack/echo" @@ -10,40 +10,18 @@ import ( const github = "https://impactdevelopment.github.io" -func Changelog(c echo.Context) error { +func changelog(c echo.Context) error { // Forward to the changelog hosted by github target, err := url.Parse(github + "/Impact/changelog") if err != nil { return err //wtf } - doProxy(c, target) + util.Proxy(c, target) return nil } -func doProxy(c echo.Context, target *url.URL) { - proxy := &httputil.ReverseProxy{ - Director: func(req *http.Request) { - // Change the URL - req.URL = target - req.Header.Set("X-Forwarded-Host", req.Host) - req.Host = target.Host - - // Don't send our cookies to github - req.Header.Del(echo.HeaderCookie) - req.Header.Del(echo.HeaderAuthorization) - }, - } - - serveProxy(proxy, c.Request(), c.Response()) -} - -// var func to allow overriding in tests -var serveProxy = func(proxy *httputil.ReverseProxy, req *http.Request, res http.ResponseWriter) { - proxy.ServeHTTP(res, req) -} - -func ImpactRedirect(c echo.Context) error { +func impactRedirect(c echo.Context) error { address := c.Request().URL // Echo tends to set the Request URL to just the path+query diff --git a/src/web/changelog_test.go b/src/web/changelog_test.go index 92d842b..9d21044 100644 --- a/src/web/changelog_test.go +++ b/src/web/changelog_test.go @@ -3,7 +3,6 @@ package web import ( "net/http" "net/http/httptest" - "net/http/httputil" "testing" "github.com/labstack/echo" @@ -14,57 +13,6 @@ func TestConst(t *testing.T) { assert.Equal(t, "https://impactdevelopment.github.io", github) } -func TestChangelog(t *testing.T) { - // Override serveProxy and store what's passed into it - var ( - servedCount = 0 - servedProxy *httputil.ReverseProxy - servedReq *http.Request - servedRes http.ResponseWriter - ) - serveProxy = func(proxy *httputil.ReverseProxy, req *http.Request, res http.ResponseWriter) { - servedCount++ - servedProxy = proxy - servedReq = req - servedRes = res - } - - // Setup the request - e := echo.New() - req := httptest.NewRequest(http.MethodGet, "http://foobar.host/changelog", nil) - rec := httptest.NewRecorder() - c := e.NewContext(req, rec) - c.SetPath("/changelog") - - // Run the handler - err := Changelog(c) - if assert.NoError(t, err) { - // Basic checks - assert.Equal(t, 1, servedCount) - assert.NotNil(t, servedProxy) - assert.NotNil(t, servedReq) - assert.NotNil(t, servedRes) - - // Request should be unchanged - assert.Equal(t, "", servedReq.Header.Get("X-Forwarded-Host")) - assert.Equal(t, "foobar.host", servedReq.Host) - assert.Equal(t, "foobar.host", servedReq.URL.Host) - assert.Equal(t, "/changelog", servedReq.URL.Path) - assert.Equal(t, "", servedReq.URL.RawQuery) - assert.Equal(t, "http://foobar.host/changelog", servedReq.URL.String()) - - // The Director function should mutate the request - servedProxy.Director(servedReq) - assert.Equal(t, "foobar.host", servedReq.Header.Get("X-Forwarded-Host")) - assert.Equal(t, "impactdevelopment.github.io", servedReq.Host) - assert.Equal(t, "https", servedReq.URL.Scheme) - assert.Equal(t, "impactdevelopment.github.io", servedReq.URL.Host) - assert.Equal(t, "/Impact/changelog", servedReq.URL.Path) - assert.Equal(t, "", servedReq.URL.RawQuery) - assert.Equal(t, github+"/Impact/changelog", servedReq.URL.String()) - } -} - func TestImpactRedirect(t *testing.T) { const route = "/Impact/" const path = "assets/css/style.css?v=foobar" @@ -74,7 +22,7 @@ func TestImpactRedirect(t *testing.T) { rec := httptest.NewRecorder() c := e.NewContext(req, rec) c.SetPath(route + "*") - err := ImpactRedirect(c) + err := impactRedirect(c) if assert.NoError(t, err) { // Expect 302 @@ -93,7 +41,7 @@ func TestChangelogRedirect(t *testing.T) { rec := httptest.NewRecorder() c := e.NewContext(req, rec) c.SetPath(route + "*") - err := ImpactRedirect(c) + err := impactRedirect(c) if assert.NoError(t, err) { // Expect 301 diff --git a/src/web/server.go b/src/web/server.go new file mode 100644 index 0000000..a4c5816 --- /dev/null +++ b/src/web/server.go @@ -0,0 +1,21 @@ +package web + +import ( + "github.com/labstack/echo/middleware" + "net/http" + + "github.com/labstack/echo" +) + +func Server() (e *echo.Echo) { + e = echo.New() + + e.Match([]string{http.MethodHead, http.MethodGet}, "/changelog", changelog) + e.Any("/Impact/*", impactRedirect) + e.Static("/", "static") + + e.Use(middleware.Logger()) + e.Use(middleware.Recover()) + + return +}