github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/cmn/uri.go (about)

     1  // Package cmn provides common constants, types, and utilities for AIS clients
     2  // and AIStore.
     3  /*
     4   * Copyright (c) 2018-2024, NVIDIA CORPORATION. All rights reserved.
     5   */
     6  package cmn
     7  
     8  import (
     9  	"encoding/base64"
    10  	"fmt"
    11  	"strconv"
    12  	"strings"
    13  
    14  	"github.com/NVIDIA/aistore/api/apc"
    15  	"github.com/NVIDIA/aistore/cmn/cos"
    16  	"github.com/OneOfOne/xxhash"
    17  )
    18  
    19  type ParseURIOpts struct {
    20  	DefaultProvider string // If set the provider will be used as provider.
    21  	IsQuery         bool   // Determines if the URI should be parsed as query.
    22  }
    23  
    24  //
    25  // Parse URI = [provider://][@uuid#namespace][/][bucketName[/objectName]]
    26  //
    27  
    28  // Splits url into [(scheme)://](address).
    29  // It's not possible to use url.Parse as (from url.Parse() docs)
    30  // 'Trying to parse a hostname and path without a scheme is invalid'
    31  func ParseURLScheme(url string) (scheme, address string) {
    32  	s := strings.SplitN(url, apc.BckProviderSeparator, 2)
    33  	if len(s) == 1 {
    34  		return "", s[0]
    35  	}
    36  	return s[0], s[1]
    37  }
    38  
    39  func OrigURLBck2Name(origURLBck string) (bckName string) {
    40  	_, b := ParseURLScheme(origURLBck)
    41  	b1 := xxhash.Checksum64S(cos.UnsafeB(b), cos.MLCG32)
    42  	b2 := strconv.FormatUint(b1, 16)
    43  	bckName = base64.RawURLEncoding.EncodeToString([]byte(b2))
    44  	return
    45  }
    46  
    47  func ParseBckObjectURI(uri string, opts ParseURIOpts) (bck Bck, objName string, err error) {
    48  	const fmtErrEmpty = "backend provider cannot be empty%s (did you mean \"ais://%s\"?)"
    49  	parts := strings.SplitN(uri, apc.BckProviderSeparator, 2)
    50  	if len(parts) > 1 && parts[0] != "" {
    51  		if bck.Provider, err = NormalizeProvider(parts[0]); err != nil {
    52  			return
    53  		}
    54  		uri = parts[1]
    55  	} else if !opts.IsQuery {
    56  		bck.Provider = opts.DefaultProvider
    57  	}
    58  
    59  	parts = strings.SplitN(uri, "/", 2)
    60  	if parts[0] != "" && (parts[0][0] == apc.NsUUIDPrefix || parts[0][0] == apc.NsNamePrefix) {
    61  		bck.Ns = ParseNsUname(parts[0])
    62  		if err := bck.Ns.validate(); err != nil {
    63  			return bck, "", err
    64  		}
    65  		if !opts.IsQuery && bck.Provider == "" {
    66  			return bck, "", fmt.Errorf(fmtErrEmpty, " when namespace is not", bck)
    67  		}
    68  		if len(parts) == 1 {
    69  			if parts[0] == string(apc.NsUUIDPrefix) && opts.IsQuery {
    70  				// Case: "[provider://]@" (only valid if uri is query)
    71  				// We need to list buckets from all possible remote clusters
    72  				bck.Ns = NsAnyRemote
    73  				return bck, "", nil
    74  			}
    75  
    76  			// Case: "[provider://]@uuid#ns"
    77  			return bck, "", nil
    78  		}
    79  
    80  		// Case: "[provider://]@uuid#ns/bucket"
    81  		parts = strings.SplitN(parts[1], "/", 2)
    82  	}
    83  
    84  	bck.Name = parts[0]
    85  	if bck.Name != "" {
    86  		if err := bck.ValidateName(); err != nil {
    87  			return bck, "", err
    88  		}
    89  		if bck.Provider == "" {
    90  			return bck, "", fmt.Errorf(fmtErrEmpty, "", bck)
    91  		}
    92  	}
    93  	if len(parts) > 1 {
    94  		objName = parts[1]
    95  	}
    96  	return
    97  }