I'm trying to set a header in my Go web server. I'm using gorilla/mux and net/http packages.
I'd like to set Access-Control-Allow-Origin: * to allow cross domain AJAX.
Here's my Go code:
func saveHandler(w http.ResponseWriter, r *http.Request) {
// do some stuff with the request data
}
func main() {
r := mux.NewRouter()
r.HandleFunc("/save", saveHandler)
http.Handle("/", r)
http.ListenAndServe(":"+port, nil)
}
The net/http package has documentation describing sending http request headers as if it were a client - I'm not exactly sure how to set response headers?
Never mind, I figured it out - I used the Set() method on Header() (doh!)
My handler looks like this now:
func saveHandler(w http.ResponseWriter, r *http.Request) {
// allow cross domain AJAX requests
w.Header().Set("Access-Control-Allow-Origin", "*")
}
Maybe this will help someone as caffeine deprived as myself sometime :)
All of the above answers are wrong because they fail to handle the OPTIONS preflight request, the solution is to override the mux router's interface. See AngularJS $http get request failed with custom header (alllowed in CORS)
func main() {
r := mux.NewRouter()
r.HandleFunc("/save", saveHandler)
http.Handle("/", &MyServer{r})
http.ListenAndServe(":8080", nil);
}
type MyServer struct {
r *mux.Router
}
func (s *MyServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
if origin := req.Header.Get("Origin"); origin != "" {
rw.Header().Set("Access-Control-Allow-Origin", origin)
rw.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
rw.Header().Set("Access-Control-Allow-Headers",
"Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
}
// Stop here if its Preflighted OPTIONS request
if req.Method == "OPTIONS" {
return
}
// Lets Gorilla work
s.r.ServeHTTP(rw, req)
}
Do not use '*' for Origin, until You really need a completely public behavior.
As Wikipedia says:
"The value of "*" is special in that it does not allow requests to supply credentials,
meaning HTTP authentication, client-side SSL certificates, nor does it allow cookies
to be sent."
That means, you'll get a lot of errors, especially in Chrome when you'll try to implement for example a simple authentication.
Here is a corrected wrapper:
// Code has not been tested.
func addDefaultHeaders(fn http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if origin := r.Header.Get("Origin"); origin != "" {
w.Header().Set("Access-Control-Allow-Origin", origin)
}
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token")
w.Header().Set("Access-Control-Allow-Credentials", "true")
fn(w, r)
}
}
And don't forget to reply all these headers to the preflight OPTIONS request.
If you don't want to override your router (if you don't have your app configured in a way that supports this, or want to configure CORS on a route by route basis), add an OPTIONS handler to handle the pre flight request.
Ie, with Gorilla Mux your routes would look like:
accounts := router.Path("/accounts").Subrouter()
accounts.Methods("POST").Handler(AccountsCreate)
accounts.Methods("OPTIONS").Handler(AccountsCreatePreFlight)
Note above that in addition to our POST handler, we're defining a specific OPTIONS method handler.
And then to actual handle the OPTIONS preflight method, you could define AccountsCreatePreFlight like so:
// Check the origin is valid.
origin := r.Header.Get("Origin")
validOrigin, err := validateOrigin(origin)
if err != nil {
return err
}
// If it is, allow CORS.
if validOrigin {
w.Header().Set("Access-Control-Allow-Origin", origin)
w.Header().Set("Access-Control-Allow-Methods", "POST")
w.Header().Set("Access-Control-Allow-Headers",
"Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
}
What really made this all click for me (in addition to actually understanding how CORS works) is that the HTTP Method of a preflight request is different from the HTTP Method of the actual request. To initiate CORS, the browser sends a preflight request with HTTP Method OPTIONS, which you have to handle explicitly in your router, and then, if it receives the appropriate response "Access-Control-Allow-Origin": origin (or "*" for all) from your application, it initiates the actual request.
I also believe that you can only do "*" for standard types of requests (ie: GET), but for others you'll have to explicitly set the origin like I do above.
Set a proper golang middleware, so you can reuse on any endpoint.
Helper Type and Function
type Adapter func(http.Handler) http.Handler
// Adapt h with all specified adapters.
func Adapt(h http.Handler, adapters ...Adapter) http.Handler {
for _, adapter := range adapters {
h = adapter(h)
}
return h
}
Actual middleware
func EnableCORS() Adapter {
return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if origin := r.Header.Get("Origin"); origin != "" {
w.Header().Set("Access-Control-Allow-Origin", origin)
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
w.Header().Set("Access-Control-Allow-Headers",
"Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
}
// Stop here if its Preflighted OPTIONS request
if r.Method == "OPTIONS" {
return
}
h.ServeHTTP(w, r)
})
}
}
Endpoint
REMEBER! Middlewares get applyed on reverse order( ExpectGET() gets fires first)
mux.Handle("/watcher/{action}/{device}",Adapt(api.SerialHandler(mux),
api.EnableCORS(),
api.ExpectGET(),
))
I create wrapper for this case:
func addDefaultHeaders(fn http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
fn(w, r)
}
}
I had the same issue as described above the solutions given above are correct, the set up I have is as follows
1) Angularjs for the Client
2) Beego framework for GO server
Please following these points
1) CORS settings must be enabled only on GO server
2) Do NOT add any type of headers in angularJS except for this
.config(['$httpProvider', function($httpProvider) {
$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];
}])
In you GO server add the CORS settings before the request starts to get processed so that the preflight request receives a 200 OK after which the the OPTIONS method will get converted to GET,POST,PUT or what ever is your request type.
I know this is a different twist on the answer, but isn't this more of a concern for a web server? For example, nginx, could help.
The ngx_http_headers_module module allows adding the “Expires” and “Cache-Control” header fields, and arbitrary fields, to a response header
...
location ~ ^<REGXP MATCHING CORS ROUTES> {
add_header Access-Control-Allow-Methods POST
...
}
...
Adding nginx in front of your go service in production seems wise. It provides a lot more feature for authorizing, logging,and modifying requests. Also, it gives the ability to control who has access to your service and not only that but one can specify different behavior for specific locations in your app, as demonstrated above.
I could go on about why to use a web server with your go api, but I think that's a topic for another discussion.
Related
I've got a POST request from my frontend app to my backend app written in Go using Gin. I was getting an error saying:
"No 'Access-Control-Allow-Origin' header is present on the requested resource"
so that pointed me to implement CORS in my backend app. So I did by using "github.com/gin-contrib/cors":
web.go:
func NewApp() *App {
db := SetUpDB()
router := gin.Default()
router.Use(cors.New(cors.Config{
//AllowOrigins: []string{"http://localhost:3000", "http://127.0.0.1:3000"},
AllowMethods: []string{"PUT", "POST", "GET", "OPTIONS","DELETE"},
AllowHeaders: []string{"Origin"},
AllowAllOrigins: true,
//ExposeHeaders: []string{"Content-Length"},
AllowCredentials: true,
MaxAge: 12 * time.Hour,
}))
return &App{
Db: db,
Router: router,
}
}
and in main.go I've got:
app := core.NewApp()
//app.Router.Use(CORS())
defer func() {
app.Db.Close()
log.Printf("core: database stopping")
}()
app.Router.Use(func(c *gin.Context) {
c.Set("db", app.Db)
})
app.Router.GET("/", func(ctx *gin.Context) {
ctx.JSON(http.StatusOK, gin.H{"data": "welcome TEST"})
})
// Initialize all api routes
routes.InitializeRoutes(app.Router.Group("/api/v1"))
as you can see I only set PUT in AllowMethods with the intention of testing CORS was actually working. By allowing only PUT I was expecting no methods other than PUT were allowed but I was wrong. I've performed a GET request from my frontend app and it goes through (it returns data), this leads me to think than the CORS implementation is not being picked up.
While browsing around, I've found people not using the package "github.com/gin-contrib/cors" but creating their own middleware:
func CORS() gin.HandlerFunc {
return func(c *gin.Context) {
fmt.Println(c.Request.Header)
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, Origin, Cache-Control, X-Requested-With")
//c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE")
c.Writer.Header().Set("Access-Control-Allow-Methods", "PUT, DELETE")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
}
}
and then:
func NewApp() *App {
db := SetUpDB()
router := gin.Default()
router.Use(CORS())
return &App{
Db: db,
Router: router,
}
}
I tried this as well with no luck. Same results are coming back.
Furthermore, when I perform the GET and print the method in my backend (c.Request.Method) the result is GET. But when I perform a POST and print the method I'm getting OPTIONS
What am I missing? Why router is not using the provided middleware?
There are two pieces to this question:
The first one was indicated above by Heiko: Get is a simple request so the result is always gonna be returned for these kind of requests.
Now, after testing back my POST, I was still getting errors. I had checked over and over the CORS config, changing things here and there just to find out that the routes for Category were define such as:
categoryRouter.POST("/", controllers.CreateNewCategory)
categoryRouter.GET("/", controllers.ListAllCategories)
as you can see there is a trailing / which was causing my request to be redirected and an error to be returned since the url used for the request was http://127.0.0.1:8080/api/v1/categories. I updated the routes to be:
categoryRouter.POST("", controllers.CreateNewCategory)
categoryRouter.GET("", controllers.ListAllCategories)
and now it is working as expected.
The Access-Control-Allow-Methods header is only checked for CORS requests that cannot result from a Javascript-less HTML page (so-called non-simple requests). For simple requests such as GET with only standard headers, only the Access-Control-Allow-Origin header is checked and the Access-Control-Allow-Methods header plays no role.
I'm doing some requests through some proxy servers. The function that defines which proxy url to use will choose randomly from a list of proxies. I would like to know for a given request, which proxy url is being used. As far as I know, when using a proxy server the http headers remain the same, but the tcp headers are the one that change.
Here's some code illustrating it (no error handling for simplicity):
func main() {
transport := &http.Transport{Proxy: chooseProxy}
client := http.Client{Transport: transport}
request, err := http.NewRequest(http.MethodGet, "https://www.google.com", nil)
checkErr(err)
// How to know here which proxy was used? Suppose the same client will perform several requests to different URL's.
response, err := client.Do(request)
checkErr(err)
dump, _ := httputil.DumpRequest(response.Request, false)
fmt.Println(dump)
}
func chooseProxy(request *http.Request) (*url.URL, error) {
proxies := []string{"proxy1", "proxy2", "proxy3"}
proxyToUse := proxies[rand.Intn(len(proxies))]
return url.Parse(proxyToUse)
}
I'm assuming that the Proxy function in the transport is called for each request even if the same client is used, as per the docs that say "Proxy specifies a function to return a proxy for a given Request". Am I right?
Some HTTP proxies add a Via header that tell who they are.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Via
You can modify your chooseProxy function so that it saves the proxy selected.
To do that, you can transform the chooseProxy func into a method of a type that will be used as storage for the information you want to keep:
type proxySelector string
func (sel *proxySelector) chooseProxy(request *http.Request) (*url.URL, error) {
proxies := []string{"proxy1", "proxy2", "proxy3"}
proxyToUse := proxies[rand.Intn(len(proxies))]
*sel = proxySelector(proxyToUse) // <-----
return url.Parse(proxyToUse)
}
func main() {
var proxy proxySelector
transport := &http.Transport{Proxy: proxy.chooseProxy} // <-----
client := http.Client{Transport: transport}
request, err := http.NewRequest(http.MethodGet, "https://www.google.com", nil)
checkErr(err)
// How to know here which proxy was used? Suppose the same client will perform several requests to different URL's.
response, err := client.Do(request)
checkErr(err)
dump, _ := httputil.DumpRequest(response.Request, false)
fmt.Println(dump)
fmt.Println("Proxy:", string(proxy)) // <-----
}
The request which contains the target URI is given as argument request to chooseProxy. So you can have the correct mapping already inside your chooseProxy function, all you need to to is check proxyToUse vs. request.URL there.
If you don't really trust the code that this mapping is actually done, then you need to look outside the code. For example you can look at the actual network traffic with Wireshark to see which proxy gets accessed.
I am having trouble getting access to a custom respoone header being provided by our backend server. Server is written in Go. I am attempting to access it using axios as my HTTP client. I can see the header in the chrome dev-tools console, but I cannot get access to the header through axios.
chrome dev tools response headers. I am looking to get the x-***-token at the bottom
headers I have access to through axios (cors enabled)
I can, however, access the header through axios when I run chrome with cors disabled:
open -n -a /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --args --user-data-dir="/tmp/chrome_dev_test" --disable-web-security
headers I have access to through axios (cors disabled)
This tells me that its not an issue with axios, but with the header configuration I have.
Below is how we have configured our headers, I am trying to get access the x-custom-token header:
func allowCORS(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if origin := r.Header.Get("Origin"); origin != "" && contains(cors, origin) {
w.Header().Set("Access-Control-Allow-Origin", origin)
}
if contains(cors, "*") {
w.Header().Set("Access-Control-Allow-Origin", "*")
}
if r.Method == "OPTIONS" || r.Method == "GET" && r.Header.Get("Access-Control-Request-Method") != "" &&
w.Header().Get("Access-Control-Allow-Origin") != "" {
headers := []string{"Content-Type", "Accept-Encoding", "X-CSRF-Token", "Authorization", "accept", "origin", "Cache-Control", "X-Requested-With", "x-custom-token"}
w.Header().Set("Access-Control-Allow-Headers", strings.Join(headers, ","))
methods := []string{"GET", "HEAD", "POST", "PUT", "DELETE"}
w.Header().Set("Access-Control-Allow-Methods", strings.Join(methods, ","))
expose := []string{"x-custom-token"}
w.Header().Set("Access-Control-Expose-Headers", strings.Join(expose, ","))
return
}
h.ServeHTTP(w, r)
})}
Can anyone tell me what header I have set incorrectly?
Your issue is that you are only exposing for OPTIONS requests.
In your if statement, one of your criteria are r.Header.Get("Access-Control-Request-Method") != "".
This header is typically only ever used in pre-flight requests and so your expose headers are not getting written on GET or other methods.
In addition, your statement returns after writing the headers. This is correct for pre-flight but would cause no content to be sent for other methods.
Solution - adjust your logic to handle exposing headers separately from the pre-flight headers.
I have been using golang's default http.ServeMux for http route handling.
wrap := func(h func(t *MyStruct, w http.ResponseWriter, r *http.Request)) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
h(t, w, r)
}
}
// Register handlers with default mux
httpMux := http.NewServeMux()
httpMux.HandleFunc("/", wrap(payloadHandler))
Assume this server is accessible via http://example.com/
Very few of my client's requests were of path http://example.com/api//module (note the extra slash) which is redirected as 301 Moved Permanently. Exploring inside golang's http ServeMux.Handler(r *Request) function, seems it's intended.
path := cleanPath(r.URL.Path)
// if there is any change between actual and cleaned path, the request is 301 ed
if path != r.URL.Path {
_, pattern = mux.handler(host, path)
url := *r.URL
url.Path = path
return RedirectHandler(url.String(), StatusMovedPermanently), pattern
}
I've looked into other similar issue.
go-web-server-is-automatically-redirecting-post-requests
Above qn has problem with redundant / in register pattern itself, but my use case is not with register pattern (in some nested path which is irrelevent to register pattern)
Problem is, since my client's requests are POST, browsers handle 301 with new GET request with exact query params and POST body. But change in the HTTP method causes the request to fail.
I have already instructed client to fix the redundant / in url, but the fix might take few (?) weeks to be deployed in all client locations.
Also these redundant / are handled fine in Apache Tomcat, but fails only in golang server. So is this the intended behaviour in my use case (redundant / in nested path) with golang or possible bug?
I am thinking of way to override the Handler func of ServeMux, but it won't be useful since Handler calls are made internally. Looking to disable this 301 behaviour, help would be appreciated.
Relevant links
http-post-method-is-actally-sending-a-get
The clean and redirect is intended behavior.
Wrap the mux with a handler that removes the double slashes:
type slashFix struct {
mux http.Handler
}
func (h *slashFix) ServeHTTP(w http.ResponseWriter, r *http.Request) {
r.URL.Path = strings.Replace(r.URL.Path, "//", "/", -1)
h.mux.ServeHTTP(w, r)
}
Use it like this:
httpMux := http.NewServeMux()
httpMux.HandleFunc("/", wrap(payloadHandler))
http.ListenAndServe(addr, &slashFix{httpMux})
Accepeted answer solved the problem
One more way is to use Gorilla mux and setting SkipClean(true). But be sure to know about the side effects in its doc
SkipClean defines the path cleaning behaviour for new routes. The initial value is false. Users should be careful about which routes are not cleaned. When true, if the route path is "/path//to", it will remain with the double slash. This is helpful if you have a route like: /fetch/http://xkcd.com/534/
When false, the path will be cleaned, so /fetch/http://xkcd.com/534/ will become /fetch/http/xkcd.com/534
func (r *Router) SkipClean(value bool) *Router {
r.skipClean = value
return r
}
Go's default HTTP server implementation merges slashes in HTTP requests, returning an HTTP redirect response to the "cleaned" path:
https://code.google.com/p/go/source/browse/src/pkg/net/http/server.go#1420
So if you make a HTTP request GET /http://foo.com/, the server responds with 301 Moved Permanently ... Location: /http:/foo.com/.
I'd like to disable this behaviour and handle all paths myself.
I'm a Go newbie, and it seems as if I could create my own Server instance and override the Handler attribute, but I'm not sure how to?
I'd like to disable this behaviour and handle all paths myself.
I'm a Go newbie, and it seems as if I could create my own Server instance and override the Handler attribute, but I'm not sure how to?
Instead of registering handlers with the http.DefaultServeMux through the http.Handle or http.HandleFunc methods just call:
http.ListenAndServe(":8080", MyHandler)
where MyHandler is an instance of a type that implements the http.Handler interface.
http.ListenAndServe in turn is just a short-hand method that does the following:
func ListenAndServe(addr string, handler http.Handler) error {
server := &http.Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
so you could do that directly instead as well.
Inside your handler you can then parse/route the URI however you wish like this:
func (h *MyHandlerType) ServeHTTP(w http.ResponseWriter, r *http.Request) {
uri := r.URL.Path
// ...use uri...
}