go - mux.Vars is empty when using httputil.ReverseProxy -


i trying use gorilla mux , httputil.reverseproxy together, when trying mux.vars empty. according https://golang.org/src/net/http/httputil/reverseproxy.go?s=2744:2819#l93 seems http.request pointer shallow copy of original request, should still work.

any ideas?

https://play.golang.org/p/jpjnvemifb

package main  import (     "github.com/gorilla/mux"     "log"     "net/http"     "net/http/httputil"     "net/url" )  type route struct {     match string     base  string }  var routes = []route{     // proxy http://localhost:3000/api/foo/bar => https://api.bar.com/5/foo/bar     route{match: "/api/{path}", base: "https://api.bar.com/5"},     route{match: "/sales/{path}", base: "https://sales.bar.com/3"}, }  func newproxy(r *route) http.handler {     director := func(req *http.request) {         out, _ := url.parse(r.base)          req.url.scheme = out.scheme         req.url.host = out.host         req.url.path = out.path + "/" + mux.vars(req)["path"] // mux vars empty here     }     return &httputil.reverseproxy{director: director} }  func main() {     _, route := range routes {         http.handle(route.match, newproxy(&route))     }      log.println("listening on port 8080")     http.listenandserve(":8080", nil) } 

you have 2 different problems here.

the first one, not using mux.router, gorilla/mux has not opportunity pre-process request. in other words, requests going directly http package reverse proxies. issue has easy fix:

r := mux.newrouter() _, route := range routes {     r.handle(route.match, newproxy(&route)) } http.handle("/", r) 

the second problem more tricky first one. issue related how mux package implemented. if mux.vars() implementation, see uses called context. context, described in official documentation, stores values shared during request lifetime. simplified context implementation be:

type context map[*http.request]interface{}  func (c context) set(req *http.request, v interface{}) {     c[req] = v }  func (c context) get(req *http.request) interface{} {     return c[req] } 

as see, given http.request, can store values in context. later can retrieve these values using same context , same http.request. mux uses global context store vars parsed in routing process can use standard http.request. but, because httputil.reverseproxy passes copy of actual request , context links values request, new request has no values in context.

to fix it, can implement own reverseproxy based on httputil.reverseproxy:

type myreverseproxy struct {     httputil.reverseproxy     director func(inr, outr *http.request) }  func (p *myreverseproxy) servehttp(rw http.responsewriter, inr *http.request) {     p.reverseproxy.director = func(outr *http.request) {         p.director(inr, outr)     }     p.reverseproxy.servehttp(rw, inr) }  func newproxy(r *route) http.handler {     director := func(inr, outr *http.request) {         out, _ := url.parse(r.base)          outr.url.scheme = out.scheme         outr.url.host = out.host         outr.url.path = out.path + "/" + mux.vars(inr)["path"]           log.printf("in vars: %#v\n", mux.vars(inr)) // inr has proper vars         log.printf("out vars: %#v\n", mux.vars(outr))     }     return &myreverseproxy{director: director} 

you can use context , keep director declaration:

type myreverseproxy struct {     httputil.reverseproxy     director func(req *http.request) }  func (p *myreverseproxy) servehttp(rw http.responsewriter, inr *http.request) {     p.reverseproxy.director = func(outr *http.request) {         context.set(outr, "in_req", inr)         p.director(outr)     }     p.reverseproxy.servehttp(rw, inr) }  func newproxy(r *route) http.handler {     director := func(outr *http.request) {         out, _ := url.parse(r.base)          inr := context.get(outr, "in_req").(*http.request)         outr.url.scheme = out.scheme         outr.url.host = out.host         outr.url.path = out.path + "/" + mux.vars(inr)["path"]          log.printf("in vars: %#v\n", mux.vars(inr)) // inr has proper vars         log.printf("out vars: %#v\n", mux.vars(outr))     }     return &myreverseproxy{director: director} } 

both implementations seem tricky me. have change httputil.reverseproxy's director in every call. so, accept mux not choice here, , instead use simpler solution:

var routes = []route{     route{match: "/api/", base: "https://api.bar.com/5"},     route{match: "/sales/", base: "https://sales.bar.com/3"}, }  func newproxy(r *route) http.handler {     director := func(req *http.request) {         out, _ := url.parse(r.base)          req.url.scheme = out.scheme         req.url.host = out.host         req.url.path = out.path + "/" + strings.trimprefix(req.url.path, r.match)     }     return &httputil.reverseproxy{director: director} } 

you can read mux source code implement complex solution based on regular expressions.


Comments

Popular posts from this blog

angularjs - ADAL JS Angular- WebAPI add a new role claim to the token -

php - CakePHP HttpSockets send array of paramms -

node.js - Using Node without global install -