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 }