github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/ais/dpq.go (about) 1 // Package ais provides core functionality for the AIStore object storage. 2 /* 3 * Copyright (c) 2018-2024, NVIDIA CORPORATION. All rights reserved. 4 */ 5 package ais 6 7 import ( 8 "fmt" 9 "net/url" 10 "strings" 11 "sync" 12 13 "github.com/NVIDIA/aistore/ais/s3" 14 "github.com/NVIDIA/aistore/api/apc" 15 "github.com/NVIDIA/aistore/cmn/archive" 16 "github.com/NVIDIA/aistore/cmn/cos" 17 "github.com/NVIDIA/aistore/cmn/debug" 18 ) 19 20 // RESTful API: datapath query parameters 21 type dpq struct { 22 bck struct { 23 provider, namespace string // bucket 24 } 25 apnd struct { 26 ty, hdl string // QparamAppendType, QparamAppendHandle 27 } 28 arch struct { 29 path, mime, regx, mmode string // QparamArchpath et al. (plus archmode below) 30 } 31 32 ptime string // req timestamp at calling/redirecting proxy (QparamUnixTime) 33 uuid string // xaction 34 origURL string // ht://url-> 35 owt string // object write transaction { OwtPut, ... } 36 fltPresence string // QparamFltPresence 37 etlName string // QparamETLName 38 binfo string // bucket info, with or without requirement to summarize remote obj-s 39 40 skipVC bool // QparamSkipVC (skip loading existing object's metadata) 41 isGFN bool // QparamIsGFNRequest 42 dontAddRemote bool // QparamDontAddRemote 43 silent bool // QparamSilent 44 latestVer bool // QparamLatestVer 45 isS3 bool // special use: frontend S3 API 46 } 47 48 var ( 49 dpqPool sync.Pool 50 dpq0 dpq 51 ) 52 53 func dpqAlloc() *dpq { 54 if v := dpqPool.Get(); v != nil { 55 return v.(*dpq) 56 } 57 return &dpq{} 58 } 59 60 func dpqFree(dpq *dpq) { 61 *dpq = dpq0 62 dpqPool.Put(dpq) 63 } 64 65 // Data Path Query structure (dpq): 66 // Parse URL query for a selected few parameters used in the datapath. 67 // (This is a faster alternative to the conventional and RFC-compliant URL.Query() 68 // to be used narrowly to handle those few (keys) and nothing else.) 69 func (dpq *dpq) parse(rawQuery string) (err error) { 70 query := rawQuery 71 for query != "" { 72 key, value := query, "" 73 if i := strings.IndexByte(key, '&'); i >= 0 { 74 key, query = key[:i], key[i+1:] 75 } else { 76 query = "" 77 } 78 if k, v, ok := _dpqKeqV(key); ok { 79 key, value = k, v 80 } 81 // supported URL query parameters explicitly named below; attempt to parse anything 82 // outside this list will fail 83 switch key { 84 case apc.QparamProvider: 85 dpq.bck.provider = value 86 case apc.QparamNamespace: 87 if dpq.bck.namespace, err = url.QueryUnescape(value); err != nil { 88 return 89 } 90 case apc.QparamSkipVC: 91 dpq.skipVC = cos.IsParseBool(value) 92 case apc.QparamUnixTime: 93 dpq.ptime = value 94 case apc.QparamUUID: 95 dpq.uuid = value 96 case apc.QparamArchpath, apc.QparamArchmime, apc.QparamArchregx, apc.QparamArchmode: 97 if err = dpq._arch(key, value); err != nil { 98 return 99 } 100 case apc.QparamIsGFNRequest: 101 dpq.isGFN = cos.IsParseBool(value) 102 case apc.QparamOrigURL: 103 if dpq.origURL, err = url.QueryUnescape(value); err != nil { 104 return 105 } 106 case apc.QparamAppendType: 107 dpq.apnd.ty = value 108 case apc.QparamAppendHandle: 109 if dpq.apnd.hdl, err = url.QueryUnescape(value); err != nil { 110 return 111 } 112 case apc.QparamOWT: 113 dpq.owt = value 114 115 case apc.QparamFltPresence: 116 dpq.fltPresence = value 117 case apc.QparamDontAddRemote: 118 dpq.dontAddRemote = cos.IsParseBool(value) 119 case apc.QparamBinfoWithOrWithoutRemote: 120 dpq.binfo = value 121 122 case apc.QparamETLName: 123 dpq.etlName = value 124 case apc.QparamSilent: 125 dpq.silent = cos.IsParseBool(value) 126 case apc.QparamLatestVer: 127 dpq.latestVer = cos.IsParseBool(value) 128 129 default: 130 debug.Func(func() { 131 switch key { 132 // not used yet 133 case apc.QparamProxyID, apc.QparamDontHeadRemote: 134 135 // flows that utilize these particular keys perform conventional 136 // `r.URL.Query()` parsing 137 case s3.QparamMptUploadID, s3.QparamMptUploads, s3.QparamMptPartNo, 138 s3.QparamAccessKeyID, s3.QparamExpires, s3.QparamSignature, 139 s3.HeaderAlgorithm, s3.HeaderCredentials, s3.HeaderDate, 140 s3.HeaderExpires, s3.HeaderSignedHeaders, s3.HeaderSignature, s3.QparamXID: 141 142 default: 143 err = fmt.Errorf("failed to fast-parse [%s], unknown key: %q", rawQuery, key) 144 debug.AssertNoErr(err) 145 } 146 }) 147 } 148 } 149 return 150 } 151 152 func _dpqKeqV(s string) (string, string, bool) { 153 if i := strings.IndexByte(s, '='); i > 0 { 154 return s[:i], s[i+1:], true 155 } 156 return s, "", false 157 } 158 159 // 160 // archive query 161 // 162 163 // parse & validate 164 func (dpq *dpq) _arch(key, val string) (err error) { 165 switch key { 166 case apc.QparamArchpath: 167 dpq.arch.path, err = url.QueryUnescape(val) 168 case apc.QparamArchmime: 169 dpq.arch.mime, err = url.QueryUnescape(val) 170 case apc.QparamArchregx: 171 dpq.arch.regx, err = url.QueryUnescape(val) 172 case apc.QparamArchmode: 173 dpq.arch.mmode, err = archive.ValidateMatchMode(val) 174 } 175 if err != nil { 176 return err 177 } 178 // either/or 179 if dpq.arch.path != "" && dpq.arch.mmode != "" { // (empty arch.regx is fine - is EmptyMatchAny) 180 err = fmt.Errorf("query parameters archpath=%q (match one) and archregx=%q (match many) are mutually exclusive", 181 apc.QparamArchpath, apc.QparamArchregx) 182 } 183 return err 184 } 185 186 func (dpq *dpq) isArch() bool { return dpq.arch.path != "" || dpq.arch.mmode != "" } 187 188 // err & log 189 func (dpq *dpq) _archstr() string { 190 if dpq.arch.path != "" { 191 return fmt.Sprintf("(%s=%s)", apc.QparamArchpath, dpq.arch.path) 192 } 193 return fmt.Sprintf("(%s=%s, %s=%s)", apc.QparamArchregx, dpq.arch.regx, apc.QparamArchmode, dpq.arch.mmode) 194 }