github.com/cs3org/reva/v2@v2.27.7/internal/http/services/reverseproxy/reverseproxy.go (about) 1 // Copyright 2018-2021 CERN 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 // In applying this license, CERN does not waive the privileges and immunities 16 // granted to it by virtue of its status as an Intergovernmental Organization 17 // or submit itself to any jurisdiction. 18 19 package reverseproxy 20 21 import ( 22 "encoding/json" 23 "net/http" 24 "net/http/httputil" 25 "net/url" 26 "os" 27 28 ctxpkg "github.com/cs3org/reva/v2/pkg/ctx" 29 "github.com/cs3org/reva/v2/pkg/rhttp/global" 30 "github.com/go-chi/chi/v5" 31 "github.com/mitchellh/mapstructure" 32 "github.com/rs/zerolog" 33 ) 34 35 func init() { 36 global.Register("reverseproxy", New) 37 } 38 39 type proxyRule struct { 40 Endpoint string `mapstructure:"endpoint" json:"endpoint"` 41 Backend string `mapstructure:"backend" json:"backend"` 42 } 43 44 type config struct { 45 ProxyRulesJSON string `mapstructure:"proxy_rules_json"` 46 } 47 48 func (c *config) init() { 49 if c.ProxyRulesJSON == "" { 50 c.ProxyRulesJSON = "/etc/revad/proxy_rules.json" 51 } 52 } 53 54 type svc struct { 55 router *chi.Mux 56 } 57 58 // New returns an instance of the reverse proxy service 59 func New(m map[string]interface{}, log *zerolog.Logger) (global.Service, error) { 60 conf := &config{} 61 if err := mapstructure.Decode(m, conf); err != nil { 62 return nil, err 63 } 64 conf.init() 65 66 f, err := os.ReadFile(conf.ProxyRulesJSON) 67 if err != nil { 68 return nil, err 69 } 70 71 var rules []proxyRule 72 err = json.Unmarshal(f, &rules) 73 if err != nil { 74 return nil, err 75 } 76 77 r := chi.NewRouter() 78 79 for _, rule := range rules { 80 remote, err := url.Parse(rule.Backend) 81 if err != nil { 82 // Skip the rule if the backend is not a valid URL 83 continue 84 } 85 86 proxy := httputil.NewSingleHostReverseProxy(remote) 87 handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 88 r.Host = remote.Host 89 if token, ok := ctxpkg.ContextGetToken(r.Context()); ok { 90 r.Header.Set(ctxpkg.TokenHeader, token) 91 } 92 proxy.ServeHTTP(w, r) 93 }) 94 r.Mount(rule.Endpoint, handler) 95 } 96 97 _ = chi.Walk(r, func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error { 98 log.Debug().Str("service", "reverseproxy").Str("method", method).Str("route", route).Int("middlewares", len(middlewares)).Msg("serving endpoint") 99 return nil 100 }) 101 102 return &svc{router: r}, nil 103 } 104 105 func (s *svc) Close() error { 106 return nil 107 } 108 109 func (s *svc) Prefix() string { 110 // This service will be served at root 111 return "" 112 } 113 114 func (s *svc) Unprotected() []string { 115 // TODO: If the services which will be served via the reverse proxy have unprotected endpoints, 116 // we won't be able to support those at the moment. 117 return []string{} 118 } 119 120 func (s *svc) Handler() http.Handler { 121 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 122 s.router.ServeHTTP(w, r) 123 }) 124 }