From bf41a38014389aa2d7c7aea1821ccb5ffc120f20 Mon Sep 17 00:00:00 2001 From: Maxim Lebedev Date: Mon, 2 Jan 2023 08:31:31 +0600 Subject: [PATCH] :recycle: Refactored user HTTP delivery layer --- internal/user/delivery/http/user_http.go | 33 +++++++++---------- internal/user/delivery/http/user_http_test.go | 25 ++++++-------- 2 files changed, 26 insertions(+), 32 deletions(-) diff --git a/internal/user/delivery/http/user_http.go b/internal/user/delivery/http/user_http.go index 542e9ee..88548fb 100644 --- a/internal/user/delivery/http/user_http.go +++ b/internal/user/delivery/http/user_http.go @@ -2,16 +2,15 @@ package http import ( "encoding/json" + "net/http" "strings" - "github.com/fasthttp/router" "github.com/lestrrat-go/jwx/v2/jwa" - http "github.com/valyala/fasthttp" "source.toby3d.me/toby3d/auth/internal/common" "source.toby3d.me/toby3d/auth/internal/domain" + "source.toby3d.me/toby3d/auth/internal/middleware" "source.toby3d.me/toby3d/auth/internal/token" - "source.toby3d.me/toby3d/middleware" ) type ( @@ -22,20 +21,20 @@ type ( Email string `json:"email,omitempty"` } - RequestHandler struct { + Handler struct { config *domain.Config tokens token.UseCase } ) -func NewRequestHandler(tokens token.UseCase, config *domain.Config) *RequestHandler { - return &RequestHandler{ +func NewHandler(tokens token.UseCase, config *domain.Config) *Handler { + return &Handler{ tokens: tokens, config: config, } } -func (h *RequestHandler) Register(r *router.Router) { +func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { chain := middleware.Chain{ //nolint:exhaustivestruct middleware.JWTWithConfig(middleware.JWTConfig{ @@ -44,39 +43,38 @@ func (h *RequestHandler) Register(r *router.Router) { SigningKey: []byte(h.config.JWT.Secret), SigningMethod: jwa.SignatureAlgorithm(h.config.JWT.Algorithm), Skipper: middleware.DefaultSkipper, - TokenLookup: "header:" + http.HeaderAuthorization + ":Bearer ", + TokenLookup: "header:" + common.HeaderAuthorization + ":Bearer ", }), middleware.LogFmt(), } - r.GET("/userinfo", chain.RequestHandler(h.handleUserInformation)) + chain.Handler(h.handleFunc).ServeHTTP(w, r) } -func (h *RequestHandler) handleUserInformation(ctx *http.RequestCtx) { - ctx.SetContentType(common.MIMEApplicationJSONCharsetUTF8) - ctx.SetStatusCode(http.StatusOK) +func (h *Handler) handleFunc(w http.ResponseWriter, r *http.Request) { + w.Header().Set(common.HeaderContentType, common.MIMEApplicationJSONCharsetUTF8) - encoder := json.NewEncoder(ctx) + encoder := json.NewEncoder(w) - tkn, userInfo, err := h.tokens.Verify(ctx, strings.TrimPrefix(string(ctx.Request.Header.Peek( - http.HeaderAuthorization)), "Bearer ")) + tkn, userInfo, err := h.tokens.Verify(r.Context(), + strings.TrimPrefix(r.Header.Get(common.HeaderAuthorization), "Bearer ")) if err != nil || tkn == nil { // WARN(toby3d): If the token is not valid, the endpoint still // MUST return a 200 Response. _ = encoder.Encode(err) //nolint:errchkjson + w.WriteHeader(http.StatusOK) return } if !tkn.Scope.Has(domain.ScopeProfile) { - ctx.SetStatusCode(http.StatusForbidden) - //nolint:errchkjson _ = encoder.Encode(domain.NewError( domain.ErrorCodeInsufficientScope, "token with 'profile' scope is required to view profile data", "https://indieauth.net/source/#user-information", )) + w.WriteHeader(http.StatusForbidden) return } @@ -105,4 +103,5 @@ func (h *RequestHandler) handleUserInformation(ctx *http.RequestCtx) { } _ = encoder.Encode(resp) //nolint:errchkjson + w.WriteHeader(http.StatusOK) } diff --git a/internal/user/delivery/http/user_http_test.go b/internal/user/delivery/http/user_http_test.go index 5eda63d..08821b5 100644 --- a/internal/user/delivery/http/user_http_test.go +++ b/internal/user/delivery/http/user_http_test.go @@ -1,20 +1,20 @@ package http_test import ( + "net/http/httptest" "path" "sync" "testing" - "github.com/fasthttp/router" "github.com/goccy/go-json" http "github.com/valyala/fasthttp" + "source.toby3d.me/toby3d/auth/internal/common" "source.toby3d.me/toby3d/auth/internal/domain" "source.toby3d.me/toby3d/auth/internal/profile" profilerepo "source.toby3d.me/toby3d/auth/internal/profile/repository/memory" "source.toby3d.me/toby3d/auth/internal/session" sessionrepo "source.toby3d.me/toby3d/auth/internal/session/repository/memory" - "source.toby3d.me/toby3d/auth/internal/testing/httptest" "source.toby3d.me/toby3d/auth/internal/token" tokenrepo "source.toby3d.me/toby3d/auth/internal/token/repository/memory" tokenucase "source.toby3d.me/toby3d/auth/internal/token/usecase" @@ -38,25 +38,20 @@ func TestUserInfo(t *testing.T) { deps := NewDependencies(t) deps.store.Store(path.Join(profilerepo.DefaultPathPrefix, deps.token.Me.String()), deps.profile) - r := router.New() - delivery.NewRequestHandler(deps.tokenService, deps.config).Register(r) - - client, _, cleanup := httptest.New(t, r.Handler) - t.Cleanup(cleanup) - req := httptest.NewRequest(http.MethodGet, "https://example.com/userinfo", nil) - defer http.ReleaseRequest(req) - deps.token.SetAuthHeader(req) + req.Header.Set(common.HeaderAuthorization, "Bearer "+deps.token.AccessToken) - resp := http.AcquireResponse() - defer http.ReleaseResponse(resp) + w := httptest.NewRecorder() + delivery.NewHandler(deps.tokenService, deps.config).ServeHTTP(w, req) - if err := client.Do(req, resp); err != nil { - t.Fatal(err) + resp := w.Result() + + if exp := http.StatusOK; resp.StatusCode != exp { + t.Errorf("%s %s = %d, want %d", req.Method, req.RequestURI, resp.StatusCode, exp) } result := new(delivery.UserInformationResponse) - if err := json.Unmarshal(resp.Body(), result); err != nil { + if err := json.NewDecoder(resp.Body).Decode(result); err != nil { t.Fatal(err) }