use ParseForm() to allow query param lookups once (#12900)

```
cpu: Intel(R) Core(TM) i5-7200U CPU @ 2.50GHz
BenchmarkURLQueryForm
BenchmarkURLQueryForm-4         247099363                4.809 ns/op           0 B/op          0 allocs/op
BenchmarkURLQuery
BenchmarkURLQuery-4              2517624               462.1 ns/op           432 B/op          4 allocs/op
PASS
ok      github.com/minio/minio/cmd      3.848s
```
This commit is contained in:
Harshavardhana 2021-08-07 22:43:01 -07:00 committed by GitHub
parent 7b0b0f9101
commit a2cd3c9a1d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 217 additions and 174 deletions

View File

@ -122,7 +122,7 @@ func (a adminAPIHandlers) SetRemoteTargetHandler(w http.ResponseWriter, r *http.
defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
vars := mux.Vars(r)
bucket := pathClean(vars["bucket"])
update := r.URL.Query().Get("update") == "true"
update := r.Form.Get("update") == "true"
if !globalIsErasure {
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
@ -169,7 +169,7 @@ func (a adminAPIHandlers) SetRemoteTargetHandler(w http.ResponseWriter, r *http.
target.SourceBucket = bucket
var ops []madmin.TargetUpdateType
if update {
ops = madmin.GetTargetUpdateOps(r.URL.Query())
ops = madmin.GetTargetUpdateOps(r.Form)
} else {
target.Arn = globalBucketTargetSys.getRemoteARN(bucket, &target)
}

View File

@ -337,7 +337,7 @@ func (a adminAPIHandlers) HelpConfigKVHandler(w http.ResponseWriter, r *http.Req
subSys := vars["subSys"]
key := vars["key"]
_, envOnly := r.URL.Query()["env"]
_, envOnly := r.Form["env"]
rd, err := GetHelp(subSys, key, envOnly)
if err != nil {

View File

@ -851,7 +851,7 @@ func (a adminAPIHandlers) ListServiceAccounts(w http.ResponseWriter, r *http.Req
var targetAccount string
user := r.URL.Query().Get("user")
user := r.Form.Get("user")
if user != "" {
if !globalIAMSys.IsAllowed(iampolicy.Args{
AccountName: cred.AccessKey,
@ -997,7 +997,7 @@ func (a adminAPIHandlers) AccountInfoHandler(w http.ResponseWriter, r *http.Requ
r.Header.Set("delimiter", SlashSeparator)
// Check if we are asked to return prefix usage
enablePrefixUsage := r.URL.Query().Get("prefix-usage") == "true"
enablePrefixUsage := r.Form.Get("prefix-usage") == "true"
isAllowedAccess := func(bucketName string) (rd, wr bool) {
if globalIAMSys.IsAllowed(iampolicy.Args{

View File

@ -450,7 +450,7 @@ func (a adminAPIHandlers) TopLocksHandler(w http.ResponseWriter, r *http.Request
}
count := 10 // by default list only top 10 entries
if countStr := r.URL.Query().Get("count"); countStr != "" {
if countStr := r.Form.Get("count"); countStr != "" {
var err error
count, err = strconv.Atoi(countStr)
if err != nil {
@ -458,7 +458,7 @@ func (a adminAPIHandlers) TopLocksHandler(w http.ResponseWriter, r *http.Request
return
}
}
stale := r.URL.Query().Get("stale") == "true" // list also stale locks
stale := r.Form.Get("stale") == "true" // list also stale locks
peerLocks := globalNotificationSys.GetLocks(ctx, r)
@ -713,7 +713,7 @@ func (a adminAPIHandlers) HealHandler(w http.ResponseWriter, r *http.Request) {
return
}
hip, errCode := extractHealInitParams(mux.Vars(r), r.URL.Query(), r.Body)
hip, errCode := extractHealInitParams(mux.Vars(r), r.Form, r.Body)
if errCode != ErrNone {
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(errCode), r.URL)
return
@ -926,9 +926,9 @@ func (a adminAPIHandlers) SpeedtestHandler(w http.ResponseWriter, r *http.Reques
return
}
sizeStr := r.URL.Query().Get(peerRESTSize)
durationStr := r.URL.Query().Get(peerRESTDuration)
concurrentStr := r.URL.Query().Get(peerRESTConcurrent)
sizeStr := r.Form.Get(peerRESTSize)
durationStr := r.Form.Get(peerRESTDuration)
concurrentStr := r.Form.Get(peerRESTConcurrent)
size, err := strconv.Atoi(sizeStr)
if err != nil {
@ -1157,7 +1157,7 @@ func mustTrace(entry interface{}, opts madmin.ServiceTraceOpts) (shouldTrace boo
}
func extractTraceOptions(r *http.Request) (opts madmin.ServiceTraceOpts, err error) {
q := r.URL.Query()
q := r.Form
opts.OnlyErrors = q.Get("err") == "true"
opts.S3 = q.Get("s3") == "true"
@ -1259,15 +1259,15 @@ func (a adminAPIHandlers) ConsoleLogHandler(w http.ResponseWriter, r *http.Reque
if objectAPI == nil {
return
}
node := r.URL.Query().Get("node")
node := r.Form.Get("node")
// limit buffered console entries if client requested it.
limitStr := r.URL.Query().Get("limit")
limitStr := r.Form.Get("limit")
limitLines, err := strconv.Atoi(limitStr)
if err != nil {
limitLines = 10
}
logKind := r.URL.Query().Get("logType")
logKind := r.Form.Get("logType")
if logKind == "" {
logKind = string(logger.All)
}
@ -1342,7 +1342,7 @@ func (a adminAPIHandlers) KMSCreateKeyHandler(w http.ResponseWriter, r *http.Req
return
}
if err := GlobalKMS.CreateKey(r.URL.Query().Get("key-id")); err != nil {
if err := GlobalKMS.CreateKey(r.Form.Get("key-id")); err != nil {
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
return
}
@ -1409,7 +1409,7 @@ func (a adminAPIHandlers) KMSKeyStatusHandler(w http.ResponseWriter, r *http.Req
return
}
keyID := r.URL.Query().Get("key-id")
keyID := r.Form.Get("key-id")
if keyID == "" {
keyID = stat.DefaultKey
}
@ -1576,7 +1576,7 @@ func (a adminAPIHandlers) HealthInfoHandler(w http.ResponseWriter, r *http.Reque
return
}
query := r.URL.Query()
query := r.Form
healthInfo := madmin.HealthInfo{Version: madmin.HealthInfoVersion}
healthInfoCh := make(chan madmin.HealthInfo)
@ -1600,7 +1600,7 @@ func (a adminAPIHandlers) HealthInfoHandler(w http.ResponseWriter, r *http.Reque
}
deadline := 1 * time.Hour
if dstr := r.URL.Query().Get("deadline"); dstr != "" {
if dstr := r.Form.Get("deadline"); dstr != "" {
var err error
deadline, err = time.ParseDuration(dstr)
if err != nil {
@ -1975,7 +1975,7 @@ func (a adminAPIHandlers) BandwidthMonitorHandler(w http.ResponseWriter, r *http
reportCh := make(chan madmin.BucketBandwidthReport)
keepAliveTicker := time.NewTicker(500 * time.Millisecond)
defer keepAliveTicker.Stop()
bucketsRequestedString := r.URL.Query().Get("buckets")
bucketsRequestedString := r.Form.Get("buckets")
bucketsRequested := strings.Split(bucketsRequestedString, ",")
go func() {
defer close(reportCh)
@ -2233,8 +2233,8 @@ func (a adminAPIHandlers) InspectDataHandler(w http.ResponseWriter, r *http.Requ
return
}
volume := r.URL.Query().Get("volume")
file := r.URL.Query().Get("file")
volume := r.Form.Get("volume")
file := r.Form.Get("file")
if len(volume) == 0 {
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInvalidBucketName), r.URL)
return

View File

@ -27,6 +27,7 @@ import (
"io"
"io/ioutil"
"net/http"
"net/url"
"strconv"
"strings"
"sync/atomic"
@ -61,13 +62,13 @@ func isRequestSignatureV2(r *http.Request) bool {
// Verify if request has AWS PreSign Version '4'.
func isRequestPresignedSignatureV4(r *http.Request) bool {
_, ok := r.URL.Query()[xhttp.AmzCredential]
_, ok := r.Form[xhttp.AmzCredential]
return ok
}
// Verify request has AWS PreSign Version '2'.
func isRequestPresignedSignatureV2(r *http.Request) bool {
_, ok := r.URL.Query()[xhttp.AmzAccessKeyID]
_, ok := r.Form[xhttp.AmzAccessKeyID]
return ok
}
@ -102,6 +103,14 @@ const (
// Get request authentication type.
func getRequestAuthType(r *http.Request) authType {
if r.URL != nil {
var err error
r.Form, err = url.ParseQuery(r.URL.RawQuery)
if err != nil {
logger.LogIf(r.Context(), err)
return authTypeUnknown
}
}
if isRequestSignatureV2(r) {
return authTypeSignedV2
} else if isRequestPresignedSignatureV2(r) {
@ -116,7 +125,7 @@ func getRequestAuthType(r *http.Request) authType {
return authTypeJWT
} else if isRequestPostPolicySignatureV4(r) {
return authTypePostPolicy
} else if _, ok := r.URL.Query()[xhttp.Action]; ok {
} else if _, ok := r.Form[xhttp.Action]; ok {
return authTypeSTS
} else if _, ok := r.Header[xhttp.Authorization]; !ok {
return authTypeAnonymous
@ -183,7 +192,7 @@ func getSessionToken(r *http.Request) (token string) {
if token != "" {
return token
}
return r.URL.Query().Get(xhttp.AmzSecurityToken)
return r.Form.Get(xhttp.AmzSecurityToken)
}
// Fetch claims in the security token returned by the client, doesn't return
@ -444,7 +453,7 @@ func isReqAuthenticated(ctx context.Context, r *http.Request, region string, sty
// Do not verify 'X-Amz-Content-Sha256' if skipSHA256.
var contentSHA256 []byte
if skipSHA256 := skipContentSha256Cksum(r); !skipSHA256 && isRequestPresignedSignatureV4(r) {
if sha256Sum, ok := r.URL.Query()[xhttp.AmzContentSha256]; ok && len(sha256Sum) > 0 {
if sha256Sum, ok := r.Form[xhttp.AmzContentSha256]; ok && len(sha256Sum) > 0 {
contentSHA256, err = hex.DecodeString(sha256Sum[0])
if err != nil {
return ErrContentSHA256Mismatch

View File

@ -38,6 +38,7 @@ func TestGetRequestAuthType(t *testing.T) {
req *http.Request
authT authType
}
nopCloser := ioutil.NopCloser(io.LimitReader(&nullReader{}, 1024))
testCases := []testCase{
// Test case - 1
// Check for generic signature v4 header.
@ -54,6 +55,7 @@ func TestGetRequestAuthType(t *testing.T) {
"Content-Encoding": []string{streamingContentEncoding},
},
Method: http.MethodPut,
Body: nopCloser,
},
authT: authTypeStreamingSigned,
},
@ -113,6 +115,7 @@ func TestGetRequestAuthType(t *testing.T) {
"Content-Type": []string{"multipart/form-data"},
},
Method: http.MethodPost,
Body: nopCloser,
},
authT: authTypePostPolicy,
},
@ -220,6 +223,7 @@ func TestIsRequestPresignedSignatureV2(t *testing.T) {
q := inputReq.URL.Query()
q.Add(testCase.inputQueryKey, testCase.inputQueryValue)
inputReq.URL.RawQuery = q.Encode()
inputReq.ParseForm()
actualResult := isRequestPresignedSignatureV2(inputReq)
if testCase.expectedResult != actualResult {
@ -254,6 +258,7 @@ func TestIsRequestPresignedSignatureV4(t *testing.T) {
q := inputReq.URL.Query()
q.Add(testCase.inputQueryKey, testCase.inputQueryValue)
inputReq.URL.RawQuery = q.Encode()
inputReq.ParseForm()
actualResult := isRequestPresignedSignatureV4(inputReq)
if testCase.expectedResult != actualResult {

View File

@ -246,7 +246,7 @@ func (api objectAPIHandlers) ListMultipartUploadsHandler(w http.ResponseWriter,
return
}
prefix, keyMarker, uploadIDMarker, delimiter, maxUploads, encodingType, errCode := getBucketMultipartResources(r.URL.Query())
prefix, keyMarker, uploadIDMarker, delimiter, maxUploads, encodingType, errCode := getBucketMultipartResources(r.Form)
if errCode != ErrNone {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(errCode), r.URL)
return
@ -1699,7 +1699,7 @@ func (api objectAPIHandlers) ResetBucketReplicationStateHandler(w http.ResponseW
vars := mux.Vars(r)
bucket := vars["bucket"]
durationStr := r.URL.Query().Get("older-than")
durationStr := r.Form.Get("older-than")
var (
days time.Duration
err error

View File

@ -458,7 +458,7 @@ func (r *RestoreObjectRequest) validate(ctx context.Context, objAPI ObjectLayer)
func postRestoreOpts(ctx context.Context, r *http.Request, bucket, object string) (opts ObjectOptions, err error) {
versioned := globalBucketVersioningSys.Enabled(bucket)
versionSuspended := globalBucketVersioningSys.Suspended(bucket)
vid := strings.TrimSpace(r.URL.Query().Get(xhttp.VersionID))
vid := strings.TrimSpace(r.Form.Get(xhttp.VersionID))
if vid != "" && vid != nullVersionID {
_, err := uuid.Parse(vid)
if err != nil {

View File

@ -92,7 +92,7 @@ func (api objectAPIHandlers) ListObjectVersionsHandler(w http.ResponseWriter, r
return
}
urlValues := r.URL.Query()
urlValues := r.Form
// Extract all the listBucketVersions query params to their native values.
prefix, marker, delimiter, maxkeys, encodingType, versionIDMarker, errCode := getListBucketObjectVersionsArgs(urlValues)
@ -153,7 +153,7 @@ func (api objectAPIHandlers) ListObjectsV2MHandler(w http.ResponseWriter, r *htt
return
}
urlValues := r.URL.Query()
urlValues := r.Form
// Extract all the listObjectsV2 query params to their native values.
prefix, token, startAfter, delimiter, fetchOwner, maxKeys, encodingType, errCode := getListObjectsV2Args(urlValues)
@ -220,7 +220,7 @@ func (api objectAPIHandlers) ListObjectsV2Handler(w http.ResponseWriter, r *http
return
}
urlValues := r.URL.Query()
urlValues := r.Form
// Extract all the listObjectsV2 query params to their native values.
prefix, token, startAfter, delimiter, fetchOwner, maxKeys, encodingType, errCode := getListObjectsV2Args(urlValues)
@ -329,7 +329,7 @@ func (api objectAPIHandlers) ListObjectsV1Handler(w http.ResponseWriter, r *http
}
// Extract all the litsObjectsV1 query params to their native values.
prefix, marker, delimiter, maxKeys, encodingType, s3Error := getListObjectsV1Args(r.URL.Query())
prefix, marker, delimiter, maxKeys, encodingType, s3Error := getListObjectsV1Args(r.Form)
if s3Error != ErrNone {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL)
return

View File

@ -77,10 +77,10 @@ func getConditionValues(r *http.Request, lc string, username string, claims map[
}
}
vid := r.URL.Query().Get("versionId")
vid := r.Form.Get(xhttp.VersionID)
if vid == "" {
if u, err := url.Parse(r.Header.Get(xhttp.AmzCopySource)); err == nil {
vid = u.Query().Get("versionId")
vid = u.Query().Get(xhttp.VersionID)
}
}
@ -143,8 +143,8 @@ func getConditionValues(r *http.Request, lc string, username string, claims map[
}
}
var cloneURLValues = url.Values{}
for k, v := range r.URL.Query() {
cloneURLValues := make(url.Values, len(r.Form))
for k, v := range r.Form {
cloneURLValues[k] = v
}

View File

@ -28,7 +28,7 @@ import (
// Test cross domain xml handler.
func TestCrossXMLHandler(t *testing.T) {
// Server initialization.
router := mux.NewRouter().SkipClean(true)
router := mux.NewRouter().SkipClean(true).UseEncodedPath()
handler := setCrossDomainPolicy(router)
srv := httptest.NewServer(handler)

View File

@ -406,7 +406,7 @@ func setRequestValidityHandler(h http.Handler) http.Handler {
return
}
// Check for bad components in URL query values.
for _, vv := range r.URL.Query() {
for _, vv := range r.Form {
for _, v := range vv {
if hasBadPathComponent(v) {
writeErrorResponse(r.Context(), w, errorCodes.ToAPIErr(ErrInvalidResourceName), r.URL)
@ -436,55 +436,6 @@ func setBucketForwardingHandler(h http.Handler) http.Handler {
return
}
// For browser requests, when federation is setup we need to
// specifically handle download and upload for browser requests.
if guessIsBrowserReq(r) {
var bucket, _ string
switch r.Method {
case http.MethodPut:
if getRequestAuthType(r) == authTypeJWT {
bucket, _ = path2BucketObjectWithBasePath(minioReservedBucketPath+"/upload", r.URL.Path)
}
case http.MethodGet:
if t := r.URL.Query().Get("token"); t != "" {
bucket, _ = path2BucketObjectWithBasePath(minioReservedBucketPath+"/download", r.URL.Path)
} else if getRequestAuthType(r) != authTypeJWT && !strings.HasPrefix(r.URL.Path, minioReservedBucketPath) {
bucket, _ = request2BucketObjectName(r)
}
}
if bucket == "" {
h.ServeHTTP(w, r)
return
}
sr, err := globalDNSConfig.Get(bucket)
if err != nil {
if err == dns.ErrNoEntriesFound {
writeErrorResponse(r.Context(), w, errorCodes.ToAPIErr(ErrNoSuchBucket),
r.URL)
} else {
writeErrorResponse(r.Context(), w, toAPIError(r.Context(), err),
r.URL)
}
return
}
if globalDomainIPs.Intersection(set.CreateStringSet(getHostsSlice(sr)...)).IsEmpty() {
r.URL.Scheme = "http"
if globalIsTLS {
r.URL.Scheme = "https"
}
r.URL.Host = getHostFromSrv(sr)
// Make sure we remove any existing headers before
// proxying the request to another node.
for k := range w.Header() {
w.Header().Del(k)
}
globalForwarder.ServeHTTP(w, r)
return
}
h.ServeHTTP(w, r)
return
}
bucket, object := request2BucketObjectName(r)
// Requests in federated setups for STS type calls which are

View File

@ -112,7 +112,7 @@ var userMetadataKeyPrefixes = []string{
// extractMetadata extracts metadata from HTTP header and HTTP queryString.
func extractMetadata(ctx context.Context, r *http.Request) (metadata map[string]string, err error) {
query := r.URL.Query()
query := r.Form
header := r.Header
metadata = make(map[string]string)
// Extract all query values.

View File

@ -42,7 +42,7 @@ func ClusterCheckHandler(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(ctx, globalAPIConfig.getClusterDeadline())
defer cancel()
opts := HealthOptions{Maintenance: r.URL.Query().Get("maintenance") == "true"}
opts := HealthOptions{Maintenance: r.Form.Get("maintenance") == "true"}
result := objLayer.Health(ctx, opts)
if result.WriteQuorum > 0 {
w.Header().Set(xhttp.MinIOWriteQuorum, strconv.Itoa(result.WriteQuorum))

View File

@ -65,7 +65,7 @@ func (api objectAPIHandlers) ListenNotificationHandler(w http.ResponseWriter, r
}
}
values := r.URL.Query()
values := r.Form
var prefix string
if len(values[peerRESTListenPrefix]) > 1 {

View File

@ -63,15 +63,16 @@ func (l *lockRESTServer) IsValid(w http.ResponseWriter, r *http.Request) bool {
}
func getLockArgs(r *http.Request) (args dsync.LockArgs, err error) {
quorum, err := strconv.Atoi(r.URL.Query().Get(lockRESTQuorum))
values := r.Form
quorum, err := strconv.Atoi(values.Get(lockRESTQuorum))
if err != nil {
return args, err
}
args = dsync.LockArgs{
Owner: r.URL.Query().Get(lockRESTOwner),
UID: r.URL.Query().Get(lockRESTUID),
Source: r.URL.Query().Get(lockRESTSource),
Owner: values.Get(lockRESTOwner),
UID: values.Get(lockRESTUID),
Source: values.Get(lockRESTSource),
Quorum: quorum,
}

View File

@ -332,8 +332,8 @@ func (s *storageRESTServer) WalkDirHandler(w http.ResponseWriter, r *http.Reques
}
}
prefix := r.URL.Query().Get(storageRESTPrefixFilter)
forward := r.URL.Query().Get(storageRESTForwardFilter)
prefix := r.Form.Get(storageRESTPrefixFilter)
forward := r.Form.Get(storageRESTForwardFilter)
writer := streamHTTPResponse(w)
writer.CloseWithError(s.storage.WalkDir(r.Context(), WalkDirOptions{
Bucket: volume,

View File

@ -83,7 +83,7 @@ func getOpts(ctx context.Context, r *http.Request, bucket, object string) (Objec
var partNumber int
var err error
if pn := r.URL.Query().Get(xhttp.PartNumber); pn != "" {
if pn := r.Form.Get(xhttp.PartNumber); pn != "" {
partNumber, err = strconv.Atoi(pn)
if err != nil {
return opts, err
@ -93,7 +93,7 @@ func getOpts(ctx context.Context, r *http.Request, bucket, object string) (Objec
}
}
vid := strings.TrimSpace(r.URL.Query().Get(xhttp.VersionID))
vid := strings.TrimSpace(r.Form.Get(xhttp.VersionID))
if vid != "" && vid != nullVersionID {
_, err := uuid.Parse(vid)
if err != nil {
@ -219,7 +219,7 @@ func delOpts(ctx context.Context, r *http.Request, bucket, object string) (opts
// get ObjectOptions for PUT calls from encryption headers and metadata
func putOpts(ctx context.Context, r *http.Request, bucket, object string, metadata map[string]string) (opts ObjectOptions, err error) {
versioned := globalBucketVersioningSys.Enabled(bucket)
vid := strings.TrimSpace(r.URL.Query().Get(xhttp.VersionID))
vid := strings.TrimSpace(r.Form.Get(xhttp.VersionID))
if vid != "" && vid != nullVersionID {
_, err := uuid.Parse(vid)
if err != nil {

View File

@ -491,7 +491,7 @@ func (api objectAPIHandlers) getObjectHandler(ctx context.Context, objectAPI Obj
setPartsCountHeaders(w, objInfo)
}
setHeadGetRespHeaders(w, r.URL.Query())
setHeadGetRespHeaders(w, r.Form)
statusCodeWritten := false
httpWriter := ioutil.WriteOnClose(w)
@ -739,7 +739,7 @@ func (api objectAPIHandlers) headObjectHandler(ctx context.Context, objectAPI Ob
}
// Set any additional requested response headers.
setHeadGetRespHeaders(w, r.URL.Query())
setHeadGetRespHeaders(w, r.Form)
// Successful response.
if rs != nil || opts.PartNumber > 0 {
@ -806,7 +806,7 @@ func getCpObjMetadataFromHeader(ctx context.Context, r *http.Request, userMeta m
// to the destination metadata.
sc := r.Header.Get(xhttp.AmzStorageClass)
if sc == "" {
sc = r.URL.Query().Get(xhttp.AmzStorageClass)
sc = r.Form.Get(xhttp.AmzStorageClass)
}
// if x-amz-metadata-directive says REPLACE then
@ -2264,8 +2264,8 @@ func (api objectAPIHandlers) CopyObjectPartHandler(w http.ResponseWriter, r *htt
return
}
uploadID := r.URL.Query().Get(xhttp.UploadID)
partIDString := r.URL.Query().Get(xhttp.PartNumber)
uploadID := r.Form.Get(xhttp.UploadID)
partIDString := r.Form.Get(xhttp.PartNumber)
partID, err := strconv.Atoi(partIDString)
if err != nil {
@ -2591,8 +2591,8 @@ func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http
return
}
uploadID := r.URL.Query().Get(xhttp.UploadID)
partIDString := r.URL.Query().Get(xhttp.PartNumber)
uploadID := r.Form.Get(xhttp.UploadID)
partIDString := r.Form.Get(xhttp.PartNumber)
partID, err := strconv.Atoi(partIDString)
if err != nil {
@ -2812,7 +2812,7 @@ func (api objectAPIHandlers) AbortMultipartUploadHandler(w http.ResponseWriter,
return
}
uploadID, _, _, _, s3Error := getObjectResources(r.URL.Query())
uploadID, _, _, _, s3Error := getObjectResources(r.Form)
if s3Error != ErrNone {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL)
return
@ -2851,7 +2851,7 @@ func (api objectAPIHandlers) ListObjectPartsHandler(w http.ResponseWriter, r *ht
return
}
uploadID, partNumberMarker, maxParts, encodingType, s3Error := getObjectResources(r.URL.Query())
uploadID, partNumberMarker, maxParts, encodingType, s3Error := getObjectResources(r.Form)
if s3Error != ErrNone {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL)
return
@ -2997,7 +2997,7 @@ func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWrite
}
// Get upload id.
uploadID, _, _, _, s3Error := getObjectResources(r.URL.Query())
uploadID, _, _, _, s3Error := getObjectResources(r.Form)
if s3Error != ErrNone {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL)
return

View File

@ -26,6 +26,7 @@ import (
"encoding/xml"
"fmt"
"io"
"path"
"runtime"
"strings"
@ -557,12 +558,8 @@ func testAPIGetObjectHandler(obj ObjectLayer, instanceType, bucketName string, a
t.Fatalf("Test %d: %s: Failed parsing response body: <ERROR> %v", i+1, instanceType, err)
}
if actualError.BucketName != testCase.bucketName {
t.Fatalf("Test %d: %s: Unexpected bucket name, expected %s, got %s", i+1, instanceType, testCase.bucketName, actualError.BucketName)
}
if actualError.Key != testCase.objectName {
t.Fatalf("Test %d: %s: Unexpected object name, expected %s, got %s", i+1, instanceType, testCase.objectName, actualError.Key)
if path.Clean(actualError.Resource) != pathJoin(SlashSeparator, testCase.bucketName, testCase.objectName) {
t.Fatalf("Test %d: %s: Unexpected resource, expected %s, got %s", i+1, instanceType, pathJoin(SlashSeparator, testCase.bucketName, testCase.objectName), actualError.Resource)
}
// Verify response of the V2 signed HTTP request.
@ -606,12 +603,8 @@ func testAPIGetObjectHandler(obj ObjectLayer, instanceType, bucketName string, a
t.Fatalf("Test %d: %s: Failed parsing response body: <ERROR> %v", i+1, instanceType, err)
}
if actualError.BucketName != testCase.bucketName {
t.Fatalf("Test %d: %s: Unexpected bucket name, expected %s, got %s", i+1, instanceType, testCase.bucketName, actualError.BucketName)
}
if actualError.Key != testCase.objectName {
t.Fatalf("Test %d: %s: Unexpected object name, expected %s, got %s", i+1, instanceType, testCase.objectName, actualError.Key)
if path.Clean(actualError.Resource) != pathJoin(SlashSeparator, testCase.bucketName, testCase.objectName) {
t.Fatalf("Test %d: %s: Unexpected resource, expected %s, got %s", i+1, instanceType, pathJoin(SlashSeparator, testCase.bucketName, testCase.objectName), actualError.Resource)
}
}
@ -918,7 +911,6 @@ func testAPIGetObjectWithPartNumberHandler(obj ObjectLayer, instanceType, bucket
mkGetReqWithPartNumber := func(oindex int, oi ObjectInput, partNumber int) {
object := oi.objectName
rec := httptest.NewRecorder()
queries := url.Values{}
queries.Add("partNumber", strconv.Itoa(partNumber))
@ -930,6 +922,7 @@ func testAPIGetObjectWithPartNumberHandler(obj ObjectLayer, instanceType, bucket
object, oindex, partNumber, err)
}
rec := httptest.NewRecorder()
apiRouter.ServeHTTP(rec, req)
// Check response code (we make only valid requests in this test)

View File

@ -136,7 +136,7 @@ func (s *peerRESTServer) LoadPolicyMappingHandler(w http.ResponseWriter, r *http
return
}
_, isGroup := r.URL.Query()[peerRESTIsGroup]
_, isGroup := r.Form[peerRESTIsGroup]
if err := globalIAMSys.LoadPolicyMapping(objAPI, userOrGroup, isGroup); err != nil {
s.writeErrorResponse(w, err)
return
@ -841,7 +841,7 @@ func (s *peerRESTServer) ListenHandler(w http.ResponseWriter, r *http.Request) {
return
}
values := r.URL.Query()
values := r.Form
var prefix string
if len(values[peerRESTListenPrefix]) > 1 {
@ -932,15 +932,13 @@ func (s *peerRESTServer) ListenHandler(w http.ResponseWriter, r *http.Request) {
}
func extractTraceOptsFromPeerRequest(r *http.Request) (opts madmin.ServiceTraceOpts, err error) {
opts.S3 = r.Form.Get(peerRESTTraceS3) == "true"
opts.OS = r.Form.Get(peerRESTTraceOS) == "true"
opts.Storage = r.Form.Get(peerRESTTraceStorage) == "true"
opts.Internal = r.Form.Get(peerRESTTraceInternal) == "true"
opts.OnlyErrors = r.Form.Get(peerRESTTraceErr) == "true"
q := r.URL.Query()
opts.OnlyErrors = q.Get(peerRESTTraceErr) == "true"
opts.Storage = q.Get(peerRESTTraceStorage) == "true"
opts.Internal = q.Get(peerRESTTraceInternal) == "true"
opts.S3 = q.Get(peerRESTTraceS3) == "true"
opts.OS = q.Get(peerRESTTraceOS) == "true"
if t := q.Get(peerRESTTraceThreshold); t != "" {
if t := r.Form.Get(peerRESTTraceThreshold); t != "" {
d, err := time.ParseDuration(t)
if err != nil {
return opts, err
@ -1078,7 +1076,8 @@ func (s *peerRESTServer) GetBandwidth(w http.ResponseWriter, r *http.Request) {
s.writeErrorResponse(w, errors.New("invalid request"))
return
}
bucketsString := r.URL.Query().Get("buckets")
bucketsString := r.Form.Get("buckets")
w.WriteHeader(http.StatusOK)
w.(http.Flusher).Flush()
@ -1259,9 +1258,9 @@ func (s *peerRESTServer) SpeedtestHandler(w http.ResponseWriter, r *http.Request
return
}
sizeStr := r.URL.Query().Get(peerRESTSize)
durationStr := r.URL.Query().Get(peerRESTDuration)
concurrentStr := r.URL.Query().Get(peerRESTConcurrent)
sizeStr := r.Form.Get(peerRESTSize)
durationStr := r.Form.Get(peerRESTDuration)
concurrentStr := r.Form.Get(peerRESTConcurrent)
size, err := strconv.Atoi(sizeStr)
if err != nil {

View File

@ -196,7 +196,7 @@ func (api objectAPIHandlers) getObjectInArchiveFileHandler(ctx context.Context,
return
}
setHeadGetRespHeaders(w, r.URL.Query())
setHeadGetRespHeaders(w, r.Form)
httpWriter := ioutil.WriteOnClose(w)
@ -467,7 +467,7 @@ func (api objectAPIHandlers) headObjectInArchiveFileHandler(ctx context.Context,
}
// Set any additional requested response headers.
setHeadGetRespHeaders(w, r.URL.Query())
setHeadGetRespHeaders(w, r.Form)
// Successful response.
if rs != nil {

View File

@ -183,7 +183,7 @@ func doesPresignV2SignatureMatch(r *http.Request) APIErrorCode {
}
func getReqAccessKeyV2(r *http.Request) (auth.Credentials, bool, APIErrorCode) {
if accessKey := r.URL.Query().Get(xhttp.AmzAccessKeyID); accessKey != "" {
if accessKey := r.Form.Get(xhttp.AmzAccessKeyID); accessKey != "" {
return checkKeyValid(accessKey)
}

View File

@ -50,7 +50,7 @@ func (c credentialHeader) getScope() string {
}
func getReqAccessKeyV4(r *http.Request, region string, stype serviceType) (auth.Credentials, bool, APIErrorCode) {
ch, s3Err := parseCredentialHeader("Credential="+r.URL.Query().Get(xhttp.AmzCredential), region, stype)
ch, s3Err := parseCredentialHeader("Credential="+r.Form.Get(xhttp.AmzCredential), region, stype)
if s3Err != ErrNone {
// Strip off the Algorithm prefix.
v4Auth := strings.TrimPrefix(r.Header.Get("Authorization"), signV4Algorithm)

View File

@ -46,7 +46,7 @@ func skipContentSha256Cksum(r *http.Request) bool {
)
if isRequestPresignedSignatureV4(r) {
v, ok = r.URL.Query()[xhttp.AmzContentSha256]
v, ok = r.Form[xhttp.AmzContentSha256]
if !ok {
v, ok = r.Header[xhttp.AmzContentSha256]
}
@ -82,7 +82,7 @@ func getContentSha256Cksum(r *http.Request, stype serviceType) string {
// X-Amz-Content-Sha256, if not set in presigned requests, checksum
// will default to 'UNSIGNED-PAYLOAD'.
defaultSha256Cksum = unsignedPayload
v, ok = r.URL.Query()[xhttp.AmzContentSha256]
v, ok = r.Form[xhttp.AmzContentSha256]
if !ok {
v, ok = r.Header[xhttp.AmzContentSha256]
}
@ -151,7 +151,7 @@ func sumHMAC(key []byte, data []byte) []byte {
// extractSignedHeaders extract signed headers from Authorization header
func extractSignedHeaders(signedHeaders []string, r *http.Request) (http.Header, APIErrorCode) {
reqHeaders := r.Header
reqQueries := r.URL.Query()
reqQueries := r.Form
// find whether "host" is part of list of signed headers.
// if not return ErrUnsignedHeaders. "host" is mandatory.
if !contains(signedHeaders, "host") {

View File

@ -89,6 +89,7 @@ func TestSkipContentSha256Cksum(t *testing.T) {
inputReq.Header.Set(testCase.inputHeaderKey, testCase.inputHeaderValue)
}
}
inputReq.ParseForm()
actualResult := skipContentSha256Cksum(inputReq)
if testCase.expectedResult != actualResult {
@ -163,6 +164,7 @@ func TestExtractSignedHeaders(t *testing.T) {
// set headers value through Get parameter
inputQuery.Add("x-amz-server-side-encryption", xhttp.AmzEncryptionAES)
r.URL.RawQuery = inputQuery.Encode()
r.ParseForm()
_, errCode = extractSignedHeaders(signedHeaders, r)
if errCode != ErrNone {
t.Fatalf("Expected the APIErrorCode to be %d, but got %d", ErrNone, errCode)
@ -267,6 +269,7 @@ func TestGetContentSha256Cksum(t *testing.T) {
if testCase.h != "" {
r.Header.Set("x-amz-content-sha256", testCase.h)
}
r.ParseForm()
got := getContentSha256Cksum(r, serviceS3)
if got != testCase.expected {
t.Errorf("Test %d: got:%s expected:%s", i+1, got, testCase.expected)

View File

@ -209,7 +209,7 @@ func doesPresignedSignatureMatch(hashedPayload string, r *http.Request, region s
req := *r
// Parse request query string.
pSignValues, err := parsePreSignV4(req.URL.Query(), region, stype)
pSignValues, err := parsePreSignV4(req.Form, region, stype)
if err != ErrNone {
return err
}
@ -241,12 +241,12 @@ func doesPresignedSignatureMatch(hashedPayload string, r *http.Request, region s
// Construct new query.
query := make(url.Values)
clntHashedPayload := req.URL.Query().Get(xhttp.AmzContentSha256)
clntHashedPayload := req.Form.Get(xhttp.AmzContentSha256)
if clntHashedPayload != "" {
query.Set(xhttp.AmzContentSha256, hashedPayload)
}
token := req.URL.Query().Get(xhttp.AmzSecurityToken)
token := req.Form.Get(xhttp.AmzSecurityToken)
if token != "" {
query.Set(xhttp.AmzSecurityToken, cred.SessionToken)
}
@ -271,7 +271,7 @@ func doesPresignedSignatureMatch(hashedPayload string, r *http.Request, region s
)
// Add missing query parameters if any provided in the request URL
for k, v := range req.URL.Query() {
for k, v := range req.Form {
if !defaultSigParams.Contains(k) {
query[k] = v
}
@ -281,19 +281,19 @@ func doesPresignedSignatureMatch(hashedPayload string, r *http.Request, region s
encodedQuery := query.Encode()
// Verify if date query is same.
if req.URL.Query().Get(xhttp.AmzDate) != query.Get(xhttp.AmzDate) {
if req.Form.Get(xhttp.AmzDate) != query.Get(xhttp.AmzDate) {
return ErrSignatureDoesNotMatch
}
// Verify if expires query is same.
if req.URL.Query().Get(xhttp.AmzExpires) != query.Get(xhttp.AmzExpires) {
if req.Form.Get(xhttp.AmzExpires) != query.Get(xhttp.AmzExpires) {
return ErrSignatureDoesNotMatch
}
// Verify if signed headers query is same.
if req.URL.Query().Get(xhttp.AmzSignedHeaders) != query.Get(xhttp.AmzSignedHeaders) {
if req.Form.Get(xhttp.AmzSignedHeaders) != query.Get(xhttp.AmzSignedHeaders) {
return ErrSignatureDoesNotMatch
}
// Verify if credential query is same.
if req.URL.Query().Get(xhttp.AmzCredential) != query.Get(xhttp.AmzCredential) {
if req.Form.Get(xhttp.AmzCredential) != query.Get(xhttp.AmzCredential) {
return ErrSignatureDoesNotMatch
}
// Verify if sha256 payload query is same.
@ -321,7 +321,7 @@ func doesPresignedSignatureMatch(hashedPayload string, r *http.Request, region s
newSignature := getSignature(presignedSigningKey, presignedStringToSign)
// Verify signature.
if !compareSignatureV4(req.URL.Query().Get(xhttp.AmzSignature), newSignature) {
if !compareSignatureV4(req.Form.Get(xhttp.AmzSignature), newSignature) {
return ErrSignatureDoesNotMatch
}
return ErrNone
@ -369,7 +369,7 @@ func doesSignatureMatch(hashedPayload string, r *http.Request, region string, st
}
// Query string.
queryStr := req.URL.Query().Encode()
queryStr := req.Form.Encode()
// Get canonical request.
canonicalRequest := getCanonicalRequest(extractedSignedHeaders, hashedPayload, queryStr, req.URL.Path, req.Method)

View File

@ -293,6 +293,9 @@ func TestDoesPresignedSignatureMatch(t *testing.T) {
req.Header.Set(key, value)
}
// parse form.
req.ParseForm()
// Check if it matches!
err := doesPresignedSignatureMatch(payloadSHA256, req, testCase.region, serviceS3)
if err != testCase.expected {

View File

@ -119,7 +119,7 @@ func (s *storageRESTServer) IsValid(w http.ResponseWriter, r *http.Request) bool
return false
}
diskID := r.URL.Query().Get(storageRESTDiskID)
diskID := r.Form.Get(storageRESTDiskID)
if diskID == "" {
// Request sent empty disk-id, we allow the request
// as the peer might be coming up and trying to read format.json
@ -282,7 +282,7 @@ func (s *storageRESTServer) DeleteVolHandler(w http.ResponseWriter, r *http.Requ
}
vars := mux.Vars(r)
volume := vars[storageRESTVolume]
forceDelete := r.URL.Query().Get(storageRESTForceDelete) == "true"
forceDelete := r.Form.Get(storageRESTForceDelete) == "true"
err := s.storage.DeleteVol(r.Context(), volume, forceDelete)
if err != nil {
s.writeErrorResponse(w, err)
@ -641,10 +641,8 @@ func (s *storageRESTServer) DeleteVersionsHandler(w http.ResponseWriter, r *http
return
}
vars := r.URL.Query()
volume := vars.Get(storageRESTVolume)
totalVersions, err := strconv.Atoi(vars.Get(storageRESTTotalVersions))
volume := r.Form.Get(storageRESTVolume)
totalVersions, err := strconv.Atoi(r.Form.Get(storageRESTTotalVersions))
if err != nil {
s.writeErrorResponse(w, err)
return

View File

@ -117,7 +117,7 @@ func calculateSeedSignature(r *http.Request) (cred auth.Credentials, signature s
}
// Query string.
queryStr := req.URL.Query().Encode()
queryStr := req.Form.Encode()
// Get canonical request.
canonicalRequest := getCanonicalRequest(extractedSignedHeaders, payload, queryStr, req.URL.Path, req.Method)

View File

@ -95,14 +95,14 @@ func registerSTSRouter(router *mux.Router) {
stsRouter.Methods(http.MethodPost).MatcherFunc(func(r *http.Request, rm *mux.RouteMatch) bool {
ctypeOk := wildcard.MatchSimple("application/x-www-form-urlencoded*", r.Header.Get(xhttp.ContentType))
authOk := wildcard.MatchSimple(signV4Algorithm+"*", r.Header.Get(xhttp.Authorization))
noQueries := len(r.URL.Query()) == 0
noQueries := len(r.URL.RawQuery) == 0
return ctypeOk && authOk && noQueries
}).HandlerFunc(httpTraceAll(sts.AssumeRole))
// Assume roles with JWT handler, handles both ClientGrants and WebIdentity.
stsRouter.Methods(http.MethodPost).MatcherFunc(func(r *http.Request, rm *mux.RouteMatch) bool {
ctypeOk := wildcard.MatchSimple("application/x-www-form-urlencoded*", r.Header.Get(xhttp.ContentType))
noQueries := len(r.URL.Query()) == 0
noQueries := len(r.URL.RawQuery) == 0
return ctypeOk && noQueries
}).HandlerFunc(httpTraceAll(sts.AssumeRoleWithSSO))
@ -155,6 +155,18 @@ func checkAssumeRoleAuth(ctx context.Context, r *http.Request) (user auth.Creden
return user, true, ErrSTSNone
}
func parseForm(r *http.Request) error {
if err := r.ParseForm(); err != nil {
return err
}
for k, v := range r.PostForm {
if _, ok := r.Form[k]; !ok {
r.Form[k] = v
}
}
return nil
}
// AssumeRole - implementation of AWS STS API AssumeRole to get temporary
// credentials for regular users on Minio.
// https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html
@ -167,7 +179,7 @@ func (sts *stsAPIHandlers) AssumeRole(w http.ResponseWriter, r *http.Request) {
return
}
if err := r.ParseForm(); err != nil {
if err := parseForm(r); err != nil {
writeSTSErrorResponse(ctx, w, true, ErrSTSInvalidParameterValue, err)
return
}
@ -275,7 +287,7 @@ func (sts *stsAPIHandlers) AssumeRoleWithSSO(w http.ResponseWriter, r *http.Requ
ctx := newContext(r, w, "AssumeRoleSSOCommon")
// Parse the incoming form data.
if err := r.ParseForm(); err != nil {
if err := parseForm(r); err != nil {
writeSTSErrorResponse(ctx, w, true, ErrSTSInvalidParameterValue, err)
return
}
@ -514,7 +526,7 @@ func (sts *stsAPIHandlers) AssumeRoleWithLDAPIdentity(w http.ResponseWriter, r *
defer logger.AuditLog(ctx, w, r, nil, stsLDAPPassword)
// Parse the incoming form data.
if err := r.ParseForm(); err != nil {
if err := parseForm(r); err != nil {
writeSTSErrorResponse(ctx, w, true, ErrSTSInvalidParameterValue, err)
return
}

View File

@ -45,6 +45,7 @@ import (
"net/http/httptest"
"net/url"
"os"
"path"
"reflect"
"sort"
"strconv"
@ -1648,12 +1649,8 @@ func ExecObjectLayerAPIAnonTest(t *testing.T, obj ObjectLayer, testName, bucketN
t.Fatal(failTestStr(unknownSignTestStr, "error response failed to parse error XML"))
}
if actualError.BucketName != bucketName {
t.Fatal(failTestStr(unknownSignTestStr, "error response bucket name differs from expected value"))
}
if actualError.Key != objectName {
t.Fatal(failTestStr(unknownSignTestStr, "error response object name differs from expected value"))
if path.Clean(actualError.Resource) != pathJoin(SlashSeparator, bucketName, SlashSeparator, objectName) {
t.Fatal(failTestStr(unknownSignTestStr, "error response resource differs from expected value"))
}
}
@ -2035,13 +2032,15 @@ func registerAPIFunctions(muxRouter *mux.Router, objLayer ObjectLayer, apiFuncti
func initTestAPIEndPoints(objLayer ObjectLayer, apiFunctions []string) http.Handler {
// initialize a new mux router.
// goriilla/mux is the library used to register all the routes and handle them.
muxRouter := mux.NewRouter().SkipClean(true)
muxRouter := mux.NewRouter().SkipClean(true).UseEncodedPath()
if len(apiFunctions) > 0 {
// Iterate the list of API functions requested for and register them in mux HTTP handler.
registerAPIFunctions(muxRouter, objLayer, apiFunctions...)
muxRouter.Use(globalHandlers...)
return muxRouter
}
registerAPIRouter(muxRouter)
muxRouter.Use(globalHandlers...)
return muxRouter
}

70
cmd/url_test.go Normal file
View File

@ -0,0 +1,70 @@
// Copyright (c) 2015-2021 MinIO, Inc.
//
// This file is part of MinIO Object Storage stack
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package cmd
import (
"net/http"
"testing"
)
func BenchmarkURLQueryForm(b *testing.B) {
req, err := http.NewRequest(http.MethodGet, "http://localhost:9000/bucket/name?uploadId=upload&partNumber=1", nil)
if err != nil {
b.Fatal(err)
}
// benchmark utility which helps obtain number of allocations and bytes allocated per ops.
b.ReportAllocs()
// the actual benchmark for PutObject starts here. Reset the benchmark timer.
b.ResetTimer()
if err := req.ParseForm(); err != nil {
b.Fatal(err)
}
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
req.Form.Get("uploadId")
}
})
// Benchmark ends here. Stop timer.
b.StopTimer()
}
// BenchmarkURLQuery - benchmark URL memory allocations
func BenchmarkURLQuery(b *testing.B) {
req, err := http.NewRequest(http.MethodGet, "http://localhost:9000/bucket/name?uploadId=upload&partNumber=1", nil)
if err != nil {
b.Fatal(err)
}
// benchmark utility which helps obtain number of allocations and bytes allocated per ops.
b.ReportAllocs()
// the actual benchmark for PutObject starts here. Reset the benchmark timer.
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
req.URL.Query().Get("uploadId")
}
})
// Benchmark ends here. Stop timer.
b.StopTimer()
}