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  }