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)) defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := pathClean(vars["bucket"]) bucket := pathClean(vars["bucket"])
update := r.URL.Query().Get("update") == "true" update := r.Form.Get("update") == "true"
if !globalIsErasure { if !globalIsErasure {
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL) writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
@ -169,7 +169,7 @@ func (a adminAPIHandlers) SetRemoteTargetHandler(w http.ResponseWriter, r *http.
target.SourceBucket = bucket target.SourceBucket = bucket
var ops []madmin.TargetUpdateType var ops []madmin.TargetUpdateType
if update { if update {
ops = madmin.GetTargetUpdateOps(r.URL.Query()) ops = madmin.GetTargetUpdateOps(r.Form)
} else { } else {
target.Arn = globalBucketTargetSys.getRemoteARN(bucket, &target) 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"] subSys := vars["subSys"]
key := vars["key"] key := vars["key"]
_, envOnly := r.URL.Query()["env"] _, envOnly := r.Form["env"]
rd, err := GetHelp(subSys, key, envOnly) rd, err := GetHelp(subSys, key, envOnly)
if err != nil { if err != nil {

View File

@ -851,7 +851,7 @@ func (a adminAPIHandlers) ListServiceAccounts(w http.ResponseWriter, r *http.Req
var targetAccount string var targetAccount string
user := r.URL.Query().Get("user") user := r.Form.Get("user")
if user != "" { if user != "" {
if !globalIAMSys.IsAllowed(iampolicy.Args{ if !globalIAMSys.IsAllowed(iampolicy.Args{
AccountName: cred.AccessKey, AccountName: cred.AccessKey,
@ -997,7 +997,7 @@ func (a adminAPIHandlers) AccountInfoHandler(w http.ResponseWriter, r *http.Requ
r.Header.Set("delimiter", SlashSeparator) r.Header.Set("delimiter", SlashSeparator)
// Check if we are asked to return prefix usage // 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) { isAllowedAccess := func(bucketName string) (rd, wr bool) {
if globalIAMSys.IsAllowed(iampolicy.Args{ 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 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 var err error
count, err = strconv.Atoi(countStr) count, err = strconv.Atoi(countStr)
if err != nil { if err != nil {
@ -458,7 +458,7 @@ func (a adminAPIHandlers) TopLocksHandler(w http.ResponseWriter, r *http.Request
return 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) peerLocks := globalNotificationSys.GetLocks(ctx, r)
@ -713,7 +713,7 @@ func (a adminAPIHandlers) HealHandler(w http.ResponseWriter, r *http.Request) {
return 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 { if errCode != ErrNone {
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(errCode), r.URL) writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(errCode), r.URL)
return return
@ -926,9 +926,9 @@ func (a adminAPIHandlers) SpeedtestHandler(w http.ResponseWriter, r *http.Reques
return return
} }
sizeStr := r.URL.Query().Get(peerRESTSize) sizeStr := r.Form.Get(peerRESTSize)
durationStr := r.URL.Query().Get(peerRESTDuration) durationStr := r.Form.Get(peerRESTDuration)
concurrentStr := r.URL.Query().Get(peerRESTConcurrent) concurrentStr := r.Form.Get(peerRESTConcurrent)
size, err := strconv.Atoi(sizeStr) size, err := strconv.Atoi(sizeStr)
if err != nil { 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) { func extractTraceOptions(r *http.Request) (opts madmin.ServiceTraceOpts, err error) {
q := r.URL.Query() q := r.Form
opts.OnlyErrors = q.Get("err") == "true" opts.OnlyErrors = q.Get("err") == "true"
opts.S3 = q.Get("s3") == "true" opts.S3 = q.Get("s3") == "true"
@ -1259,15 +1259,15 @@ func (a adminAPIHandlers) ConsoleLogHandler(w http.ResponseWriter, r *http.Reque
if objectAPI == nil { if objectAPI == nil {
return return
} }
node := r.URL.Query().Get("node") node := r.Form.Get("node")
// limit buffered console entries if client requested it. // limit buffered console entries if client requested it.
limitStr := r.URL.Query().Get("limit") limitStr := r.Form.Get("limit")
limitLines, err := strconv.Atoi(limitStr) limitLines, err := strconv.Atoi(limitStr)
if err != nil { if err != nil {
limitLines = 10 limitLines = 10
} }
logKind := r.URL.Query().Get("logType") logKind := r.Form.Get("logType")
if logKind == "" { if logKind == "" {
logKind = string(logger.All) logKind = string(logger.All)
} }
@ -1342,7 +1342,7 @@ func (a adminAPIHandlers) KMSCreateKeyHandler(w http.ResponseWriter, r *http.Req
return 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) writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
return return
} }
@ -1409,7 +1409,7 @@ func (a adminAPIHandlers) KMSKeyStatusHandler(w http.ResponseWriter, r *http.Req
return return
} }
keyID := r.URL.Query().Get("key-id") keyID := r.Form.Get("key-id")
if keyID == "" { if keyID == "" {
keyID = stat.DefaultKey keyID = stat.DefaultKey
} }
@ -1576,7 +1576,7 @@ func (a adminAPIHandlers) HealthInfoHandler(w http.ResponseWriter, r *http.Reque
return return
} }
query := r.URL.Query() query := r.Form
healthInfo := madmin.HealthInfo{Version: madmin.HealthInfoVersion} healthInfo := madmin.HealthInfo{Version: madmin.HealthInfoVersion}
healthInfoCh := make(chan madmin.HealthInfo) healthInfoCh := make(chan madmin.HealthInfo)
@ -1600,7 +1600,7 @@ func (a adminAPIHandlers) HealthInfoHandler(w http.ResponseWriter, r *http.Reque
} }
deadline := 1 * time.Hour deadline := 1 * time.Hour
if dstr := r.URL.Query().Get("deadline"); dstr != "" { if dstr := r.Form.Get("deadline"); dstr != "" {
var err error var err error
deadline, err = time.ParseDuration(dstr) deadline, err = time.ParseDuration(dstr)
if err != nil { if err != nil {
@ -1975,7 +1975,7 @@ func (a adminAPIHandlers) BandwidthMonitorHandler(w http.ResponseWriter, r *http
reportCh := make(chan madmin.BucketBandwidthReport) reportCh := make(chan madmin.BucketBandwidthReport)
keepAliveTicker := time.NewTicker(500 * time.Millisecond) keepAliveTicker := time.NewTicker(500 * time.Millisecond)
defer keepAliveTicker.Stop() defer keepAliveTicker.Stop()
bucketsRequestedString := r.URL.Query().Get("buckets") bucketsRequestedString := r.Form.Get("buckets")
bucketsRequested := strings.Split(bucketsRequestedString, ",") bucketsRequested := strings.Split(bucketsRequestedString, ",")
go func() { go func() {
defer close(reportCh) defer close(reportCh)
@ -2233,8 +2233,8 @@ func (a adminAPIHandlers) InspectDataHandler(w http.ResponseWriter, r *http.Requ
return return
} }
volume := r.URL.Query().Get("volume") volume := r.Form.Get("volume")
file := r.URL.Query().Get("file") file := r.Form.Get("file")
if len(volume) == 0 { if len(volume) == 0 {
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInvalidBucketName), r.URL) writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInvalidBucketName), r.URL)
return return

View File

@ -27,6 +27,7 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/url"
"strconv" "strconv"
"strings" "strings"
"sync/atomic" "sync/atomic"
@ -61,13 +62,13 @@ func isRequestSignatureV2(r *http.Request) bool {
// Verify if request has AWS PreSign Version '4'. // Verify if request has AWS PreSign Version '4'.
func isRequestPresignedSignatureV4(r *http.Request) bool { func isRequestPresignedSignatureV4(r *http.Request) bool {
_, ok := r.URL.Query()[xhttp.AmzCredential] _, ok := r.Form[xhttp.AmzCredential]
return ok return ok
} }
// Verify request has AWS PreSign Version '2'. // Verify request has AWS PreSign Version '2'.
func isRequestPresignedSignatureV2(r *http.Request) bool { func isRequestPresignedSignatureV2(r *http.Request) bool {
_, ok := r.URL.Query()[xhttp.AmzAccessKeyID] _, ok := r.Form[xhttp.AmzAccessKeyID]
return ok return ok
} }
@ -102,6 +103,14 @@ const (
// Get request authentication type. // Get request authentication type.
func getRequestAuthType(r *http.Request) authType { 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) { if isRequestSignatureV2(r) {
return authTypeSignedV2 return authTypeSignedV2
} else if isRequestPresignedSignatureV2(r) { } else if isRequestPresignedSignatureV2(r) {
@ -116,7 +125,7 @@ func getRequestAuthType(r *http.Request) authType {
return authTypeJWT return authTypeJWT
} else if isRequestPostPolicySignatureV4(r) { } else if isRequestPostPolicySignatureV4(r) {
return authTypePostPolicy return authTypePostPolicy
} else if _, ok := r.URL.Query()[xhttp.Action]; ok { } else if _, ok := r.Form[xhttp.Action]; ok {
return authTypeSTS return authTypeSTS
} else if _, ok := r.Header[xhttp.Authorization]; !ok { } else if _, ok := r.Header[xhttp.Authorization]; !ok {
return authTypeAnonymous return authTypeAnonymous
@ -183,7 +192,7 @@ func getSessionToken(r *http.Request) (token string) {
if token != "" { if token != "" {
return 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 // 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. // Do not verify 'X-Amz-Content-Sha256' if skipSHA256.
var contentSHA256 []byte var contentSHA256 []byte
if skipSHA256 := skipContentSha256Cksum(r); !skipSHA256 && isRequestPresignedSignatureV4(r) { 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]) contentSHA256, err = hex.DecodeString(sha256Sum[0])
if err != nil { if err != nil {
return ErrContentSHA256Mismatch return ErrContentSHA256Mismatch

View File

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

View File

@ -246,7 +246,7 @@ func (api objectAPIHandlers) ListMultipartUploadsHandler(w http.ResponseWriter,
return 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 { if errCode != ErrNone {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(errCode), r.URL) writeErrorResponse(ctx, w, errorCodes.ToAPIErr(errCode), r.URL)
return return
@ -1699,7 +1699,7 @@ func (api objectAPIHandlers) ResetBucketReplicationStateHandler(w http.ResponseW
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
durationStr := r.URL.Query().Get("older-than") durationStr := r.Form.Get("older-than")
var ( var (
days time.Duration days time.Duration
err error 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) { func postRestoreOpts(ctx context.Context, r *http.Request, bucket, object string) (opts ObjectOptions, err error) {
versioned := globalBucketVersioningSys.Enabled(bucket) versioned := globalBucketVersioningSys.Enabled(bucket)
versionSuspended := globalBucketVersioningSys.Suspended(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 { if vid != "" && vid != nullVersionID {
_, err := uuid.Parse(vid) _, err := uuid.Parse(vid)
if err != nil { if err != nil {

View File

@ -92,7 +92,7 @@ func (api objectAPIHandlers) ListObjectVersionsHandler(w http.ResponseWriter, r
return return
} }
urlValues := r.URL.Query() urlValues := r.Form
// Extract all the listBucketVersions query params to their native values. // Extract all the listBucketVersions query params to their native values.
prefix, marker, delimiter, maxkeys, encodingType, versionIDMarker, errCode := getListBucketObjectVersionsArgs(urlValues) prefix, marker, delimiter, maxkeys, encodingType, versionIDMarker, errCode := getListBucketObjectVersionsArgs(urlValues)
@ -153,7 +153,7 @@ func (api objectAPIHandlers) ListObjectsV2MHandler(w http.ResponseWriter, r *htt
return return
} }
urlValues := r.URL.Query() urlValues := r.Form
// Extract all the listObjectsV2 query params to their native values. // Extract all the listObjectsV2 query params to their native values.
prefix, token, startAfter, delimiter, fetchOwner, maxKeys, encodingType, errCode := getListObjectsV2Args(urlValues) prefix, token, startAfter, delimiter, fetchOwner, maxKeys, encodingType, errCode := getListObjectsV2Args(urlValues)
@ -220,7 +220,7 @@ func (api objectAPIHandlers) ListObjectsV2Handler(w http.ResponseWriter, r *http
return return
} }
urlValues := r.URL.Query() urlValues := r.Form
// Extract all the listObjectsV2 query params to their native values. // Extract all the listObjectsV2 query params to their native values.
prefix, token, startAfter, delimiter, fetchOwner, maxKeys, encodingType, errCode := getListObjectsV2Args(urlValues) 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. // 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 { if s3Error != ErrNone {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL) writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL)
return 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 vid == "" {
if u, err := url.Parse(r.Header.Get(xhttp.AmzCopySource)); err == nil { 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{} cloneURLValues := make(url.Values, len(r.Form))
for k, v := range r.URL.Query() { for k, v := range r.Form {
cloneURLValues[k] = v cloneURLValues[k] = v
} }

View File

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

View File

@ -406,7 +406,7 @@ func setRequestValidityHandler(h http.Handler) http.Handler {
return return
} }
// Check for bad components in URL query values. // Check for bad components in URL query values.
for _, vv := range r.URL.Query() { for _, vv := range r.Form {
for _, v := range vv { for _, v := range vv {
if hasBadPathComponent(v) { if hasBadPathComponent(v) {
writeErrorResponse(r.Context(), w, errorCodes.ToAPIErr(ErrInvalidResourceName), r.URL) writeErrorResponse(r.Context(), w, errorCodes.ToAPIErr(ErrInvalidResourceName), r.URL)
@ -436,55 +436,6 @@ func setBucketForwardingHandler(h http.Handler) http.Handler {
return 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) bucket, object := request2BucketObjectName(r)
// Requests in federated setups for STS type calls which are // 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. // extractMetadata extracts metadata from HTTP header and HTTP queryString.
func extractMetadata(ctx context.Context, r *http.Request) (metadata map[string]string, err error) { func extractMetadata(ctx context.Context, r *http.Request) (metadata map[string]string, err error) {
query := r.URL.Query() query := r.Form
header := r.Header header := r.Header
metadata = make(map[string]string) metadata = make(map[string]string)
// Extract all query values. // 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()) ctx, cancel := context.WithTimeout(ctx, globalAPIConfig.getClusterDeadline())
defer cancel() defer cancel()
opts := HealthOptions{Maintenance: r.URL.Query().Get("maintenance") == "true"} opts := HealthOptions{Maintenance: r.Form.Get("maintenance") == "true"}
result := objLayer.Health(ctx, opts) result := objLayer.Health(ctx, opts)
if result.WriteQuorum > 0 { if result.WriteQuorum > 0 {
w.Header().Set(xhttp.MinIOWriteQuorum, strconv.Itoa(result.WriteQuorum)) 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 var prefix string
if len(values[peerRESTListenPrefix]) > 1 { 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) { 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 { if err != nil {
return args, err return args, err
} }
args = dsync.LockArgs{ args = dsync.LockArgs{
Owner: r.URL.Query().Get(lockRESTOwner), Owner: values.Get(lockRESTOwner),
UID: r.URL.Query().Get(lockRESTUID), UID: values.Get(lockRESTUID),
Source: r.URL.Query().Get(lockRESTSource), Source: values.Get(lockRESTSource),
Quorum: quorum, Quorum: quorum,
} }

View File

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

View File

@ -83,7 +83,7 @@ func getOpts(ctx context.Context, r *http.Request, bucket, object string) (Objec
var partNumber int var partNumber int
var err error 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) partNumber, err = strconv.Atoi(pn)
if err != nil { if err != nil {
return opts, err 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 { if vid != "" && vid != nullVersionID {
_, err := uuid.Parse(vid) _, err := uuid.Parse(vid)
if err != nil { 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 // 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) { func putOpts(ctx context.Context, r *http.Request, bucket, object string, metadata map[string]string) (opts ObjectOptions, err error) {
versioned := globalBucketVersioningSys.Enabled(bucket) 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 { if vid != "" && vid != nullVersionID {
_, err := uuid.Parse(vid) _, err := uuid.Parse(vid)
if err != nil { if err != nil {

View File

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

View File

@ -26,6 +26,7 @@ import (
"encoding/xml" "encoding/xml"
"fmt" "fmt"
"io" "io"
"path"
"runtime" "runtime"
"strings" "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) t.Fatalf("Test %d: %s: Failed parsing response body: <ERROR> %v", i+1, instanceType, err)
} }
if actualError.BucketName != testCase.bucketName { if path.Clean(actualError.Resource) != pathJoin(SlashSeparator, testCase.bucketName, testCase.objectName) {
t.Fatalf("Test %d: %s: Unexpected bucket name, expected %s, got %s", i+1, instanceType, testCase.bucketName, actualError.BucketName) t.Fatalf("Test %d: %s: Unexpected resource, expected %s, got %s", i+1, instanceType, pathJoin(SlashSeparator, testCase.bucketName, testCase.objectName), actualError.Resource)
}
if actualError.Key != testCase.objectName {
t.Fatalf("Test %d: %s: Unexpected object name, expected %s, got %s", i+1, instanceType, testCase.objectName, actualError.Key)
} }
// Verify response of the V2 signed HTTP request. // 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) t.Fatalf("Test %d: %s: Failed parsing response body: <ERROR> %v", i+1, instanceType, err)
} }
if actualError.BucketName != testCase.bucketName { if path.Clean(actualError.Resource) != pathJoin(SlashSeparator, testCase.bucketName, testCase.objectName) {
t.Fatalf("Test %d: %s: Unexpected bucket name, expected %s, got %s", i+1, instanceType, testCase.bucketName, actualError.BucketName) t.Fatalf("Test %d: %s: Unexpected resource, expected %s, got %s", i+1, instanceType, pathJoin(SlashSeparator, testCase.bucketName, testCase.objectName), actualError.Resource)
}
if actualError.Key != testCase.objectName {
t.Fatalf("Test %d: %s: Unexpected object name, expected %s, got %s", i+1, instanceType, testCase.objectName, actualError.Key)
} }
} }
@ -918,7 +911,6 @@ func testAPIGetObjectWithPartNumberHandler(obj ObjectLayer, instanceType, bucket
mkGetReqWithPartNumber := func(oindex int, oi ObjectInput, partNumber int) { mkGetReqWithPartNumber := func(oindex int, oi ObjectInput, partNumber int) {
object := oi.objectName object := oi.objectName
rec := httptest.NewRecorder()
queries := url.Values{} queries := url.Values{}
queries.Add("partNumber", strconv.Itoa(partNumber)) queries.Add("partNumber", strconv.Itoa(partNumber))
@ -930,6 +922,7 @@ func testAPIGetObjectWithPartNumberHandler(obj ObjectLayer, instanceType, bucket
object, oindex, partNumber, err) object, oindex, partNumber, err)
} }
rec := httptest.NewRecorder()
apiRouter.ServeHTTP(rec, req) apiRouter.ServeHTTP(rec, req)
// Check response code (we make only valid requests in this test) // 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 return
} }
_, isGroup := r.URL.Query()[peerRESTIsGroup] _, isGroup := r.Form[peerRESTIsGroup]
if err := globalIAMSys.LoadPolicyMapping(objAPI, userOrGroup, isGroup); err != nil { if err := globalIAMSys.LoadPolicyMapping(objAPI, userOrGroup, isGroup); err != nil {
s.writeErrorResponse(w, err) s.writeErrorResponse(w, err)
return return
@ -841,7 +841,7 @@ func (s *peerRESTServer) ListenHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
values := r.URL.Query() values := r.Form
var prefix string var prefix string
if len(values[peerRESTListenPrefix]) > 1 { 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) { 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() if t := r.Form.Get(peerRESTTraceThreshold); t != "" {
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 != "" {
d, err := time.ParseDuration(t) d, err := time.ParseDuration(t)
if err != nil { if err != nil {
return opts, err return opts, err
@ -1078,7 +1076,8 @@ func (s *peerRESTServer) GetBandwidth(w http.ResponseWriter, r *http.Request) {
s.writeErrorResponse(w, errors.New("invalid request")) s.writeErrorResponse(w, errors.New("invalid request"))
return return
} }
bucketsString := r.URL.Query().Get("buckets")
bucketsString := r.Form.Get("buckets")
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
w.(http.Flusher).Flush() w.(http.Flusher).Flush()
@ -1259,9 +1258,9 @@ func (s *peerRESTServer) SpeedtestHandler(w http.ResponseWriter, r *http.Request
return return
} }
sizeStr := r.URL.Query().Get(peerRESTSize) sizeStr := r.Form.Get(peerRESTSize)
durationStr := r.URL.Query().Get(peerRESTDuration) durationStr := r.Form.Get(peerRESTDuration)
concurrentStr := r.URL.Query().Get(peerRESTConcurrent) concurrentStr := r.Form.Get(peerRESTConcurrent)
size, err := strconv.Atoi(sizeStr) size, err := strconv.Atoi(sizeStr)
if err != nil { if err != nil {

View File

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

View File

@ -183,7 +183,7 @@ func doesPresignV2SignatureMatch(r *http.Request) APIErrorCode {
} }
func getReqAccessKeyV2(r *http.Request) (auth.Credentials, bool, 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) 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) { 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 { if s3Err != ErrNone {
// Strip off the Algorithm prefix. // Strip off the Algorithm prefix.
v4Auth := strings.TrimPrefix(r.Header.Get("Authorization"), signV4Algorithm) v4Auth := strings.TrimPrefix(r.Header.Get("Authorization"), signV4Algorithm)

View File

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

View File

@ -89,6 +89,7 @@ func TestSkipContentSha256Cksum(t *testing.T) {
inputReq.Header.Set(testCase.inputHeaderKey, testCase.inputHeaderValue) inputReq.Header.Set(testCase.inputHeaderKey, testCase.inputHeaderValue)
} }
} }
inputReq.ParseForm()
actualResult := skipContentSha256Cksum(inputReq) actualResult := skipContentSha256Cksum(inputReq)
if testCase.expectedResult != actualResult { if testCase.expectedResult != actualResult {
@ -163,6 +164,7 @@ func TestExtractSignedHeaders(t *testing.T) {
// set headers value through Get parameter // set headers value through Get parameter
inputQuery.Add("x-amz-server-side-encryption", xhttp.AmzEncryptionAES) inputQuery.Add("x-amz-server-side-encryption", xhttp.AmzEncryptionAES)
r.URL.RawQuery = inputQuery.Encode() r.URL.RawQuery = inputQuery.Encode()
r.ParseForm()
_, errCode = extractSignedHeaders(signedHeaders, r) _, errCode = extractSignedHeaders(signedHeaders, r)
if errCode != ErrNone { if errCode != ErrNone {
t.Fatalf("Expected the APIErrorCode to be %d, but got %d", ErrNone, errCode) 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 != "" { if testCase.h != "" {
r.Header.Set("x-amz-content-sha256", testCase.h) r.Header.Set("x-amz-content-sha256", testCase.h)
} }
r.ParseForm()
got := getContentSha256Cksum(r, serviceS3) got := getContentSha256Cksum(r, serviceS3)
if got != testCase.expected { if got != testCase.expected {
t.Errorf("Test %d: got:%s expected:%s", i+1, 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 req := *r
// Parse request query string. // Parse request query string.
pSignValues, err := parsePreSignV4(req.URL.Query(), region, stype) pSignValues, err := parsePreSignV4(req.Form, region, stype)
if err != ErrNone { if err != ErrNone {
return err return err
} }
@ -241,12 +241,12 @@ func doesPresignedSignatureMatch(hashedPayload string, r *http.Request, region s
// Construct new query. // Construct new query.
query := make(url.Values) query := make(url.Values)
clntHashedPayload := req.URL.Query().Get(xhttp.AmzContentSha256) clntHashedPayload := req.Form.Get(xhttp.AmzContentSha256)
if clntHashedPayload != "" { if clntHashedPayload != "" {
query.Set(xhttp.AmzContentSha256, hashedPayload) query.Set(xhttp.AmzContentSha256, hashedPayload)
} }
token := req.URL.Query().Get(xhttp.AmzSecurityToken) token := req.Form.Get(xhttp.AmzSecurityToken)
if token != "" { if token != "" {
query.Set(xhttp.AmzSecurityToken, cred.SessionToken) 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 // 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) { if !defaultSigParams.Contains(k) {
query[k] = v query[k] = v
} }
@ -281,19 +281,19 @@ func doesPresignedSignatureMatch(hashedPayload string, r *http.Request, region s
encodedQuery := query.Encode() encodedQuery := query.Encode()
// Verify if date query is same. // 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 return ErrSignatureDoesNotMatch
} }
// Verify if expires query is same. // 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 return ErrSignatureDoesNotMatch
} }
// Verify if signed headers query is same. // 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 return ErrSignatureDoesNotMatch
} }
// Verify if credential query is same. // 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 return ErrSignatureDoesNotMatch
} }
// Verify if sha256 payload query is same. // Verify if sha256 payload query is same.
@ -321,7 +321,7 @@ func doesPresignedSignatureMatch(hashedPayload string, r *http.Request, region s
newSignature := getSignature(presignedSigningKey, presignedStringToSign) newSignature := getSignature(presignedSigningKey, presignedStringToSign)
// Verify signature. // Verify signature.
if !compareSignatureV4(req.URL.Query().Get(xhttp.AmzSignature), newSignature) { if !compareSignatureV4(req.Form.Get(xhttp.AmzSignature), newSignature) {
return ErrSignatureDoesNotMatch return ErrSignatureDoesNotMatch
} }
return ErrNone return ErrNone
@ -369,7 +369,7 @@ func doesSignatureMatch(hashedPayload string, r *http.Request, region string, st
} }
// Query string. // Query string.
queryStr := req.URL.Query().Encode() queryStr := req.Form.Encode()
// Get canonical request. // Get canonical request.
canonicalRequest := getCanonicalRequest(extractedSignedHeaders, hashedPayload, queryStr, req.URL.Path, req.Method) 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) req.Header.Set(key, value)
} }
// parse form.
req.ParseForm()
// Check if it matches! // Check if it matches!
err := doesPresignedSignatureMatch(payloadSHA256, req, testCase.region, serviceS3) err := doesPresignedSignatureMatch(payloadSHA256, req, testCase.region, serviceS3)
if err != testCase.expected { if err != testCase.expected {

View File

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

View File

@ -117,7 +117,7 @@ func calculateSeedSignature(r *http.Request) (cred auth.Credentials, signature s
} }
// Query string. // Query string.
queryStr := req.URL.Query().Encode() queryStr := req.Form.Encode()
// Get canonical request. // Get canonical request.
canonicalRequest := getCanonicalRequest(extractedSignedHeaders, payload, queryStr, req.URL.Path, req.Method) 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 { 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)) ctypeOk := wildcard.MatchSimple("application/x-www-form-urlencoded*", r.Header.Get(xhttp.ContentType))
authOk := wildcard.MatchSimple(signV4Algorithm+"*", r.Header.Get(xhttp.Authorization)) 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 return ctypeOk && authOk && noQueries
}).HandlerFunc(httpTraceAll(sts.AssumeRole)) }).HandlerFunc(httpTraceAll(sts.AssumeRole))
// Assume roles with JWT handler, handles both ClientGrants and WebIdentity. // Assume roles with JWT handler, handles both ClientGrants and WebIdentity.
stsRouter.Methods(http.MethodPost).MatcherFunc(func(r *http.Request, rm *mux.RouteMatch) bool { 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)) 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 return ctypeOk && noQueries
}).HandlerFunc(httpTraceAll(sts.AssumeRoleWithSSO)) }).HandlerFunc(httpTraceAll(sts.AssumeRoleWithSSO))
@ -155,6 +155,18 @@ func checkAssumeRoleAuth(ctx context.Context, r *http.Request) (user auth.Creden
return user, true, ErrSTSNone 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 // AssumeRole - implementation of AWS STS API AssumeRole to get temporary
// credentials for regular users on Minio. // credentials for regular users on Minio.
// https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html // 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 return
} }
if err := r.ParseForm(); err != nil { if err := parseForm(r); err != nil {
writeSTSErrorResponse(ctx, w, true, ErrSTSInvalidParameterValue, err) writeSTSErrorResponse(ctx, w, true, ErrSTSInvalidParameterValue, err)
return return
} }
@ -275,7 +287,7 @@ func (sts *stsAPIHandlers) AssumeRoleWithSSO(w http.ResponseWriter, r *http.Requ
ctx := newContext(r, w, "AssumeRoleSSOCommon") ctx := newContext(r, w, "AssumeRoleSSOCommon")
// Parse the incoming form data. // Parse the incoming form data.
if err := r.ParseForm(); err != nil { if err := parseForm(r); err != nil {
writeSTSErrorResponse(ctx, w, true, ErrSTSInvalidParameterValue, err) writeSTSErrorResponse(ctx, w, true, ErrSTSInvalidParameterValue, err)
return return
} }
@ -514,7 +526,7 @@ func (sts *stsAPIHandlers) AssumeRoleWithLDAPIdentity(w http.ResponseWriter, r *
defer logger.AuditLog(ctx, w, r, nil, stsLDAPPassword) defer logger.AuditLog(ctx, w, r, nil, stsLDAPPassword)
// Parse the incoming form data. // Parse the incoming form data.
if err := r.ParseForm(); err != nil { if err := parseForm(r); err != nil {
writeSTSErrorResponse(ctx, w, true, ErrSTSInvalidParameterValue, err) writeSTSErrorResponse(ctx, w, true, ErrSTSInvalidParameterValue, err)
return return
} }

View File

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