feat(caching): Implement cache hit detection and response header management

- Added functionality to mark responses as cache hits to prevent incorrect X-Cache headers.
- Introduced setCacheHitFlag function to traverse response writer wrappers and set cache hit flag.
- Updated cachingResponseWriter to manage cache hit state and adjust X-Cache header accordingly.
- Enhanced ProcessRequest and ProcessResponse methods to utilize new caching logic.

feat(extension): Introduce ResponseWriterWrapper and ResponseFinalizer interfaces

- Added ResponseWriterWrapper interface for extensions to wrap response writers.
- Introduced ResponseFinalizer interface for finalizing responses after processing.

refactor(manager): Improve response writer wrapping and finalization

- Updated Manager.Handler to wrap response writers through all enabled extensions.
- Implemented finalization of response writers after processing requests.

test(caching): Add comprehensive integration tests for caching behavior

- Created caching_test.go with tests for cache hit/miss, TTL expiration, pattern-based caching, and more.
- Ensured that caching logic works correctly for various scenarios including query strings and error responses.

test(routing): Add integration tests for routing behavior

- Created routing_test.go with tests for route priority, case sensitivity, default routes, and return directives.
- Verified that routing behaves as expected with multiple regex routes and named groups.
This commit is contained in:
Илья Глазунов
2025-12-12 01:03:32 +03:00
parent 81ac5c4d29
commit bd0b381195
6 changed files with 1275 additions and 20 deletions
+43 -6
View File
@@ -177,26 +177,58 @@ func (m *Manager) Handler(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
// Create response wrapper to capture response for ProcessResponse
wrapper := newResponseWrapper(w)
// Wrap response writer through all extensions that support it
// Process in reverse priority order so highest priority wrapper is outermost
wrappedWriter := w
var finalizers []ResponseFinalizer
m.mu.RLock()
extensions := m.extensions
m.mu.RUnlock()
// Wrap response writer (lowest priority first, so they wrap in correct order)
for _, ext := range extensions {
if !ext.Enabled() {
continue
}
if wrapper, ok := ext.(ResponseWriterWrapper); ok {
wrappedWriter = wrapper.WrapResponseWriter(wrappedWriter, r)
// Check if the wrapped writer implements Finalizer
if finalizer, ok := wrappedWriter.(ResponseFinalizer); ok {
finalizers = append(finalizers, finalizer)
}
}
}
// Create response wrapper to capture status code
responseWrapper := newResponseWrapper(wrappedWriter)
// Process request through extensions
handled, err := m.ProcessRequest(ctx, wrapper, r)
handled, err := m.ProcessRequest(ctx, responseWrapper, r)
if err != nil {
m.logger.Error("Error processing request", "error", err)
}
if handled {
// Extension handled the request, process response
m.ProcessResponse(ctx, wrapper, r)
m.ProcessResponse(ctx, responseWrapper, r)
// Finalize all response writers
for i := len(finalizers) - 1; i >= 0; i-- {
finalizers[i].Finalize()
}
return
}
// No extension handled, pass to next handler
next.ServeHTTP(wrapper, r)
next.ServeHTTP(responseWrapper, r)
// Process response
m.ProcessResponse(ctx, wrapper, r)
m.ProcessResponse(ctx, responseWrapper, r)
// Finalize all response writers
for i := len(finalizers) - 1; i >= 0; i-- {
finalizers[i].Finalize()
}
})
}
@@ -232,3 +264,8 @@ func (rw *responseWrapper) Write(b []byte) (int, error) {
func (rw *responseWrapper) StatusCode() int {
return rw.statusCode
}
// Unwrap returns the underlying ResponseWriter (for type assertions)
func (rw *responseWrapper) Unwrap() http.ResponseWriter {
return rw.ResponseWriter
}