revproxy.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. package engine
  2. import (
  3. "net/http"
  4. "net/url"
  5. log "github.com/sirupsen/logrus"
  6. "github.com/xyproto/algernon/utils"
  7. )
  8. // ReverseProxy holds which path prefix (like "/api") should be sent where (like "http://localhost:8080")
  9. type ReverseProxy struct {
  10. PathPrefix string
  11. Endpoint url.URL
  12. }
  13. // ReverseProxyConfig holds several "path prefix --> URL" ReverseProxy structs,
  14. // together with structures that speeds up the prefix matching.
  15. type ReverseProxyConfig struct {
  16. proxyMatcher utils.PrefixMatch
  17. prefix2rproxy map[string]int
  18. ReverseProxies []ReverseProxy
  19. }
  20. // NewReverseProxyConfig creates a new and empty ReverseProxyConfig struct
  21. func NewReverseProxyConfig() *ReverseProxyConfig {
  22. return &ReverseProxyConfig{}
  23. }
  24. // Add can add a ReverseProxy and will also (re-)initialize the internal proxy matcher
  25. func (rc *ReverseProxyConfig) Add(rp *ReverseProxy) {
  26. rc.ReverseProxies = append(rc.ReverseProxies, *rp)
  27. rc.Init()
  28. }
  29. // DoProxyPass tries to proxy the given http.Request to where the ReverseProxy points
  30. func (rp *ReverseProxy) DoProxyPass(req http.Request) (*http.Response, error) {
  31. client := &http.Client{}
  32. endpoint := rp.Endpoint
  33. req.RequestURI = ""
  34. req.URL.Path = req.URL.Path[len(rp.PathPrefix):]
  35. req.URL.Scheme = endpoint.Scheme
  36. req.URL.Host = endpoint.Host
  37. res, err := client.Do(&req)
  38. if err != nil {
  39. log.Errorf("reverse proxy error: %v\nPlease check your server config for AddReverseProxy calls.\n", err)
  40. return nil, err
  41. }
  42. return res, nil
  43. }
  44. // Init prepares the proxyMatcher and prefix2rproxy fields according to the ReverseProxy structs
  45. func (rc *ReverseProxyConfig) Init() {
  46. keys := make([]string, 0, len(rc.ReverseProxies))
  47. rc.prefix2rproxy = make(map[string]int)
  48. for i, rp := range rc.ReverseProxies {
  49. keys = append(keys, rp.PathPrefix)
  50. rc.prefix2rproxy[rp.PathPrefix] = i
  51. }
  52. rc.proxyMatcher.Build(keys)
  53. }
  54. // FindMatchingReverseProxy checks if the given URL path should be proxied
  55. func (rc *ReverseProxyConfig) FindMatchingReverseProxy(path string) *ReverseProxy {
  56. matches := rc.proxyMatcher.Match(path)
  57. if len(matches) == 0 {
  58. return nil
  59. }
  60. if len(matches) > 1 {
  61. log.Warnf("found more than one reverse proxy for `%s`: %+v. returning the longest", matches, path)
  62. }
  63. var match *ReverseProxy
  64. maxlen := 0
  65. for _, prefix := range matches {
  66. if len(prefix) > maxlen {
  67. maxlen = len(prefix)
  68. match = &rc.ReverseProxies[rc.prefix2rproxy[prefix]]
  69. }
  70. }
  71. return match
  72. }