Implementing Request Interceptors in Go
Interceptors are a powerful mechanism for modifying requests before they are sent and responses after they are received. This challenge asks you to implement a system of request interceptors in Go, allowing you to add functionality like logging, authentication, or request transformation without modifying the core request-sending logic. This is a common pattern in distributed systems and microservices architectures.
Problem Description
You are tasked with creating a simple HTTP client with interceptor support. The client should be able to send HTTP requests to a given URL and allow multiple interceptors to be chained together. Each interceptor should have the opportunity to modify the request before it's sent and the response after it's received.
What needs to be achieved:
- Interceptor Interface: Define an
Interceptorinterface with two methods:InterceptRequestandInterceptResponse.InterceptRequesttakes the request as input and can modify it.InterceptResponsetakes the response as input and can modify it. Both methods return the modified request/response. - Client with Interceptor Chain: Create an
HTTPClientstruct that accepts a slice ofInterceptorimplementations in its constructor. The client should have aDomethod that sends an HTTP request, applying each interceptor in the chain sequentially. - Request and Response Structs: Define simple
RequestandResponsestructs to represent the HTTP request and response. These structs should contain at least aURLfield for the request and aBodyfield for the response.
Key Requirements:
- The interceptors should be applied in the order they are provided to the client.
- The
Domethod should handle sending the HTTP request and receiving the response. For simplicity, you can use the standardnet/httppackage for this. - Each interceptor should receive the modified request/response from the previous interceptor in the chain.
- The client should return an error if the HTTP request fails.
Expected Behavior:
The Do method should:
- Start with the original request.
- Pass the request to the first interceptor.
- Pass the modified request to the second interceptor, and so on.
- Send the final modified request using
net/http. - Receive the response.
- Pass the response to the last interceptor.
- Pass the modified response to the second-to-last interceptor, and so on.
- Return the final modified response.
Edge Cases to Consider:
- Empty interceptor chain (no interceptors provided).
- Interceptors that return errors. The client should propagate these errors.
- Invalid URLs.
- Network errors during the HTTP request.
Examples
Example 1:
Input: Client with two interceptors:
Interceptor 1: Adds a header to the request.
Interceptor 2: Logs the request URL.
Request: GET /api/data
Output: Response with the added header and logged URL.
Explanation: Interceptor 1 modifies the request by adding a header. Interceptor 2 logs the URL of the modified request before it's sent. The client then sends the modified request and returns the response.
Example 2:
Input: Client with no interceptors.
Request: GET /api/data
Output: Response from the server.
Explanation: The request is sent directly to the server without any modifications.
Example 3: (Edge Case)
Input: Client with one interceptor that returns an error.
Request: GET /api/data
Output: Error message indicating the interceptor failed.
Explanation: The client should catch the error returned by the interceptor and return it to the caller.
Constraints
- The
Requeststruct must have aURLfield (string). - The
Responsestruct must have aBodyfield (string). - The interceptors should be applied sequentially.
- The
Domethod should use thenet/httppackage for sending requests. - Error handling is crucial. Propagate errors appropriately.
- Assume the URL is always valid for the purpose of this challenge.
Notes
- Consider using interfaces to decouple the client from specific interceptor implementations.
- Think about how to handle errors returned by interceptors.
- This challenge focuses on the core logic of interceptors. You don't need to implement a full-fledged HTTP client with all the features.
- The
InterceptRequestandInterceptResponsemethods should return the modified request/response. If no modification is needed, return the original request/response.