@@ -23,121 +23,34 @@ import (
2323 "net/http/httputil"
2424 "net/url"
2525 "os"
26- "path"
27- "strings"
28-
29- "github.com/kcp-dev/logicalcluster/v3"
3026
3127 "k8s.io/component-base/metrics/legacyregistry"
3228 "k8s.io/klog/v2"
3329 "sigs.k8s.io/yaml"
3430
3531 "github.com/kcp-dev/kcp/pkg/proxy/index"
3632 proxyoptions "github.com/kcp-dev/kcp/pkg/proxy/options"
33+ "github.com/kcp-dev/kcp/pkg/server/proxy"
3734)
3835
39- // PathMapping describes how to route traffic from a path to a backend server.
40- // Each Path is registered with the DefaultServeMux with a handler that
41- // delegates to the specified backend.
42- type PathMapping struct {
43- Path string `json:"path"`
44- Backend string `json:"backend"`
45- BackendServerCA string `json:"backend_server_ca"`
46- ProxyClientCert string `json:"proxy_client_cert"`
47- ProxyClientKey string `json:"proxy_client_key"`
48- UserHeader string `json:"user_header,omitempty"`
49- GroupHeader string `json:"group_header,omitempty"`
50- ExtraHeaderPrefix string `json:"extra_header_prefix"`
51- }
52-
53- type HttpHandler struct {
54- index index.Index
55- mapping []httpHandlerMapping
56- defaultHandler http.Handler
57- }
58-
59- // httpHandlerMapping is used to route traffic to the correct backend server.
60- // Higher weight means that the mapping is more specific and should be matched first.
61- type httpHandlerMapping struct {
62- weight int
63- path string
64- handler http.Handler
65- }
66-
67- func (h * HttpHandler ) ServeHTTP (w http.ResponseWriter , r * http.Request ) {
68- // mappings are used to route traffic to the correct backend server.
69- // It should not have `/clusters` as prefix because that is handled by the
70- // shardHandler or mounts. Logic is as follows:
71- // 1. We detect URL for the request and find the correct handler. URL can be
72- // shard based, virtual workspace or mount. First two are covered by r.URL,
73- // where mounts are covered by annotation on the workspace with the mount path.
74- // 2. If mountpoint is found, we rewrite the URL to resolve, else use one in
75- // request to match with mappings.
76- // 3. Iterate over mappings and find the one that matches the URL. If found,
77- // use the handler for that mapping, else use default handler - kcp.
78- // Mappings are done from most specific to least specific:
79- // Example: /clusters/cluster1/ will be matched before /clusters/
80- for _ , m := range h .mapping {
81- url , errorCode := h .resolveURL (r )
82- if errorCode != 0 {
83- http .Error (w , http .StatusText (errorCode ), errorCode )
84- return
85- }
86- if strings .HasPrefix (url , m .path ) {
87- m .handler .ServeHTTP (w , r )
88- return
89- }
90- }
91-
92- h .defaultHandler .ServeHTTP (w , r )
93- }
94-
95- func (h * HttpHandler ) resolveURL (r * http.Request ) (string , int ) {
96- // if we don't match any of the paths, use the default behavior - request
97- var cs = strings .SplitN (strings .TrimLeft (r .URL .Path , "/" ), "/" , 3 )
98- if len (cs ) < 2 || cs [0 ] != "clusters" {
99- return r .URL .Path , 0
100- }
101-
102- clusterPath := logicalcluster .NewPath (cs [1 ])
103- if ! clusterPath .IsValid () {
104- return r .URL .Path , 0
105- }
106-
107- u , found , errCode := h .index .LookupURL (clusterPath )
108- if errCode != 0 {
109- return "" , errCode
110- }
111- if found {
112- u , err := url .Parse (u )
113- if err == nil && u != nil {
114- u .Path = strings .TrimSuffix (u .Path , "/" )
115- r .URL .Path = path .Join (u .Path , strings .Join (cs [2 :], "/" )) // override request prefix and keep kube api contextual suffix
116- return u .Path , 0
117- }
118- }
119-
120- return r .URL .Path , 0
121- }
122-
12336func NewHandler (ctx context.Context , o * proxyoptions.Options , index index.Index ) (http.Handler , error ) {
12437 mappingData , err := os .ReadFile (o .MappingFile )
12538 if err != nil {
12639 return nil , fmt .Errorf ("failed to read mapping file %q: %w" , o .MappingFile , err )
12740 }
12841
129- var mapping []PathMapping
42+ var mapping []proxy. PathMapping
13043 if err = yaml .Unmarshal (mappingData , & mapping ); err != nil {
13144 return nil , fmt .Errorf ("failed to unmarshal mapping file %q: %w" , o .MappingFile , err )
13245 }
13346
134- handlers := HttpHandler {
135- index : index ,
136- mapping : [] httpHandlerMapping {
47+ handlers := proxy. HttpHandler {
48+ Index : index ,
49+ Mappings : proxy. HttpHandlerMappings {
13750 {
138- weight : 0 ,
139- path : "/metrics" ,
140- handler : legacyregistry .Handler (),
51+ Weight : 0 ,
52+ Path : "/metrics" ,
53+ Handler : legacyregistry .Handler (),
14154 },
14255 },
14356 }
@@ -185,31 +98,17 @@ func NewHandler(ctx context.Context, o *proxyoptions.Options, index index.Index)
18598
18699 logger .V (2 ).WithValues ("path" , m .Path ).Info ("adding handler" )
187100 if m .Path == "/" {
188- handlers .defaultHandler = handler
101+ handlers .DefaultHandler = handler
189102 } else {
190- handlers .mapping = append (handlers .mapping , httpHandlerMapping {
191- weight : len (m .Path ),
192- path : m .Path ,
193- handler : handler ,
103+ handlers .Mappings = append (handlers .Mappings , proxy. HttpHandlerMapping {
104+ Weight : len (m .Path ),
105+ Path : m .Path ,
106+ Handler : handler ,
194107 })
195108 }
196109 }
197110
198- handlers .mapping = sortMappings ( handlers . mapping )
111+ handlers .Mappings . Sort ( )
199112
200113 return & handlers , nil
201114}
202-
203- func sortMappings (mappings []httpHandlerMapping ) []httpHandlerMapping {
204- // sort mappings by weight
205- // higher weight means that the mapping is more specific and should be matched first
206- // Example: /clusters/cluster1/ will be matched before /clusters/
207- for i := range mappings {
208- for j := range mappings {
209- if mappings [i ].weight > mappings [j ].weight {
210- mappings [i ], mappings [j ] = mappings [j ], mappings [i ]
211- }
212- }
213- }
214- return mappings
215- }
0 commit comments