github.com/cortesi/devd@v0.0.0-20200427000907-c1a3bfba27d8/routespec/routespec.go (about) 1 package routespec 2 3 import ( 4 "errors" 5 "fmt" 6 "net/url" 7 "strings" 8 ) 9 10 const defaultDomain = "devd.io" 11 12 func checkURL(s string) (isURL bool, err error) { 13 var parsed *url.URL 14 15 parsed, err = url.Parse(s) 16 if err != nil { 17 return 18 } 19 20 switch { 21 case parsed.Scheme == "": // No scheme means local file system 22 isURL = false 23 case parsed.Scheme == "http", parsed.Scheme == "https": 24 isURL = true 25 case parsed.Scheme == "ws": 26 err = fmt.Errorf("Websocket protocol not supported: %s", s) 27 default: 28 // A route of "localhost:1234/abc" without the "http" or "https" triggers this case. 29 // Unfortunately a route of "localhost/abc" just looks like a file and is not caught here. 30 err = fmt.Errorf("Unknown scheme '%s': did you mean http or https?: %s", parsed.Scheme, s) 31 } 32 return 33 } 34 35 // A RouteSpec is a parsed route specification 36 type RouteSpec struct { 37 Host string 38 Path string 39 Value string 40 IsURL bool 41 } 42 43 // MuxMatch produces a match clause suitable for passing to a Mux 44 func (rp *RouteSpec) MuxMatch() string { 45 // Path is guaranteed to start with / 46 return rp.Host + rp.Path 47 } 48 49 // ParseRouteSpec parses a string route specification 50 func ParseRouteSpec(s string) (*RouteSpec, error) { 51 seq := strings.SplitN(s, "=", 2) 52 var path, value, host string 53 if len(seq) == 1 { 54 path = "/" 55 value = seq[0] 56 } else { 57 path = seq[0] 58 value = seq[1] 59 } 60 if path == "" || value == "" { 61 return nil, errors.New("Invalid specification") 62 } 63 if path[0] != '/' { 64 seq := strings.SplitN(path, "/", 2) 65 host = seq[0] + "." + defaultDomain 66 switch len(seq) { 67 case 1: 68 path = "/" 69 case 2: 70 path = "/" + seq[1] 71 } 72 } 73 if value[0] == ':' { 74 value = "http://localhost" + value 75 } 76 isURL, err := checkURL(value) 77 if err != nil { 78 return nil, err 79 } 80 return &RouteSpec{host, path, value, isURL}, nil 81 }