From f9f3a06e0cbcf56325415735ad7772c08c583de4 Mon Sep 17 00:00:00 2001 From: Joshua Liebow-Feeser Date: Sat, 9 May 2020 22:02:10 -0700 Subject: [PATCH] Convert to HTTP handlers inside outside of the main package Previously, the top-level package exported functions with the signature `func(*util.Context) util.StatusError`, and the `main` package was responsible for wrapping these and producing normal HTTP handlers. However, the Google Cloud Functions execution environment expects to be able to invoke HTTP handlers which are exported directly from the top-level package. We move the logic of converting these inner handlers into HTTP handlers to the `internal/util` package, allowing us to expose HTTP handlers from the top-level package --- functions/challenge.go | 4 ++- functions/cmd/main.go | 32 +--------------------- functions/internal/util/handler.go | 43 ++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 32 deletions(-) create mode 100644 functions/internal/util/handler.go diff --git a/functions/challenge.go b/functions/challenge.go index 11c23dc..e9d3dc0 100644 --- a/functions/challenge.go +++ b/functions/challenge.go @@ -8,7 +8,9 @@ import ( ) // ChallengeHandler is a handler for the /challenge endpoint. -func ChallengeHandler(ctx *util.Context) util.StatusError { +var ChallengeHandler = util.MakeHTTPHandler(challengeHandler) + +func challengeHandler(ctx *util.Context) util.StatusError { if err := util.ValidateRequestMethod(ctx, "GET", ""); err != nil { return err } diff --git a/functions/cmd/main.go b/functions/cmd/main.go index 115b9cb..e9c689d 100644 --- a/functions/cmd/main.go +++ b/functions/cmd/main.go @@ -1,20 +1,17 @@ package main import ( - "encoding/json" "fmt" "log" - "net/http" "os" "functions" - "functions/internal/util" "github.com/GoogleCloudPlatform/functions-framework-go/funcframework" ) func main() { - funcframework.RegisterHTTPFunction("/challenge", makeHTTPHandler(functions.ChallengeHandler)) + funcframework.RegisterHTTPFunction("/challenge", functions.ChallengeHandler) // Use PORT environment variable, or default to 8080. port := "8080" if envPort := os.Getenv("PORT"); envPort != "" { @@ -26,30 +23,3 @@ func main() { log.Fatalf("funcframework.Start: %v\n", err) } } - -func makeHTTPHandler(handler func(ctx *util.Context) util.StatusError) func(http.ResponseWriter, *http.Request) { - return func(w http.ResponseWriter, r *http.Request) { - ctx, err := util.NewContext(w, r) - if err != nil { - writeStatusError(w, r, err) - return - } - - if err := handler(&ctx); err != nil { - writeStatusError(w, r, err) - } - } -} - -func writeStatusError(w http.ResponseWriter, r *http.Request, err util.StatusError) { - type response struct { - Message string `json:"message"` - } - - w.Header().Set("Content-Type", "application/json; charset=utf-8") - w.WriteHeader(err.HTTPStatusCode()) - json.NewEncoder(w).Encode(response{Message: err.Message()}) - - log.Printf("[%v %v %v]: responding with error code %v and message \"%v\"", - r.RemoteAddr, r.Method, r.URL, err.HTTPStatusCode(), err.Message()) -} diff --git a/functions/internal/util/handler.go b/functions/internal/util/handler.go new file mode 100644 index 0000000..080f8a8 --- /dev/null +++ b/functions/internal/util/handler.go @@ -0,0 +1,43 @@ +package util + +import ( + "encoding/json" + "log" + "net/http" +) + +// Handler is a handler for a request to this service. Use MakeHTTPHandler to +// wrap a Handler with the logic necessary to produce a handler which can be +// registered with the "net/http" package. +type Handler = func(ctx *Context) StatusError + +// MakeHTTPHandler wraps a Handler, producing a handler which can be registered +// with the "net/http" package. The returned handler is responsible for: +// - Constructing a *Context +// - Converting any errors into an HTTP response +func MakeHTTPHandler(handler func(ctx *Context) StatusError) func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + ctx, err := NewContext(w, r) + if err != nil { + writeStatusError(w, r, err) + return + } + + if err := handler(&ctx); err != nil { + writeStatusError(w, r, err) + } + } +} + +func writeStatusError(w http.ResponseWriter, r *http.Request, err StatusError) { + type response struct { + Message string `json:"message"` + } + + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(err.HTTPStatusCode()) + json.NewEncoder(w).Encode(response{Message: err.Message()}) + + log.Printf("[%v %v %v]: responding with error code %v and message \"%v\" (error: %v)", + r.RemoteAddr, r.Method, r.URL, err.HTTPStatusCode(), err.Message(), err) +}