/ components / ingress / pkg / proxy / host_route_parse.go
host_route_parse.go
  1  // Copyright 2026 Alibaba Group Holding Ltd.
  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  package proxy
 16  
 17  import (
 18  	"errors"
 19  	"fmt"
 20  	"strconv"
 21  	"strings"
 22  
 23  	"github.com/alibaba/opensandbox/ingress/pkg/signature"
 24  )
 25  
 26  type parsedRoute struct {
 27  	sandboxID       string
 28  	port            int
 29  	expiresB36      string
 30  	signature       string
 31  	requestURI      string
 32  	uriParsedAsOSEP bool
 33  }
 34  
 35  func parseHostRoute(s string) (parsedRoute, error) {
 36  	domain := strings.Split(strings.TrimPrefix(strings.TrimPrefix(s, "https://"), "http://"), ".")
 37  	if len(domain) < 1 {
 38  		return parsedRoute{}, fmt.Errorf("invalid host: %s", s)
 39  	}
 40  	label := domain[0]
 41  
 42  	sandboxID, port, expires, routeSig, parseErr := signature.ParseRouteToken(label)
 43  	if parseErr == nil {
 44  		return parsedRoute{
 45  			sandboxID:  sandboxID,
 46  			port:       port,
 47  			expiresB36: expires,
 48  			signature:  routeSig,
 49  			requestURI: "",
 50  		}, nil
 51  	}
 52  
 53  	ingressAndPort := strings.Split(label, "-")
 54  	if len(ingressAndPort) <= 1 || ingressAndPort[0] == "" {
 55  		return parsedRoute{}, fmt.Errorf("invalid host: %s", s)
 56  	}
 57  	ingress := strings.Join(ingressAndPort[:len(ingressAndPort)-1], "-")
 58  	port, err := strconv.Atoi(ingressAndPort[len(ingressAndPort)-1])
 59  	if err != nil {
 60  		return parsedRoute{}, fmt.Errorf("invalid port format: %w", err)
 61  	}
 62  	return parsedRoute{sandboxID: ingress, port: port, expiresB36: "", signature: "", requestURI: ""}, nil
 63  }
 64  
 65  func parseURIRoute(path string) (parsedRoute, error) {
 66  	if path == "" {
 67  		return parsedRoute{}, errors.New("missing URI path")
 68  	}
 69  
 70  	trimmed := strings.TrimPrefix(path, "/")
 71  	parts := strings.SplitN(trimmed, "/", 5)
 72  	if len(parts) >= 4 && parts[0] != "" {
 73  		port, perr := signature.ParsePortSegment(parts[1])
 74  		if perr == nil {
 75  			if _, eerr := signature.ParseExpiresB36(parts[2]); eerr == nil {
 76  				if signature.ValidateSignatureFormat(parts[3]) == nil {
 77  					requestURI := "/"
 78  					if len(parts) == 5 && parts[4] != "" {
 79  						requestURI = "/" + parts[4]
 80  					}
 81  					return parsedRoute{
 82  						sandboxID:       parts[0],
 83  						port:            port,
 84  						expiresB36:      parts[2],
 85  						signature:       parts[3],
 86  						requestURI:      requestURI,
 87  						uriParsedAsOSEP: true,
 88  					}, nil
 89  				}
 90  			}
 91  		}
 92  	}
 93  	return parseURILegacy(path)
 94  }
 95  
 96  func parseURILegacy(path string) (parsedRoute, error) {
 97  	trimmed := strings.TrimPrefix(path, "/")
 98  	parts := strings.SplitN(trimmed, "/", 3)
 99  	if len(parts) < 2 {
100  		return parsedRoute{}, fmt.Errorf("invalid URI path format: expected '/<sandbox-id>/<sandbox-port>/<path-to-request>', got: %s", path)
101  	}
102  	sandboxID := parts[0]
103  	if sandboxID == "" {
104  		return parsedRoute{}, errors.New("missing sandbox-id or sandbox-port in URI path")
105  	}
106  	port, err := signature.ParsePortSegment(parts[1])
107  	if err != nil {
108  		return parsedRoute{}, fmt.Errorf("invalid port format: %w", err)
109  	}
110  	var requestURI string
111  	if len(parts) >= 3 && parts[2] != "" {
112  		requestURI = "/" + parts[2]
113  	} else {
114  		requestURI = "/"
115  	}
116  	return parsedRoute{
117  		sandboxID:       sandboxID,
118  		port:            port,
119  		expiresB36:      "",
120  		signature:       "",
121  		requestURI:      requestURI,
122  		uriParsedAsOSEP: false,
123  	}, nil
124  }