http.NewRequest allow only one redirect - go

I came from this question and the answer works if I want no redirect at all:
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
But how can I allow exactly one redirect here?

As the documentation states:
The arguments req and via are the upcoming request and the requests made already, oldest first.
So at the first redirect, len(via) will be 1. If you return error if len(via)>1, it should fail for additional requests.

Related

Golang removes double slashes and fires GET instead POST requests , how can I skip this in Cloud Functions?

I have a Cloud function where the same endpoint accepts 2 methods: POST and GET.
My problem is when the client tries to upload a multipart/form-data file through a POST request and by mistake the url contains double slashes, Golang redirects to GET method.
I have looked some replies where they talk about the Clean method https://golang.org/src/path/path.go?s=2443:2895#L74. And how the Mux under the hod is redirecting to GET.
Is there any way where I can check if that request has been redirected? so I can decide if the client has typed double slashes send a 400 Response for example instead the response from the logic in the GET method. I can't find that info in the headers. fmt.Printf("%+v", r)
Is there any way to skip the Clean method and accept the double slashes?.
Endpoint: https://google.com/hello/folder/folder//image.jpg
package test
func Hello(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
//some logic here
return
case "POST":
//some logic here
return
default:
fmt.Fprintf(w, "Sorry, only GET and POST methods are supported.")
return
}
}
Thanks.

How to get the redirected url in golang

When I request the url_a, the server will redirect the request to the url_b.
How to get the redirected url_b when I do the request in golang?
The default HTTP client follows redirects. If you want to handle redirects yourself or simply not follow them, set the http Client CheckRedirect function:
cli := &http.Client{
CheckRedirect: func(req *Request, via []*Request) error {
return http.ErrUseLastResponse;
},
}
cli.Get(...)
When you return ErrUseLastResponse, the GET method will return the last response unmodified.

307 redirect with Authorization header

In looking at the Go docs for http it looks like the Authorization header is removed when a response is a 307. Obviously it makes sense for almost every case but is there a way not to remove the Authorization header?
You can modify your http.Client to add the header again after it has been removed using CheckRedirect:
CheckRedirect func(req *Request, via []*Request) error
Since req is the upcoming request, it can be modified before it is sent. After making the changes, return nil to indicate that the request should still be sent.
Since this is a change to the http client instead of the request, you should check that this redirect is only used for the one URL where you need it (in case you use that client to do other requests).
You client definition could look like this:
http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
// you can check old responses for a status code
if len(via) != 0 && via[0].Response.StatusCode == http.StatusTemporaryRedirect {
req.Header.Add("Authorization", "some-value")
}
return nil
},
}

How do I redirect a GET request to a POST request with some data?

