From a32b9fe723c633cf5349bb0479d97e1f6d04445d Mon Sep 17 00:00:00 2001 From: Melonai Date: Fri, 10 Sep 2021 23:40:49 +0200 Subject: Refactor path into destination --- config.go | 4 +-- destination.go | 75 ++++++++++++++++++++++++++++++++++++++++++++++ handlers/handler.go | 33 +++++++++----------- handlers/passthrough.go | 4 +-- handlers/portgate.go | 8 +++-- path.go | 80 ------------------------------------------------- 6 files changed, 98 insertions(+), 106 deletions(-) create mode 100644 destination.go delete mode 100644 path.go diff --git a/config.go b/config.go index 7630e87..f5fec33 100644 --- a/config.go +++ b/config.go @@ -45,9 +45,9 @@ func (c *Config) TargetAddress(port int) string { } // MakeUrl creates the URL on the destination host that the user wants to access. -func (c *Config) MakeUrl(p Path) string { +func (c *Config) MakeUrl(p Destination) string { // TODO: Figure out what to do with TLS - return fmt.Sprintf("http://%s:%d%s", c.targetHost, p.DestinationIdentifier, p.ResourcePath) + return fmt.Sprintf("http://%s:%d%s", c.targetHost, p.Port, p.Path) } // CheckKey checks whether the givenKey matches the one in the config. diff --git a/destination.go b/destination.go new file mode 100644 index 0000000..d911e3e --- /dev/null +++ b/destination.go @@ -0,0 +1,75 @@ +package portgate + +import ( + "net/url" + "path" + "strconv" + "strings" +) + +// Destination represents a routing destination the user gave. +type Destination struct { + // Identifier to which port of the destination host the path points to and to which the + // user's request will be proxied to. + Port int + // The path without the port identifier. + // This is the path which will be requested from the destination. + Path string + IsPortgatePath bool +} + +// ParseDestinationFromURL creates a Path from the requested URL. +func DestinationFromURL(p string) Destination { + p = path.Clean("/" + p) + + // Get first path part, which is the potential port identifier. + destinationIdentifierEnd := strings.Index(p[1:], "/") + 1 + + // If there is no '/' in the path, apart from the root, the first part is the entire path + if destinationIdentifierEnd == 0 { + destinationIdentifierEnd = len(p) + } + + destinationIdentifier := p[1:destinationIdentifierEnd] + + // We have to to check that destinationIdentifier is a port + port, err := strconv.Atoi(destinationIdentifier) + if err == nil { + // We got an identifier and can split the path + resourcePath := path.Clean("/" + p[destinationIdentifierEnd:]) + + return Destination{ + Port: port, + Path: resourcePath, + } + } else { + destination := Destination{ + Port: 0, + Path: p, + } + + if strings.HasPrefix(destination.Path, "/_portgate") { + destination.IsPortgatePath = true + return destination + } + + return destination + } +} + +// DestinationFromReferer tries to create a Path from the Referer header of the request. +func (d Destination) AddReferer(referer string) Destination { + u, err := url.Parse(referer) + if err != nil { + return Destination{} + } + + // d has the correct resource path but the wrong port, so we create a new destination + // with the correct data from both. + newDestination := DestinationFromURL(u.Path) + + return Destination{ + Port: newDestination.Port, + Path: d.Path, + } +} \ No newline at end of file diff --git a/handlers/handler.go b/handlers/handler.go index d6e1b5b..7989766 100644 --- a/handlers/handler.go +++ b/handlers/handler.go @@ -42,34 +42,29 @@ func NewRequestHandler(config *portgate.Config, templates portgate.Templates) Re // HandleRequest handles all types of requests and delegates to more specific handlers. func (h *RequestHandler) HandleRequest(ctx *fasthttp.RequestCtx) { - path := portgate.ParsePath(string(ctx.Path())) + destination := portgate.DestinationFromURL(string(ctx.Path())) - if path.IsPortgatePath() { - h.handlePortgateRequest(ctx, path) + if destination.IsPortgatePath { + h.handlePortgateRequest(ctx, destination) return } - if path.DestinationIdentifier == -1 { - // We were not given a destination. + if destination.Port == 0 { + // Try to get the port from the Referer. + destination = destination.AddReferer(string(ctx.Request.Header.Referer())) - // Try to grab actual destination from Referer header. - // This can help us if the user followed an absolute link on a proxied page. - refererPath, err := portgate.ParsePathFromReferer(path, string(ctx.Request.Header.Referer())) - if err != nil || refererPath.DestinationIdentifier == -1 { - // The referer path also has no destination + // Still no port? + if destination.Port == 0 { h.handleUnknownRequest(ctx) - } else { - // We found the destination from the referer path, so we - // redirect the user to the Portgate URL they should've requested. - - portgateUrl := fmt.Sprintf("/%d%s", refererPath.DestinationIdentifier, refererPath.ResourcePath) - ctx.Redirect(portgateUrl, http.StatusTemporaryRedirect) + return } - } else { - // We were given a port, so we have to pass the request through to the destination host. - h.handlePassthroughRequest(ctx, path) + portgateUrl := fmt.Sprintf("/%d%s", destination.Port, destination.Path) + ctx.Redirect(portgateUrl, http.StatusTemporaryRedirect) + return } + + h.handlePassthroughRequest(ctx, destination) } // handleUnknownRequest handles any request which could not be processed due to missing diff --git a/handlers/passthrough.go b/handlers/passthrough.go index 3f8aafc..30cc7df 100644 --- a/handlers/passthrough.go +++ b/handlers/passthrough.go @@ -9,7 +9,7 @@ import ( // handlePassthroughRequest handles requests which are supposed to be proxied to the destination host. // If the user is authorized they are allowed to pass, otherwise they should be redirected to // the authentication page. (/_portgate) -func (h *RequestHandler) handlePassthroughRequest(ctx *fasthttp.RequestCtx, p portgate.Path) { +func (h *RequestHandler) handlePassthroughRequest(ctx *fasthttp.RequestCtx, p portgate.Destination) { // TODO: Check whether port is allowed to be accessed. // Check whether given cookie is ok, if not redirect to the authentication page. @@ -21,7 +21,7 @@ func (h *RequestHandler) handlePassthroughRequest(ctx *fasthttp.RequestCtx, p po // We reuse the request given to us by the user with minor changes to route it to the // destination host. ctx.Request.SetRequestURI(h.config.MakeUrl(p)) - ctx.Request.Header.SetHost(h.config.TargetAddress(p.DestinationIdentifier)) + ctx.Request.Header.SetHost(h.config.TargetAddress(p.Port)) // We pipe the response given to us by the destination host back to the user. // Since it's possible that we get a redirect, we take this into account, diff --git a/handlers/portgate.go b/handlers/portgate.go index 6e002e7..2cf8e13 100644 --- a/handlers/portgate.go +++ b/handlers/portgate.go @@ -1,16 +1,18 @@ package handlers import ( - "github.com/valyala/fasthttp" "net/http" "portgate" + "strings" "time" + + "github.com/valyala/fasthttp" ) // handlePortgateRequest handles all Portgate specific request for either showing Portgate // specific pages or handling creation of authorization tokens. -func (h *RequestHandler) handlePortgateRequest(ctx *fasthttp.RequestCtx, path portgate.Path) { - if path.IsPortgateStaticPath() { +func (h *RequestHandler) handlePortgateRequest(ctx *fasthttp.RequestCtx, destination portgate.Destination) { + if strings.HasPrefix(destination.Path, "/_portgate/static") { h.staticHandler(ctx) } else { // TODO: Implement authentication, authorization diff --git a/path.go b/path.go deleted file mode 100644 index 82a8353..0000000 --- a/path.go +++ /dev/null @@ -1,80 +0,0 @@ -package portgate - -import ( - "net/url" - "path" - "strconv" - "strings" -) - -// Path represents a routing destination the user gave. -type Path struct { - // Identifier to which port of the destination host the path points to and to which the - // user's request will be proxied to. - // If there was no identifier given it's -1. - DestinationIdentifier int - // The path without the port identifier. - // This is the path which will be requested from the destination. - ResourcePath string -} - -// ParsePath creates a Path from the requested URL. -func ParsePath(p string) Path { - p = path.Clean("/" + p) - - // Get first path part, which is the destinationIdentifier - - destinationIdentifierEnd := strings.Index(p[1:], "/") + 1 - - // If there is no '/' at in the path, apart from the root, the first part is the entire path - if destinationIdentifierEnd == 0 { - destinationIdentifierEnd = len(p) - } - - destinationIdentifier := p[1:destinationIdentifierEnd] - - // We have to to check that destinationIdentifier is a port - port, err := strconv.Atoi(destinationIdentifier) - if err == nil { - // We got an identifier and can split the path - - resourcePath := path.Clean("/" + p[destinationIdentifierEnd:]) - return Path{ - DestinationIdentifier: port, - ResourcePath: resourcePath, - } - } else { - // We got some other path without an identifier - - return Path{ - DestinationIdentifier: -1, - ResourcePath: p, - } - } -} - -// ParsePathFromReferer tries to create a Path from the Referer header of the request. -func ParsePathFromReferer(p Path, r string) (Path, error) { - u, err := url.Parse(r) - if err != nil { - return Path{}, err - } - - // p has the correct resource path but the wrong port, so we create a new path - // with the correct data from both. - rp := ParsePath(u.Path) - return Path{ - DestinationIdentifier: rp.DestinationIdentifier, - ResourcePath: p.ResourcePath, - }, nil -} - -// IsPortgatePath returns whether the Path goes to Portgate. -func (p *Path) IsPortgatePath() bool { - return p.DestinationIdentifier == -1 && strings.HasPrefix(p.ResourcePath, "/_portgate") -} - -// IsPortgateStaticPath returns whether the Path goes to the Portgate static files. -func (p *Path) IsPortgateStaticPath() bool { - return p.DestinationIdentifier == -1 && strings.HasPrefix(p.ResourcePath, "/_portgate/static") -} -- cgit 1.4.1