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

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.

Related

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
},
}

Echo CORS w/ Proxy middlewares causes problems w/ Access-Allow-Origins response header

I'm using LabStack's Golang Echo Framework to build out a service.
One of the routes, needs to proxy requests and responses to and from a backend service.
But I also need CORS to work on this service as well.
So I'm using middleware.CORSWithConfig along w/ a middleware.ProxyWithConfig in my request/response stack.
I'm seeing some oddness w/ the Access-Control-Allow-Origins header where the value for that header on the response from the proxied service to my Echo server *, but once it passes through the proxy, it changes to *, * by the time it gets back into the client.
Upon which I start seeing the following browser errors related to CORS violations:
VM1627:362 Access to XMLHttpRequest at 'http://localhost:6273/' from origin 'http://localhost:8002' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed.
Has anyone come across this? Anyone have any idea why this might be happening and maybe a way around it?
Here's some example code:
package main
func singleTargetBalancer(url *url.URL) middleware.ProxyBalancer {
targetURL := []*middleware.ProxyTarget{
{
URL: url,
},
}
return middleware.NewRoundRobinBalancer(targetURL)
}
func Noop(ctx echo.Context) (err error) {
ctx.String(
http.StatusNotImplemented,
"No op handler should never be reached!",
)
return err
}
func main() {
e := echo.New()
e.HideBanner = true
e.Use(
middleware.CORSWithConfig(middlewares.CustomCorsConfig),
middlewares.ThriftMetrics(),
)
// Have to use a Noop handler since we're not trying to set up a full-on proxy for the backend service. We only want this one route to be proxied.
e.POST(
"/",
handlers.Noop,
middleware.ProxyWithConfig(middleware.ProxyConfig{
Balancer: singleTargetBalancer("[backend service URL]"),
})
)
}
I ultimately solved this by writing a custom Echo middleware to hook into the response before Echo's proxy middleware could send the headers back to the client.
func setResponseACAOHeaderFromRequest (req http.Request, resp echo.Response) {
resp.Header().Set(echo.HeaderAccessControlAllowOrigin,
req.Header.Get(echo.HeaderOrigin))
}
func ACAOHeaderOverwriteMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(ctx echo.Context) error {
ctx.Response().Before(func() {
setResponseACAOHeaderFromRequest(*ctx.Request(), *ctx.Response())
})
return next(ctx)
}
}
Then just put this middleware in e.Use() right before your proxy middleware:
e.POST(
"/",
handlers.Noop,
ACAOHeaderOverwriteMiddleware,
middleware.ProxyWithConfig(middleware.ProxyConfig{
Balancer: singleTargetBalancer("[backend service URL]"),
})
)
Docs for Echo's Request::Before() hook: https://echo.labstack.com/guide/response#before-response

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?

Go http response few headers

I'm want send to user alert if he type wrong password and return it to page were he type password. I'm making it like this
func sendJSONHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
http.ServeFile(w, r, "template/api/api.html")
} else if r.Method == "POST" {
r.ParseForm()
if r.Form["password"][0] == "apiPassword" {
j := struct {
Proxies []string
}{Proxies: code.UP.Proxy}
w.Header().Set("Access-Control-Allow-Origin", corsAddrSite)
json.NewEncoder(w).Encode(j)
} else {
// here is a problem
fmt.Fprintln(w, "<script>alert('Wrong Password')</script>")
http.ServeFile(w, r, "template/api/api.html")
}
}
}
But i'v get http: multiple response.WriteHeader calls error.
How to do it right?
You cannot write to the http.ResponseWriter more than once depending on the HTTP spec.
from the go docs https://golang.org/pkg/net/http/#ResponseWriter
To solve your issue, you could have the script tags inside the template file, or make a new template. You could also tailor the response by adding the alert script before you send it. Maybe with template files.
However a proper solution to this problem might be to have more logic in the actual html served, the front end should display a response based on the status code or response body.

Resources