From bbded3be7aa77f775430732e0b7fd8781d2c6a2f Mon Sep 17 00:00:00 2001 From: Roey Berman Date: Mon, 22 Jul 2024 10:36:32 -0700 Subject: [PATCH] Remove gorilla mux dependency (#15) Also bump version to 0.0.9. ## Why? We want to minimize dependencies for our SDKs. --- go.mod | 1 - go.sum | 2 - nexus/api.go | 2 +- nexus/server.go | 138 +++++++++++++++++++++++------------------------- 4 files changed, 68 insertions(+), 75 deletions(-) diff --git a/go.mod b/go.mod index b3e7fb7..75ede8f 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,6 @@ go 1.21 require ( github.com/google/uuid v1.3.0 - github.com/gorilla/mux v1.8.0 github.com/stretchr/testify v1.8.4 ) diff --git a/go.sum b/go.sum index 32f3fd2..8d8b455 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,6 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= diff --git a/nexus/api.go b/nexus/api.go index 523dbae..9fbc922 100644 --- a/nexus/api.go +++ b/nexus/api.go @@ -15,7 +15,7 @@ import ( ) // Package version. -const version = "v0.0.8" +const version = "v0.0.9" const ( // Nexus specific headers. diff --git a/nexus/server.go b/nexus/server.go index 6b276bb..975f5f9 100644 --- a/nexus/server.go +++ b/nexus/server.go @@ -12,9 +12,8 @@ import ( "net/http" "net/url" "strconv" + "strings" "time" - - "github.com/gorilla/mux" ) // An HandlerStartOperationResult is the return type from the [Handler] StartOperation and [Operation] Start methods. It @@ -264,18 +263,7 @@ func (h *baseHTTPHandler) writeFailure(writer http.ResponseWriter, err error) { } } -func (h *httpHandler) startOperation(writer http.ResponseWriter, request *http.Request) { - vars := mux.Vars(request) - service, err := url.PathUnescape(vars["service"]) - if err != nil { - h.writeFailure(writer, HandlerErrorf(HandlerErrorTypeBadRequest, "failed to parse URL path")) - return - } - operation, err := url.PathUnescape(vars["operation"]) - if err != nil { - h.writeFailure(writer, HandlerErrorf(HandlerErrorTypeBadRequest, "failed to parse URL path")) - return - } +func (h *httpHandler) startOperation(service, operation string, writer http.ResponseWriter, request *http.Request) { options := StartOperationOptions{ RequestID: request.Header.Get(headerRequestID), CallbackURL: request.URL.Query().Get(queryCallbackURL), @@ -304,23 +292,7 @@ func (h *httpHandler) startOperation(writer http.ResponseWriter, request *http.R } } -func (h *httpHandler) getOperationResult(writer http.ResponseWriter, request *http.Request) { - vars := mux.Vars(request) - service, err := url.PathUnescape(vars["service"]) - if err != nil { - h.writeFailure(writer, HandlerErrorf(HandlerErrorTypeBadRequest, "failed to parse URL path")) - return - } - operation, err := url.PathUnescape(vars["operation"]) - if err != nil { - h.writeFailure(writer, HandlerErrorf(HandlerErrorTypeBadRequest, "failed to parse URL path")) - return - } - operationID, err := url.PathUnescape(vars["operation_id"]) - if err != nil { - h.writeFailure(writer, HandlerErrorf(HandlerErrorTypeBadRequest, "failed to parse URL path")) - return - } +func (h *httpHandler) getOperationResult(service, operation, operationID string, writer http.ResponseWriter, request *http.Request) { options := GetOperationResultOptions{Header: httpHeaderToNexusHeader(request.Header)} // If both Request-Timeout http header and wait query string are set, the minimum of the Request-Timeout header @@ -365,23 +337,7 @@ func (h *httpHandler) getOperationResult(writer http.ResponseWriter, request *ht h.writeResult(writer, result) } -func (h *httpHandler) getOperationInfo(writer http.ResponseWriter, request *http.Request) { - vars := mux.Vars(request) - service, err := url.PathUnescape(vars["service"]) - if err != nil { - h.writeFailure(writer, HandlerErrorf(HandlerErrorTypeBadRequest, "failed to parse URL path")) - return - } - operation, err := url.PathUnescape(vars["operation"]) - if err != nil { - h.writeFailure(writer, HandlerErrorf(HandlerErrorTypeBadRequest, "failed to parse URL path")) - return - } - operationID, err := url.PathUnescape(vars["operation_id"]) - if err != nil { - h.writeFailure(writer, HandlerErrorf(HandlerErrorTypeBadRequest, "failed to parse URL path")) - return - } +func (h *httpHandler) getOperationInfo(service, operation, operationID string, writer http.ResponseWriter, request *http.Request) { options := GetOperationInfoOptions{Header: httpHeaderToNexusHeader(request.Header)} ctx, cancel, ok := h.contextWithTimeoutFromHTTPRequest(writer, request) @@ -407,23 +363,7 @@ func (h *httpHandler) getOperationInfo(writer http.ResponseWriter, request *http } } -func (h *httpHandler) cancelOperation(writer http.ResponseWriter, request *http.Request) { - vars := mux.Vars(request) - service, err := url.PathUnescape(vars["service"]) - if err != nil { - h.writeFailure(writer, HandlerErrorf(HandlerErrorTypeBadRequest, "failed to parse URL path")) - return - } - operation, err := url.PathUnescape(vars["operation"]) - if err != nil { - h.writeFailure(writer, HandlerErrorf(HandlerErrorTypeBadRequest, "failed to parse URL path")) - return - } - operationID, err := url.PathUnescape(vars["operation_id"]) - if err != nil { - h.writeFailure(writer, HandlerErrorf(HandlerErrorTypeBadRequest, "failed to parse URL path")) - return - } +func (h *httpHandler) cancelOperation(service, operation, operationID string, writer http.ResponseWriter, request *http.Request) { options := CancelOperationOptions{Header: httpHeaderToNexusHeader(request.Header)} ctx, cancel, ok := h.contextWithTimeoutFromHTTPRequest(writer, request) @@ -488,6 +428,67 @@ type HandlerOptions struct { Serializer Serializer } +func (h *httpHandler) handleRequest(writer http.ResponseWriter, request *http.Request) { + parts := strings.Split(request.URL.EscapedPath(), "/") + // First part is empty (due to leading /) + if len(parts) < 3 { + h.writeFailure(writer, HandlerErrorf(HandlerErrorTypeNotFound, "not found")) + return + } + service, err := url.PathUnescape(parts[1]) + if err != nil { + h.writeFailure(writer, HandlerErrorf(HandlerErrorTypeBadRequest, "failed to parse URL path")) + return + } + operation, err := url.PathUnescape(parts[2]) + if err != nil { + h.writeFailure(writer, HandlerErrorf(HandlerErrorTypeBadRequest, "failed to parse URL path")) + return + } + var operationID string + if len(parts) > 3 { + operationID, err = url.PathUnescape(parts[3]) + if err != nil { + h.writeFailure(writer, HandlerErrorf(HandlerErrorTypeBadRequest, "failed to parse URL path")) + return + } + } + + switch len(parts) { + case 3: // /{service}/{operation} + if request.Method != "POST" { + h.writeFailure(writer, HandlerErrorf(HandlerErrorTypeBadRequest, "invalid request method: expected POST, got %q", request.Method)) + return + } + h.startOperation(service, operation, writer, request) + case 4: // /{service}/{operation}/{operation_id} + if request.Method != "GET" { + h.writeFailure(writer, HandlerErrorf(HandlerErrorTypeBadRequest, "invalid request method: expected GET, got %q", request.Method)) + return + } + h.getOperationInfo(service, operation, operationID, writer, request) + case 5: + switch parts[4] { + case "result": // /{service}/{operation}/{operation_id}/result + if request.Method != "GET" { + h.writeFailure(writer, HandlerErrorf(HandlerErrorTypeBadRequest, "invalid request method: expected GET, got %q", request.Method)) + return + } + h.getOperationResult(service, operation, operationID, writer, request) + case "cancel": // /{service}/{operation}/{operation_id}/cancel + if request.Method != "POST" { + h.writeFailure(writer, HandlerErrorf(HandlerErrorTypeBadRequest, "invalid request method: expected POST, got %q", request.Method)) + return + } + h.cancelOperation(service, operation, operationID, writer, request) + default: + h.writeFailure(writer, HandlerErrorf(HandlerErrorTypeNotFound, "not found")) + } + default: + h.writeFailure(writer, HandlerErrorf(HandlerErrorTypeNotFound, "not found")) + } +} + // NewHTTPHandler constructs an [http.Handler] from given options for handling Nexus service requests. func NewHTTPHandler(options HandlerOptions) http.Handler { if options.Logger == nil { @@ -506,10 +507,5 @@ func NewHTTPHandler(options HandlerOptions) http.Handler { options: options, } - router := mux.NewRouter().UseEncodedPath() - router.HandleFunc("/{service}/{operation}", handler.startOperation).Methods("POST") - router.HandleFunc("/{service}/{operation}/{operation_id}", handler.getOperationInfo).Methods("GET") - router.HandleFunc("/{service}/{operation}/{operation_id}/result", handler.getOperationResult).Methods("GET") - router.HandleFunc("/{service}/{operation}/{operation_id}/cancel", handler.cancelOperation).Methods("POST") - return router + return http.HandlerFunc(handler.handleRequest) }