Skip to content

Commit

Permalink
replace request logging mdw w/ decode request mdw
Browse files Browse the repository at this point in the history
  • Loading branch information
pirtleshell committed Feb 7, 2024
1 parent 19c0c7c commit 845d356
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 28 deletions.
2 changes: 2 additions & 0 deletions service/cachemdw/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ func IsCacheable(
req *decode.EVMRPCRequestEnvelope,
) bool {
// TODO: technically, we _could_ cache the "invalid request" response for `null` requests...
// however, doing so will may result in different than expected responses for batch requests
// ie. {error} response vs [{error}] (the not-list is expected)
if req == nil {
return false
}
Expand Down
25 changes: 6 additions & 19 deletions service/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ func (w bodySaverResponseWriter) Write(b []byte) (int, error) {
// - routes single requests to next()
func createDecodeRequestMiddleware(next http.HandlerFunc, batchProcessingMiddleware http.HandlerFunc, serviceLogger *logging.ServiceLogger) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// capture the initial request time in order to calculate response time & latency at the end
requestStartTimeContext := context.WithValue(r.Context(), RequestStartTimeContextKey, time.Now())

// skip processing if there is no body content
if r.Body == nil {
serviceLogger.Trace().Msg("no data in request")
Expand Down Expand Up @@ -124,7 +127,7 @@ func createDecodeRequestMiddleware(next http.HandlerFunc, batchProcessingMiddlew
serviceLogger.Trace().
Any("decoded request", decodedRequest).
Msg("successfully decoded single EVM request")
singleDecodedReqContext := context.WithValue(r.Context(), DecodedRequestContextKey, decodedRequest)
singleDecodedReqContext := context.WithValue(requestStartTimeContext, DecodedRequestContextKey, decodedRequest)
next.ServeHTTP(w, r.WithContext(singleDecodedReqContext))
return
}
Expand All @@ -137,33 +140,17 @@ func createDecodeRequestMiddleware(next http.HandlerFunc, batchProcessingMiddlew
return
}
if len(batchRequests) == 0 {
// TODO: hardcode or cache error response here?
serviceLogger.Trace().Msg(fmt.Sprintf("request is for an empty batch: %s", rawBody))
next.ServeHTTP(w, r)
return
}

// TODO: Trace
serviceLogger.Trace().Any("batch", batchRequests).Msg("successfully decoded batch of requests")
batchDecodedReqContext := context.WithValue(r.Context(), DecodedBatchRequestContextKey, batchRequests)
batchDecodedReqContext := context.WithValue(requestStartTimeContext, DecodedBatchRequestContextKey, batchRequests)
batchProcessingMiddleware.ServeHTTP(w, r.WithContext(batchDecodedReqContext))
}
}

// createRequestLoggingMiddleware returns a handler that logs any request to stdout
// and if able to decode the request to a known type adds it as a context key
// To use the decoded request body, get the value from the context and then
// use type assertion to EVMRPCRequestEnvelope. With this middleware, the request body
// can be read once, and then accessed by all future middleware and the final
// http handler.
func createRequestLoggingMiddleware(h http.HandlerFunc, serviceLogger *logging.ServiceLogger) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
requestStartTimeContext := context.WithValue(r.Context(), RequestStartTimeContextKey, time.Now())
h.ServeHTTP(w, r.WithContext(requestStartTimeContext))

// TODO: cleanup. is this middleware still useful? should it actually...log the request? lol
}
}

// create the main service middleware for
// introspecting and transforming the original request
// and the backend origin server(s) response(s)
Expand Down
26 changes: 17 additions & 9 deletions service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func New(ctx context.Context, config config.Config, serviceLogger *logging.Servi
// create an http router for registering handlers for a given route
mux := http.NewServeMux()

// will run after the proxy middleware handler and is
// AfterProxyFinalizer will run after the proxy middleware handler and is
// the final function called after all other middleware
// allowing it to access values added to the request context
// to do things like metric the response and cache the response
Expand All @@ -67,7 +67,10 @@ func New(ctx context.Context, config config.Config, serviceLogger *logging.Servi
// - response is present in context
cacheAfterProxyMiddleware := serviceCache.CachingMiddleware(afterProxyFinalizer)

// create an http handler that will proxy any request to the specified URL
// ProxyRequestMiddleware responds to the client with
// - cached data if present in the context
// - a forwarded request to the appropriate backend
// Backend is decided by the Proxies configuration for a particular host.
proxyMiddleware := createProxyRequestMiddleware(cacheAfterProxyMiddleware, config, serviceLogger, []RequestInterceptor{}, []RequestInterceptor{})

// IsCachedMiddleware works in the following way:
Expand All @@ -76,19 +79,24 @@ func New(ctx context.Context, config config.Config, serviceLogger *logging.Servi
// - if not present marks as uncached in context and forwards to next middleware
cacheMiddleware := serviceCache.IsCachedMiddleware(proxyMiddleware)

// TODO: docs
// BatchProcessingMiddleware separates a batch into multiple requests and routes each one
// through the single request middleware sequence.
// This allows the sub-requests of a batch to leverage the cache & metric recording.
// Expects non-zero length batch to be in the context.
batchMdwConfig := batchmdw.BatchMiddlewareConfig{
ServiceLogger: serviceLogger,
ContextKeyDecodedRequestBatch: DecodedBatchRequestContextKey,
ContextKeyDecodedRequestSingle: DecodedRequestContextKey,
}
batchProcessingMiddleware := batchmdw.CreateBatchProcessingMiddleware(cacheMiddleware, &batchMdwConfig)
// TODO: docs
decodeRequestMiddleware := createDecodeRequestMiddleware(cacheMiddleware, batchProcessingMiddleware, serviceLogger)

// create an http handler that will log the request to stdout
// this handler will run before the proxyMiddleware handler
requestLoggingMiddleware := createRequestLoggingMiddleware(decodeRequestMiddleware, serviceLogger)
// DecodeRequestMiddleware captures the request start time & attempts to decode the request body.
// If successful, the decoded request is put into the request context:
// - if decoded as a single EVM request: it forwards it to the single request middleware sequence
// - if decoded as a batch EVM request: it forwards it to the batchProcessingMiddleware
// - if fails to decode: it passes to single request middleware sequence which will proxy the request
// When requests fail to decode, no context value is set.
decodeRequestMiddleware := createDecodeRequestMiddleware(cacheMiddleware, batchProcessingMiddleware, serviceLogger)

// register healthcheck handler that can be used during deployment and operations
// to determine if the service is ready to receive requests
Expand All @@ -99,7 +107,7 @@ func New(ctx context.Context, config config.Config, serviceLogger *logging.Servi
mux.HandleFunc("/servicecheck", createServicecheckHandler(&service))

// register middleware chain as the default handler for any request to the proxy service
mux.HandleFunc("/", requestLoggingMiddleware)
mux.HandleFunc("/", decodeRequestMiddleware)

// create an http server for the caller to start on demand with a call to ProxyService.Run()
server := &http.Server{
Expand Down

0 comments on commit 845d356

Please sign in to comment.