github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/utils/earl/url.go (about)

     1  // Copyright 2019 Dolthub, Inc.
     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 earl
    16  
    17  import (
    18  	"net/url"
    19  	"regexp"
    20  	"strconv"
    21  	"strings"
    22  
    23  	"github.com/dolthub/dolt/go/libraries/utils/osutil"
    24  )
    25  
    26  var validHostRegex = regexp.MustCompile("^[-.a-zA-z0-9]*$")
    27  var validHostWithPortRegex = regexp.MustCompile("^[-.a-zA-z0-9]*:[0-9]*$")
    28  
    29  func isValidHost(hostAndPortStr string) bool {
    30  	hostStr := hostAndPortStr
    31  	portStr := ""
    32  
    33  	if idx := strings.IndexRune(hostAndPortStr, ':'); idx != -1 {
    34  		hostStr = hostAndPortStr[:idx]
    35  		portStr = strings.TrimSpace(hostAndPortStr[idx+1:])
    36  	}
    37  
    38  	if len(portStr) > 0 {
    39  		if _, err := strconv.ParseUint(portStr, 10, 16); err != nil {
    40  			return false
    41  		}
    42  	}
    43  
    44  	if hostStr == "" {
    45  		return false
    46  	} else if hostStr == "localhost" {
    47  		return true
    48  	} else if strings.Index(hostStr, ".") == -1 {
    49  		return false
    50  	}
    51  
    52  	return validHostRegex.MatchString(hostStr) || validHostWithPortRegex.MatchString(hostStr)
    53  }
    54  
    55  func Parse(urlStr string) (*url.URL, error) {
    56  	u, err := parse(urlStr)
    57  
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  
    62  	// url.parse doesn't handle file paths that begin with . correctly
    63  	if u.Scheme == "file" && strings.HasPrefix(u.Host, ".") {
    64  		u.Path = u.Host + u.Path
    65  		u.Host = ""
    66  	}
    67  
    68  	// if Path is e.g. "/C$/" for a network location, it should instead be "C:/"
    69  	if len(u.Path) >= 3 && u.Path[0] == '/' && u.Path[1] >= 'A' && u.Path[1] <= 'Z' && u.Path[2] == '$' {
    70  		u.Path = u.Path[1:2] + ":" + u.Path[3:]
    71  	} else if !osutil.StartsWithWindowsVolume(u.Path) { // normalize some
    72  		if len(u.Path) == 0 || (u.Path[0] != '/' && u.Path[0] != '.') {
    73  			u.Path = "/" + u.Path
    74  		}
    75  	}
    76  	u.Path = strings.ReplaceAll(u.Path, `\`, "/")
    77  
    78  	return u, nil
    79  }
    80  
    81  func parse(urlStr string) (*url.URL, error) {
    82  	if strIdx := strings.Index(urlStr, ":///"); strIdx != -1 && osutil.StartsWithWindowsVolume(urlStr[strIdx+4:]) {
    83  		return &url.URL{
    84  			Scheme: urlStr[:strIdx],
    85  			Path:   urlStr[strIdx+4:],
    86  		}, nil
    87  	}
    88  	if strings.Index(urlStr, "://") == -1 {
    89  		u, err := url.Parse("http://" + urlStr)
    90  
    91  		if err == nil && isValidHost(u.Host) {
    92  			u.Scheme = ""
    93  			return u, nil
    94  		} else if err != nil {
    95  			return nil, err
    96  		}
    97  	}
    98  
    99  	return url.Parse(urlStr)
   100  }
   101  
   102  // FileUrlFromPath returns a url for the given path with the "file" scheme i.e. file://...
   103  func FileUrlFromPath(path string, separator rune) string {
   104  	if osutil.StartsWithWindowsVolume(path) {
   105  		path = "/" + path
   106  	}
   107  
   108  	if separator != '/' {
   109  		path = strings.ReplaceAll(path, string(separator), "/")
   110  	}
   111  
   112  	u := &url.URL{Scheme: "file", Path: path}
   113  	urlStr := u.String()
   114  	return urlStr
   115  }