When a user hits a certain url with a GET request I'd like to redirect them to a POST request at another location.
package main
import (
"bytes"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
)
func old(w http.ResponseWriter, r *http.Request) {
newURL := "/new"
var bdy = []byte(`title=Buy cheese and bread for breakfast.`)
r.Method = "POST"
r.URL, _ = url.Parse(newURL)
r.RequestURI = newURL
r.Body = ioutil.NopCloser(bytes.NewReader(bdy))
r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
http.Redirect(w, r, newURL, 302)
}
func new(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
fmt.Printf("Method:%v\n", r.Method)
fmt.Printf("Title:%v\n", r.Form.Get("title"))
}
func main() {
http.HandleFunc("/", old)
http.HandleFunc("/new", new)
port := 8000
fmt.Printf("listening on %v\n", port)
if err := http.ListenAndServe(fmt.Sprintf(":%v", port), nil); err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
When I hit "/" I end up getting redirected to "/new" but with a GET request and no form data:
Method:GET
Title:
If I curl "/new" directly I get :
curl -XPOST localhost:8000/new -d "title=Buy cheese and bread for breakfast."
Method:POST
Title:Buy cheese and bread for breakfast.
A HTTP redirect (i.e. reply with status code 301, 302, 307,308 and Location header) can only redirect the existing request to another location and not change the payload of the request. It can add some cookies in the response header though.
In order to automatically change a GET request into a POST request with a specific payload you might try to send the client a HTML page with a <form method=POST... and the payload with hidden input fields, i.e. <input name=... value=... type=hidden> and then add some JavaScript to the page which automatically submits the form. But this kind of hack will only work in browsers and only if JavaScript is enabled and will not work with all kind of payloads either.
To keep compatibility with a broader range of clients it is probably better to design it differently, i.e. keep the GET request in the redirect but give the necessary payload as a parameter to the new target, i.e. http://new.target/foo?payload=..... But the details depend on what the target of the request can deal with.
Unfortunately I don't believe a redirect can change the verb (e.g., GET, POST) or add data to the request. It can only change the URL.
See Redirect () for more information.
I've never heard about changing verb from GET to POST. I guess it's impossible because POST supposes body of body (however may be empty) and GET doesn't. So in general case browser would not be able to take the body from nothing.
Otherwise is possible: you may send 302 redirect after post to make browser perform get. Also verb can be kept with 307 reply code.
Try to rethink browser-server interaction. May be you can redirect POST to another location to solve a task?

Negroni continues to call other handlers after request is completed

My web-application in Go (using Gorilla mux and negroni) has about 20 handlers split into three groups based on what Middleware functions should be applied. Specifically:
Group 1: Static requests (no middleware at all)
GET /favicon.ico
GET /files
GET /files/index.html
GET /files/favicon.ico
Group 2: Requests that should have CORS middleware only, no authentication:
GET /
GET /login
POST /login
GET /auth-configuration
GET /service-status
Group 3: Requests that should have both CORS and authentication middleware applied:
GET /articles
POST /articles
PUT /articles/etc
PATCH /articles/etc
This is my code that sets-up the HTTP server:
func run() {
negroniStack := setUpNegroni()
bindAddr := // ...
http.ListenAndServe(bindAddr, negroniStack)
}
func setUpNegroni() negroni.Negroni {
negroniStack := negroni.Negroni{}
staticNegroni := setUpRoutesAndMiddlewareForStaticRequests()
loginNegroni := setUpRoutesAndMiddlewareForLogin()
serviceNegroni = setUpRoutesAndMiddlewareForService()
negroniStack.UseHandler(&staticNegroni)
negroniStack.UseHandler(&loginNegroni)
negroniStack.UseHandler(&serviceNegroni)
return negroniStack
}
func setUpRoutesAndMiddlewareForStaticRequests() negroni.Negroni {
staticNegroni := negroni.Negroni{}
staticRouter := mux.NewRouter()
staticRouter.PathPrefix("/files").HandlerFunc(staticHandler)
staticRouter.Path("/favicon.ico").HandlerFunc(staticHandler)
staticNegroni.UseHandler(staticRouter)
return staticNegroni
}
func setUpRoutesAndMiddlewareForLogin() negroni.Negroni {
authNegroni := negroni.Negroni{}
corsMiddleware := cors.New(cors.Options{
AllowedMethods: []string{"GET", "HEAD", "POST", "PUT", "PATCH", "DELETE"},
AllowCredentials: true,
OptionsPassthrough: false,
})
authNegroni.Use(corsMiddleware)
authRouter := mux.NewRouter()
authRouter.HandleFunc("/login", HandlePostAuth).Methods("POST")
authRouter.HandleFunc("/login", HandleGetAuth) // GET
authNegroni.UseHandler(authRouter)
return authNegroni
}
func setUpRoutesAndMiddlewareForService() negroni.Negroni {
serviceNegroni := negroni.Negroni{}
corsMiddleware := cors.New(cors.Options{
AllowedMethods: []string{"GET", "HEAD", "POST", "PUT", "PATCH", "DELETE"},
AllowCredentials: true,
OptionsPassthrough: false,
})
serviceNegroni.Use(corsMiddleware)
serviceNegroni.UseFunc(jwtMiddleware)
serviceRouter := mux.NewRouter()
serviceRouter.HandleFunc("/articles", HandleGetArticles).Methods("GET")
serviceRouter.HandleFunc("/articles", HandlePostArticles).Methods("POST")
// etc
serviceNegroni.UseHandler(serviceRouter)
return serviceNegroni
}
I believe this is correct based on the "Route Specific Middleware" section in Negroni's documentation where it says:
If you have a route group of routes that need specific middleware to be executed, you can simply create a new Negroni instance and use it as your route handler.
However, when I make requests and use the debugger, I see that (*Negroni).ServeHTTP is called multiple times. For example, if I request GET /favicon.ico then the staticHandler function is called correctly and calls WriteHeader(200), but after that it then calls into the next mux.Router which calls WriteHeader(404) which prints out a warning in the terminal because the header was written twice (http: multiple response.WriteHeader calls)
If it's for a route that doesn't exist then the Gorilla default NotFoundHandler is invoked 3 times (one for each mux.Router).
How do I get Negroni to stop invoking other handlers after the request was completed?
...and if I have misconfigured my Negroni instance, why doesn't it perform checks during initialization to warn me about an invalid configuration?
My understanding is that negroni.Use and UseFunc are for setting-up middleware (which are all invoked for every request), while UseHandler is to set-up the terminal handler (only 1 is invoked for each request, or fallback to 404). If I understand the situation correctly then for some reason it's treating my terminal handlers as middlewares.
From the UseHandler documentation (https://godoc.org/github.com/urfave/negroni#Negroni.UseHandler)
UseHandler adds a http.Handler onto the middleware stack. Handlers are invoked in the order they are added to a Negroni.
So it seems what you are seeing here is the expected behaviour.
You are basically creating different negroni instances and chaining them, so your final negroniStack is a middleware itself which will execute the other middlewares you added.
I believe what you want to do is create routes using an actual router, then add the appropriate middleware (using negroni) to each route.
If you look at the example you linked from the docs, that's what they are doing in that section (https://github.com/urfave/negroni#route-specific-middleware).
router.PathPrefix("/admin").Handler(negroni.New(
Middleware1,
Middleware2,
negroni.Wrap(adminRoutes),
))
See that they are not nesting negroni instances but rather creating just one which is applied to the desired routes.

Resources