Wait, I can’t modify the incoming http request?4 min read
Recently, I learned that incoming http request can’t directly changed. Let’s create this scenario and see how it happens?
I am using this site to get some dummy response for the API
Naive approach?
func fetchEmployees(w http.ResponseWriter, r *http.Request) {
client := &http.Client{}
r.Host = "dummy.restapiexample.com". // This is the line where we are trying to modify the incoming original request.
resp, err := client.Do(r)
if err != nil {
fmt.Println("Error performing request:", err)
return
}
defer resp.Body.Close()
}
func main() {
http.HandleFunc("/api/v1/employees/", fetchEmployees)
fmt.Println("Server started at https://localhost:8080")
err := http.ListenAndServeTLS(":8080", "./HTTPModifyRequest/server.crt", "./HTTPModifyRequest/server.key", nil)
if err != nil {
log.Fatal("ListenAndServeTLS: ", err)
}
}
You can use any set of certificate generation technique to genertae some key and certificate. Now try to hit the API from any tool you like.
curl --location 'https://localhost:8080/api/v1/employees/'
Server logs as below,
Error performing request: Get "/api/v1/employees/": http: Request.RequestURI can't be set in client requests
Intelligent approach?
I was like, okay!! When we try to modify the original request only, you will be throwing the error, right? How about if make a copy of the incoming request and try to send the cloned request? Oh yeah, I was like, I'm totally intelligent
😛
func fetchEmployeesUsingCloneRequest(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
cloneReq := r.Clone(ctx) // Trying to clone the request and execute the same.
client := &http.Client{}
cloneReq.Host = "dummy.restapiexample.com" // This is the line where we are trying to modify the incoming original request.
resp, err := client.Do(cloneReq)
if err != nil {
fmt.Println("Error performing request:", err)
return
}
defer resp.Body.Close()
}
func main() {
http.HandleFunc("/api/v1/employees/", fetchEmployeesUsingCloneRequest)
fmt.Println("Server started at https://localhost:8080")
err := http.ListenAndServeTLS(":8080", "./HTTPModifyRequest/server.crt", "./HTTPModifyRequest/server.key", nil)
if err != nil {
log.Fatal("ListenAndServeTLS: ", err)
}
}
With in a minute, I was like, I'm a complete idiot
because I got the same error.
Error performing request: Get "/api/v1/employees/": http: Request.RequestURI can't be set in client requests
Next approach?
After doing some search a bit around, got to know that incoming request can’t be modified directly. But instead you could use reverse proxy to execute the request. And go
already has inbuilt package for this. Alright now it time to give a try with the reverse proxy
func fetchEmployeesUsingReverseProxy(w http.ResponseWriter, req *http.Request) {
baseURL := "dummy.restapiexample.com"
targetURL := &url.URL{
Scheme: "https",
Host: baseURL,
}
proxy := httputil.NewSingleHostReverseProxy(targetURL)
proxy.Director = func(r *http.Request) {
r.Host = baseURL
r.URL.Host = r.Host
r.URL.Scheme = "https"
r.Header.Set("Authorization", "Bearer "+"<token_from_user_context>")
}
rr := httptest.NewRecorder() // Create a ResponseRecorder to capture the response
proxy.ServeHTTP(w, req) // Use the proxy to handle the request
// Retrieve the response from the ResponseRecorder
resp := rr.Result()
defer resp.Body.Close()
// Write the response body to the original response writer
_, err := io.Copy(w, resp.Body)
if err != nil {
http.Error(w, "Failed to write response", http.StatusInternalServerError)
return
}
return
}
func main() {
http.HandleFunc("/api/v1/employees/", fetchEmployeesUsingReverseProxy)
fmt.Println("Server started at https://localhost:8080")
err := http.ListenAndServeTLS(":8080", "./HTTPModifyRequest/server.crt", "./HTTPModifyRequest/server.key", nil)
if err != nil {
log.Fatal("ListenAndServeTLS: ", err)
}
}
Here we are trying to modify the proxy request as in we needed.
- In some cases, lets say you’re creating a proxy server? So you got to remove few internal session, token etc from the outgoing request(outside org perhaps).
- In other case, you server might sit and just pass the incoming request from one server to another server. (additionally validates if this request can be forwarded or not?)
Think of any scenario, where we need to modify the incoming request. These scenario’s you might run into when you’re actually creating a proxy or reverse proxy or SSO etc. (where we majorly play with the request)
curl --location 'https://localhost:8080/api/v1/employees/'
{
"status": "success",
"data": [
{
"id": 1,
"employee_name": "Tiger Nixon",
"employee_salary": 320800,
"employee_age": 61,
"profile_image": ""
},
{
"id": 2,
"employee_name": "Garrett Winters",
"employee_salary": 170750,
"employee_age": 63,
"profile_image": ""
}
],
"message": "Successfully! All records has been fetched."
}
Voila! Happy learning folks! Subscribe if you’re interested for more.