diff --git a/cmd/admin-handlers.go b/cmd/admin-handlers.go index ab3e116e4..ecbb7ca33 100644 --- a/cmd/admin-handlers.go +++ b/cmd/admin-handlers.go @@ -34,6 +34,7 @@ import ( "github.com/tidwall/gjson" "github.com/tidwall/sjson" + xhttp "github.com/minio/minio/cmd/http" "github.com/minio/minio/cmd/logger" "github.com/minio/minio/pkg/cpu" "github.com/minio/minio/pkg/disk" @@ -682,7 +683,7 @@ func (a adminAPIHandlers) HealHandler(w http.ResponseWriter, r *http.Request) { // Start writing response to client started = true setCommonHeaders(w) - w.Header().Set("Content-Type", string(mimeJSON)) + w.Header().Set(xhttp.ContentType, string(mimeJSON)) // Set 200 OK status w.WriteHeader(200) } @@ -702,20 +703,20 @@ func (a adminAPIHandlers) HealHandler(w http.ResponseWriter, r *http.Request) { var errorRespJSON []byte if hr.errBody == "" { errorRespJSON = encodeResponseJSON(getAPIErrorResponse(ctx, hr.apiErr, - r.URL.Path, w.Header().Get(responseRequestIDKey), + r.URL.Path, w.Header().Get(xhttp.AmzRequestID), globalDeploymentID)) } else { errorRespJSON = encodeResponseJSON(APIErrorResponse{ Code: hr.apiErr.Code, Message: hr.errBody, Resource: r.URL.Path, - RequestID: w.Header().Get(responseRequestIDKey), + RequestID: w.Header().Get(xhttp.AmzRequestID), HostID: globalDeploymentID, }) } if !started { setCommonHeaders(w) - w.Header().Set("Content-Type", string(mimeJSON)) + w.Header().Set(xhttp.ContentType, string(mimeJSON)) w.WriteHeader(hr.apiErr.HTTPStatusCode) } w.Write(errorRespJSON) @@ -1487,7 +1488,7 @@ func (a adminAPIHandlers) TraceHandler(w http.ResponseWriter, r *http.Request) { // Avoid reusing tcp connection if read timeout is hit // This is needed to make r.Context().Done() work as // expected in case of read timeout - w.Header().Add("Connection", "close") + w.Header().Add(xhttp.Connection, "close") doneCh := make(chan struct{}) defer close(doneCh) diff --git a/cmd/api-datatypes.go b/cmd/api-datatypes.go index 0ae385838..d24d8c70b 100644 --- a/cmd/api-datatypes.go +++ b/cmd/api-datatypes.go @@ -20,11 +20,6 @@ import ( "encoding/xml" ) -const ( - // Response request id. - responseRequestIDKey = "x-amz-request-id" -) - // ObjectIdentifier carries key name for the object to delete. type ObjectIdentifier struct { ObjectName string `xml:"Key"` diff --git a/cmd/api-headers.go b/cmd/api-headers.go index 733c9a1f6..d472e174f 100644 --- a/cmd/api-headers.go +++ b/cmd/api-headers.go @@ -26,6 +26,7 @@ import ( "time" "github.com/minio/minio/cmd/crypto" + xhttp "github.com/minio/minio/cmd/http" ) // Returns a hexadecimal representation of time at the @@ -36,13 +37,13 @@ func mustGetRequestID(t time.Time) string { // Write http common headers func setCommonHeaders(w http.ResponseWriter) { - w.Header().Set("Server", "MinIO/"+ReleaseTag) + w.Header().Set(xhttp.ServerInfo, "MinIO/"+ReleaseTag) // Set `x-amz-bucket-region` only if region is set on the server // by default minio uses an empty region. if region := globalServerConfig.GetRegion(); region != "" { - w.Header().Set("X-Amz-Bucket-Region", region) + w.Header().Set(xhttp.AmzBucketRegion, region) } - w.Header().Set("Accept-Ranges", "bytes") + w.Header().Set(xhttp.AcceptRanges, "bytes") // Remove sensitive information crypto.RemoveSensitiveHeaders(w.Header()) @@ -72,23 +73,23 @@ func setObjectHeaders(w http.ResponseWriter, objInfo ObjectInfo, rs *HTTPRangeSp // Set last modified time. lastModified := objInfo.ModTime.UTC().Format(http.TimeFormat) - w.Header().Set("Last-Modified", lastModified) + w.Header().Set(xhttp.LastModified, lastModified) // Set Etag if available. if objInfo.ETag != "" { - w.Header()["ETag"] = []string{"\"" + objInfo.ETag + "\""} + w.Header()[xhttp.ETag] = []string{"\"" + objInfo.ETag + "\""} } if objInfo.ContentType != "" { - w.Header().Set("Content-Type", objInfo.ContentType) + w.Header().Set(xhttp.ContentType, objInfo.ContentType) } if objInfo.ContentEncoding != "" { - w.Header().Set("Content-Encoding", objInfo.ContentEncoding) + w.Header().Set(xhttp.ContentEncoding, objInfo.ContentEncoding) } if !objInfo.Expires.IsZero() { - w.Header().Set("Expires", objInfo.Expires.UTC().Format(http.TimeFormat)) + w.Header().Set(xhttp.Expires, objInfo.Expires.UTC().Format(http.TimeFormat)) } // Set all other user defined metadata. @@ -124,10 +125,10 @@ func setObjectHeaders(w http.ResponseWriter, objInfo ObjectInfo, rs *HTTPRangeSp } // Set content length. - w.Header().Set("Content-Length", strconv.FormatInt(rangeLen, 10)) + w.Header().Set(xhttp.ContentLength, strconv.FormatInt(rangeLen, 10)) if rs != nil { contentRange := fmt.Sprintf("bytes %d-%d/%d", start, start+rangeLen-1, totalObjectSize) - w.Header().Set("Content-Range", contentRange) + w.Header().Set(xhttp.ContentRange, contentRange) } return nil diff --git a/cmd/api-response.go b/cmd/api-response.go index 2e1383b20..4e1c775cb 100644 --- a/cmd/api-response.go +++ b/cmd/api-response.go @@ -26,6 +26,7 @@ import ( "strings" "time" + xhttp "github.com/minio/minio/cmd/http" "github.com/minio/minio/cmd/logger" "github.com/minio/minio/pkg/handlers" ) @@ -522,9 +523,9 @@ func generateMultiDeleteResponse(quiet bool, deletedObjects []ObjectIdentifier, func writeResponse(w http.ResponseWriter, statusCode int, response []byte, mType mimeType) { setCommonHeaders(w) if mType != mimeNone { - w.Header().Set("Content-Type", string(mType)) + w.Header().Set(xhttp.ContentType, string(mType)) } - w.Header().Set("Content-Length", strconv.Itoa(len(response))) + w.Header().Set(xhttp.ContentLength, strconv.Itoa(len(response))) w.WriteHeader(statusCode) if response != nil { w.Write(response) @@ -563,7 +564,7 @@ func writeSuccessNoContent(w http.ResponseWriter) { // writeRedirectSeeOther writes Location header with http status 303 func writeRedirectSeeOther(w http.ResponseWriter, location string) { - w.Header().Set("Location", location) + w.Header().Set(xhttp.Location, location) writeResponse(w, http.StatusSeeOther, nil, mimeNone) } @@ -577,12 +578,12 @@ func writeErrorResponse(ctx context.Context, w http.ResponseWriter, err APIError case "SlowDown", "XMinioServerNotInitialized", "XMinioReadQuorum", "XMinioWriteQuorum": // Set retry-after header to indicate user-agents to retry request after 120secs. // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After - w.Header().Set("Retry-After", "120") + w.Header().Set(xhttp.RetryAfter, "120") case "AccessDenied": // The request is from browser and also if browser // is enabled we need to redirect. if browser { - w.Header().Set("Location", minioReservedBucketPath+reqURL.Path) + w.Header().Set(xhttp.Location, minioReservedBucketPath+reqURL.Path) w.WriteHeader(http.StatusTemporaryRedirect) return } @@ -590,7 +591,7 @@ func writeErrorResponse(ctx context.Context, w http.ResponseWriter, err APIError // Generate error response. errorResponse := getAPIErrorResponse(ctx, err, reqURL.Path, - w.Header().Get(responseRequestIDKey), globalDeploymentID) + w.Header().Get(xhttp.AmzRequestID), globalDeploymentID) encodedErrorResponse := encodeResponse(errorResponse) writeResponse(w, err.HTTPStatusCode, encodedErrorResponse, mimeXML) } @@ -603,7 +604,7 @@ func writeErrorResponseHeadersOnly(w http.ResponseWriter, err APIError) { // useful for admin APIs. func writeErrorResponseJSON(ctx context.Context, w http.ResponseWriter, err APIError, reqURL *url.URL) { // Generate error response. - errorResponse := getAPIErrorResponse(ctx, err, reqURL.Path, w.Header().Get(responseRequestIDKey), globalDeploymentID) + errorResponse := getAPIErrorResponse(ctx, err, reqURL.Path, w.Header().Get(xhttp.AmzRequestID), globalDeploymentID) encodedErrorResponse := encodeResponseJSON(errorResponse) writeResponse(w, err.HTTPStatusCode, encodedErrorResponse, mimeJSON) } @@ -621,7 +622,7 @@ func writeCustomErrorResponseJSON(ctx context.Context, w http.ResponseWriter, er Resource: reqURL.Path, BucketName: reqInfo.BucketName, Key: reqInfo.ObjectName, - RequestID: w.Header().Get(responseRequestIDKey), + RequestID: w.Header().Get(xhttp.AmzRequestID), HostID: globalDeploymentID, } encodedErrorResponse := encodeResponseJSON(errorResponse) @@ -637,12 +638,12 @@ func writeCustomErrorResponseXML(ctx context.Context, w http.ResponseWriter, err case "SlowDown", "XMinioServerNotInitialized", "XMinioReadQuorum", "XMinioWriteQuorum": // Set retry-after header to indicate user-agents to retry request after 120secs. // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After - w.Header().Set("Retry-After", "120") + w.Header().Set(xhttp.RetryAfter, "120") case "AccessDenied": // The request is from browser and also if browser // is enabled we need to redirect. if browser && globalIsBrowserEnabled { - w.Header().Set("Location", minioReservedBucketPath+reqURL.Path) + w.Header().Set(xhttp.Location, minioReservedBucketPath+reqURL.Path) w.WriteHeader(http.StatusTemporaryRedirect) return } @@ -655,7 +656,7 @@ func writeCustomErrorResponseXML(ctx context.Context, w http.ResponseWriter, err Resource: reqURL.Path, BucketName: reqInfo.BucketName, Key: reqInfo.ObjectName, - RequestID: w.Header().Get(responseRequestIDKey), + RequestID: w.Header().Get(xhttp.AmzRequestID), HostID: globalDeploymentID, } diff --git a/cmd/api-router.go b/cmd/api-router.go index ab3dab3d5..9b1439b69 100644 --- a/cmd/api-router.go +++ b/cmd/api-router.go @@ -20,6 +20,7 @@ import ( "net/http" "github.com/gorilla/mux" + xhttp "github.com/minio/minio/cmd/http" ) // objectAPIHandler implements and provides http handlers for S3 API. @@ -58,98 +59,98 @@ func registerAPIRouter(router *mux.Router, encryptionEnabled, allowSSEKMS bool) for _, bucket := range routers { // Object operations // HeadObject - bucket.Methods("HEAD").Path("/{object:.+}").HandlerFunc(httpTraceAll(api.HeadObjectHandler)) + bucket.Methods(http.MethodHead).Path("/{object:.+}").HandlerFunc(httpTraceAll(api.HeadObjectHandler)) // CopyObjectPart - bucket.Methods("PUT").Path("/{object:.+}").HeadersRegexp("X-Amz-Copy-Source", ".*?(\\/|%2F).*?").HandlerFunc(httpTraceAll(api.CopyObjectPartHandler)).Queries("partNumber", "{partNumber:[0-9]+}", "uploadId", "{uploadId:.*}") + bucket.Methods(http.MethodPut).Path("/{object:.+}").HeadersRegexp(xhttp.AmzCopySource, ".*?(\\/|%2F).*?").HandlerFunc(httpTraceAll(api.CopyObjectPartHandler)).Queries("partNumber", "{partNumber:[0-9]+}", "uploadId", "{uploadId:.*}") // PutObjectPart - bucket.Methods("PUT").Path("/{object:.+}").HandlerFunc(httpTraceHdrs(api.PutObjectPartHandler)).Queries("partNumber", "{partNumber:[0-9]+}", "uploadId", "{uploadId:.*}") + bucket.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc(httpTraceHdrs(api.PutObjectPartHandler)).Queries("partNumber", "{partNumber:[0-9]+}", "uploadId", "{uploadId:.*}") // ListObjectPxarts - bucket.Methods("GET").Path("/{object:.+}").HandlerFunc(httpTraceAll(api.ListObjectPartsHandler)).Queries("uploadId", "{uploadId:.*}") + bucket.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(httpTraceAll(api.ListObjectPartsHandler)).Queries("uploadId", "{uploadId:.*}") // CompleteMultipartUpload - bucket.Methods("POST").Path("/{object:.+}").HandlerFunc(httpTraceAll(api.CompleteMultipartUploadHandler)).Queries("uploadId", "{uploadId:.*}") + bucket.Methods(http.MethodPost).Path("/{object:.+}").HandlerFunc(httpTraceAll(api.CompleteMultipartUploadHandler)).Queries("uploadId", "{uploadId:.*}") // NewMultipartUpload - bucket.Methods("POST").Path("/{object:.+}").HandlerFunc(httpTraceAll(api.NewMultipartUploadHandler)).Queries("uploads", "") + bucket.Methods(http.MethodPost).Path("/{object:.+}").HandlerFunc(httpTraceAll(api.NewMultipartUploadHandler)).Queries("uploads", "") // AbortMultipartUpload - bucket.Methods("DELETE").Path("/{object:.+}").HandlerFunc(httpTraceAll(api.AbortMultipartUploadHandler)).Queries("uploadId", "{uploadId:.*}") + bucket.Methods(http.MethodDelete).Path("/{object:.+}").HandlerFunc(httpTraceAll(api.AbortMultipartUploadHandler)).Queries("uploadId", "{uploadId:.*}") // GetObjectACL - this is a dummy call. - bucket.Methods("GET").Path("/{object:.+}").HandlerFunc(httpTraceHdrs(api.GetObjectACLHandler)).Queries("acl", "") + bucket.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(httpTraceHdrs(api.GetObjectACLHandler)).Queries("acl", "") // GetObjectTagging - this is a dummy call. - bucket.Methods("GET").Path("/{object:.+}").HandlerFunc(httpTraceHdrs(api.GetObjectTaggingHandler)).Queries("tagging", "") + bucket.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(httpTraceHdrs(api.GetObjectTaggingHandler)).Queries("tagging", "") // SelectObjectContent - bucket.Methods("POST").Path("/{object:.+}").HandlerFunc(httpTraceHdrs(api.SelectObjectContentHandler)).Queries("select", "").Queries("select-type", "2") + bucket.Methods(http.MethodPost).Path("/{object:.+}").HandlerFunc(httpTraceHdrs(api.SelectObjectContentHandler)).Queries("select", "").Queries("select-type", "2") // GetObject - bucket.Methods("GET").Path("/{object:.+}").HandlerFunc(httpTraceHdrs(api.GetObjectHandler)) + bucket.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(httpTraceHdrs(api.GetObjectHandler)) // CopyObject - bucket.Methods("PUT").Path("/{object:.+}").HeadersRegexp("X-Amz-Copy-Source", ".*?(\\/|%2F).*?").HandlerFunc(httpTraceAll(api.CopyObjectHandler)) + bucket.Methods(http.MethodPut).Path("/{object:.+}").HeadersRegexp(xhttp.AmzCopySource, ".*?(\\/|%2F).*?").HandlerFunc(httpTraceAll(api.CopyObjectHandler)) // PutObject - bucket.Methods("PUT").Path("/{object:.+}").HandlerFunc(httpTraceHdrs(api.PutObjectHandler)) + bucket.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc(httpTraceHdrs(api.PutObjectHandler)) // DeleteObject - bucket.Methods("DELETE").Path("/{object:.+}").HandlerFunc(httpTraceAll(api.DeleteObjectHandler)) + bucket.Methods(http.MethodDelete).Path("/{object:.+}").HandlerFunc(httpTraceAll(api.DeleteObjectHandler)) /// Bucket operations // GetBucketLocation - bucket.Methods("GET").HandlerFunc(httpTraceAll(api.GetBucketLocationHandler)).Queries("location", "") + bucket.Methods(http.MethodGet).HandlerFunc(httpTraceAll(api.GetBucketLocationHandler)).Queries("location", "") // GetBucketPolicy - bucket.Methods("GET").HandlerFunc(httpTraceAll(api.GetBucketPolicyHandler)).Queries("policy", "") + bucket.Methods(http.MethodGet).HandlerFunc(httpTraceAll(api.GetBucketPolicyHandler)).Queries("policy", "") // Dummy Bucket Calls // GetBucketACL -- this is a dummy call. - bucket.Methods("GET").HandlerFunc(httpTraceAll(api.GetBucketACLHandler)).Queries("acl", "") + bucket.Methods(http.MethodGet).HandlerFunc(httpTraceAll(api.GetBucketACLHandler)).Queries("acl", "") // GetBucketCors - this is a dummy call. - bucket.Methods("GET").HandlerFunc(httpTraceAll(api.GetBucketCorsHandler)).Queries("cors", "") + bucket.Methods(http.MethodGet).HandlerFunc(httpTraceAll(api.GetBucketCorsHandler)).Queries("cors", "") // GetBucketWebsiteHandler - this is a dummy call. - bucket.Methods("GET").HandlerFunc(httpTraceAll(api.GetBucketWebsiteHandler)).Queries("website", "") + bucket.Methods(http.MethodGet).HandlerFunc(httpTraceAll(api.GetBucketWebsiteHandler)).Queries("website", "") // GetBucketVersioningHandler - this is a dummy call. - bucket.Methods("GET").HandlerFunc(httpTraceAll(api.GetBucketVersioningHandler)).Queries("versioning", "") + bucket.Methods(http.MethodGet).HandlerFunc(httpTraceAll(api.GetBucketVersioningHandler)).Queries("versioning", "") // GetBucketAccelerateHandler - this is a dummy call. - bucket.Methods("GET").HandlerFunc(httpTraceAll(api.GetBucketAccelerateHandler)).Queries("accelerate", "") + bucket.Methods(http.MethodGet).HandlerFunc(httpTraceAll(api.GetBucketAccelerateHandler)).Queries("accelerate", "") // GetBucketRequestPaymentHandler - this is a dummy call. - bucket.Methods("GET").HandlerFunc(httpTraceAll(api.GetBucketRequestPaymentHandler)).Queries("requestPayment", "") + bucket.Methods(http.MethodGet).HandlerFunc(httpTraceAll(api.GetBucketRequestPaymentHandler)).Queries("requestPayment", "") // GetBucketLoggingHandler - this is a dummy call. - bucket.Methods("GET").HandlerFunc(httpTraceAll(api.GetBucketLoggingHandler)).Queries("logging", "") + bucket.Methods(http.MethodGet).HandlerFunc(httpTraceAll(api.GetBucketLoggingHandler)).Queries("logging", "") // GetBucketLifecycleHandler - this is a dummy call. - bucket.Methods("GET").HandlerFunc(httpTraceAll(api.GetBucketLifecycleHandler)).Queries("lifecycle", "") + bucket.Methods(http.MethodGet).HandlerFunc(httpTraceAll(api.GetBucketLifecycleHandler)).Queries("lifecycle", "") // GetBucketReplicationHandler - this is a dummy call. - bucket.Methods("GET").HandlerFunc(httpTraceAll(api.GetBucketReplicationHandler)).Queries("replication", "") + bucket.Methods(http.MethodGet).HandlerFunc(httpTraceAll(api.GetBucketReplicationHandler)).Queries("replication", "") // GetBucketTaggingHandler - this is a dummy call. - bucket.Methods("GET").HandlerFunc(httpTraceAll(api.GetBucketTaggingHandler)).Queries("tagging", "") + bucket.Methods(http.MethodGet).HandlerFunc(httpTraceAll(api.GetBucketTaggingHandler)).Queries("tagging", "") //DeleteBucketWebsiteHandler - bucket.Methods("DELETE").HandlerFunc(httpTraceAll(api.DeleteBucketWebsiteHandler)).Queries("website", "") + bucket.Methods(http.MethodDelete).HandlerFunc(httpTraceAll(api.DeleteBucketWebsiteHandler)).Queries("website", "") // DeleteBucketTaggingHandler - bucket.Methods("DELETE").HandlerFunc(httpTraceAll(api.DeleteBucketTaggingHandler)).Queries("tagging", "") + bucket.Methods(http.MethodDelete).HandlerFunc(httpTraceAll(api.DeleteBucketTaggingHandler)).Queries("tagging", "") // GetBucketNotification - bucket.Methods("GET").HandlerFunc(httpTraceAll(api.GetBucketNotificationHandler)).Queries("notification", "") + bucket.Methods(http.MethodGet).HandlerFunc(httpTraceAll(api.GetBucketNotificationHandler)).Queries("notification", "") // ListenBucketNotification - bucket.Methods("GET").HandlerFunc(httpTraceAll(api.ListenBucketNotificationHandler)).Queries("events", "{events:.*}") + bucket.Methods(http.MethodGet).HandlerFunc(httpTraceAll(api.ListenBucketNotificationHandler)).Queries("events", "{events:.*}") // ListMultipartUploads - bucket.Methods("GET").HandlerFunc(httpTraceAll(api.ListMultipartUploadsHandler)).Queries("uploads", "") + bucket.Methods(http.MethodGet).HandlerFunc(httpTraceAll(api.ListMultipartUploadsHandler)).Queries("uploads", "") // ListObjectsV2 - bucket.Methods("GET").HandlerFunc(httpTraceAll(api.ListObjectsV2Handler)).Queries("list-type", "2") + bucket.Methods(http.MethodGet).HandlerFunc(httpTraceAll(api.ListObjectsV2Handler)).Queries("list-type", "2") // ListObjectsV1 (Legacy) - bucket.Methods("GET").HandlerFunc(httpTraceAll(api.ListObjectsV1Handler)) + bucket.Methods(http.MethodGet).HandlerFunc(httpTraceAll(api.ListObjectsV1Handler)) // PutBucketPolicy - bucket.Methods("PUT").HandlerFunc(httpTraceAll(api.PutBucketPolicyHandler)).Queries("policy", "") + bucket.Methods(http.MethodPut).HandlerFunc(httpTraceAll(api.PutBucketPolicyHandler)).Queries("policy", "") // PutBucketNotification - bucket.Methods("PUT").HandlerFunc(httpTraceAll(api.PutBucketNotificationHandler)).Queries("notification", "") + bucket.Methods(http.MethodPut).HandlerFunc(httpTraceAll(api.PutBucketNotificationHandler)).Queries("notification", "") // PutBucket - bucket.Methods("PUT").HandlerFunc(httpTraceAll(api.PutBucketHandler)) + bucket.Methods(http.MethodPut).HandlerFunc(httpTraceAll(api.PutBucketHandler)) // HeadBucket - bucket.Methods("HEAD").HandlerFunc(httpTraceAll(api.HeadBucketHandler)) + bucket.Methods(http.MethodHead).HandlerFunc(httpTraceAll(api.HeadBucketHandler)) // PostPolicy - bucket.Methods("POST").HeadersRegexp("Content-Type", "multipart/form-data*").HandlerFunc(httpTraceHdrs(api.PostPolicyBucketHandler)) + bucket.Methods(http.MethodPost).HeadersRegexp(xhttp.ContentType, "multipart/form-data*").HandlerFunc(httpTraceHdrs(api.PostPolicyBucketHandler)) // DeleteMultipleObjects - bucket.Methods("POST").HandlerFunc(httpTraceAll(api.DeleteMultipleObjectsHandler)).Queries("delete", "") + bucket.Methods(http.MethodPost).HandlerFunc(httpTraceAll(api.DeleteMultipleObjectsHandler)).Queries("delete", "") // DeleteBucketPolicy - bucket.Methods("DELETE").HandlerFunc(httpTraceAll(api.DeleteBucketPolicyHandler)).Queries("policy", "") + bucket.Methods(http.MethodDelete).HandlerFunc(httpTraceAll(api.DeleteBucketPolicyHandler)).Queries("policy", "") // DeleteBucket - bucket.Methods("DELETE").HandlerFunc(httpTraceAll(api.DeleteBucketHandler)) + bucket.Methods(http.MethodDelete).HandlerFunc(httpTraceAll(api.DeleteBucketHandler)) } /// Root operation // ListBuckets - apiRouter.Methods("GET").Path("/").HandlerFunc(httpTraceAll(api.ListBucketsHandler)) + apiRouter.Methods(http.MethodGet).Path("/").HandlerFunc(httpTraceAll(api.ListBucketsHandler)) // If none of the routes match. apiRouter.NotFoundHandler = http.HandlerFunc(httpTraceAll(notFoundHandler)) diff --git a/cmd/auth-handler.go b/cmd/auth-handler.go index 4efb5511f..2e16f668b 100644 --- a/cmd/auth-handler.go +++ b/cmd/auth-handler.go @@ -29,6 +29,7 @@ import ( "strings" jwtgo "github.com/dgrijalva/jwt-go" + xhttp "github.com/minio/minio/cmd/http" "github.com/minio/minio/cmd/logger" "github.com/minio/minio/pkg/auth" "github.com/minio/minio/pkg/hash" @@ -38,41 +39,41 @@ import ( // Verify if request has JWT. func isRequestJWT(r *http.Request) bool { - return strings.HasPrefix(r.Header.Get("Authorization"), jwtAlgorithm) + return strings.HasPrefix(r.Header.Get(xhttp.Authorization), jwtAlgorithm) } // Verify if request has AWS Signature Version '4'. func isRequestSignatureV4(r *http.Request) bool { - return strings.HasPrefix(r.Header.Get("Authorization"), signV4Algorithm) + return strings.HasPrefix(r.Header.Get(xhttp.Authorization), signV4Algorithm) } // Verify if request has AWS Signature Version '2'. func isRequestSignatureV2(r *http.Request) bool { - return (!strings.HasPrefix(r.Header.Get("Authorization"), signV4Algorithm) && - strings.HasPrefix(r.Header.Get("Authorization"), signV2Algorithm)) + return (!strings.HasPrefix(r.Header.Get(xhttp.Authorization), signV4Algorithm) && + strings.HasPrefix(r.Header.Get(xhttp.Authorization), signV2Algorithm)) } // Verify if request has AWS PreSign Version '4'. func isRequestPresignedSignatureV4(r *http.Request) bool { - _, ok := r.URL.Query()["X-Amz-Credential"] + _, ok := r.URL.Query()[xhttp.AmzCredential] return ok } // Verify request has AWS PreSign Version '2'. func isRequestPresignedSignatureV2(r *http.Request) bool { - _, ok := r.URL.Query()["AWSAccessKeyId"] + _, ok := r.URL.Query()[xhttp.AmzAccessKeyID] return ok } // Verify if request has AWS Post policy Signature Version '4'. func isRequestPostPolicySignatureV4(r *http.Request) bool { - return strings.Contains(r.Header.Get("Content-Type"), "multipart/form-data") && + return strings.Contains(r.Header.Get(xhttp.ContentType), "multipart/form-data") && r.Method == http.MethodPost } // Verify if the request has AWS Streaming Signature Version '4'. This is only valid for 'PUT' operation. func isRequestSignStreamingV4(r *http.Request) bool { - return r.Header.Get("x-amz-content-sha256") == streamingContentSHA256 && + return r.Header.Get(xhttp.AmzContentSha256) == streamingContentSHA256 && r.Method == http.MethodPut } @@ -109,9 +110,9 @@ func getRequestAuthType(r *http.Request) authType { return authTypeJWT } else if isRequestPostPolicySignatureV4(r) { return authTypePostPolicy - } else if _, ok := r.URL.Query()["Action"]; ok { + } else if _, ok := r.URL.Query()[xhttp.Action]; ok { return authTypeSTS - } else if _, ok := r.Header["Authorization"]; !ok { + } else if _, ok := r.Header[xhttp.Authorization]; !ok { return authTypeAnonymous } return authTypeUnknown @@ -121,7 +122,7 @@ func getRequestAuthType(r *http.Request) authType { // It does not accept presigned or JWT or anonymous requests. func checkAdminRequestAuthType(ctx context.Context, r *http.Request, region string) APIErrorCode { s3Err := ErrAccessDenied - if _, ok := r.Header["X-Amz-Content-Sha256"]; ok && + if _, ok := r.Header[xhttp.AmzContentSha256]; ok && getRequestAuthType(r) == authTypeSigned && !skipContentSha256Cksum(r) { // We only support admin credentials to access admin APIs. @@ -148,11 +149,11 @@ func checkAdminRequestAuthType(ctx context.Context, r *http.Request, region stri // Fetch the security token set by the client. func getSessionToken(r *http.Request) (token string) { - token = r.Header.Get("X-Amz-Security-Token") + token = r.Header.Get(xhttp.AmzSecurityToken) if token != "" { return token } - return r.URL.Query().Get("X-Amz-Security-Token") + return r.URL.Query().Get(xhttp.AmzSecurityToken) } // Fetch claims in the security token returned by the client, doesn't return @@ -370,8 +371,8 @@ func isReqAuthenticated(ctx context.Context, r *http.Request, region string, sty contentMD5, contentSHA256 []byte ) // Extract 'Content-Md5' if present. - if _, ok := r.Header["Content-Md5"]; ok { - contentMD5, err = base64.StdEncoding.Strict().DecodeString(r.Header.Get("Content-Md5")) + if _, ok := r.Header[xhttp.ContentMD5]; ok { + contentMD5, err = base64.StdEncoding.Strict().DecodeString(r.Header.Get(xhttp.ContentMD5)) if err != nil || len(contentMD5) == 0 { return ErrInvalidDigest } @@ -380,14 +381,14 @@ func isReqAuthenticated(ctx context.Context, r *http.Request, region string, sty // Extract either 'X-Amz-Content-Sha256' header or 'X-Amz-Content-Sha256' query parameter (if V4 presigned) // Do not verify 'X-Amz-Content-Sha256' if skipSHA256. if skipSHA256 := skipContentSha256Cksum(r); !skipSHA256 && isRequestPresignedSignatureV4(r) { - if sha256Sum, ok := r.URL.Query()["X-Amz-Content-Sha256"]; ok && len(sha256Sum) > 0 { + if sha256Sum, ok := r.URL.Query()[xhttp.AmzContentSha256]; ok && len(sha256Sum) > 0 { contentSHA256, err = hex.DecodeString(sha256Sum[0]) if err != nil { return ErrContentSHA256Mismatch } } - } else if _, ok := r.Header["X-Amz-Content-Sha256"]; !skipSHA256 && ok { - contentSHA256, err = hex.DecodeString(r.Header.Get("X-Amz-Content-Sha256")) + } else if _, ok := r.Header[xhttp.AmzContentSha256]; !skipSHA256 && ok { + contentSHA256, err = hex.DecodeString(r.Header.Get(xhttp.AmzContentSha256)) if err != nil || len(contentSHA256) == 0 { return ErrContentSHA256Mismatch } @@ -395,7 +396,8 @@ func isReqAuthenticated(ctx context.Context, r *http.Request, region string, sty // Verify 'Content-Md5' and/or 'X-Amz-Content-Sha256' if present. // The verification happens implicit during reading. - reader, err := hash.NewReader(r.Body, -1, hex.EncodeToString(contentMD5), hex.EncodeToString(contentSHA256), -1, globalCLIContext.StrictS3Compat) + reader, err := hash.NewReader(r.Body, -1, hex.EncodeToString(contentMD5), + hex.EncodeToString(contentSHA256), -1, globalCLIContext.StrictS3Compat) if err != nil { return toAPIErrorCode(ctx, err) } diff --git a/cmd/bucket-handlers.go b/cmd/bucket-handlers.go index e8b6c9e68..545e9f2f3 100644 --- a/cmd/bucket-handlers.go +++ b/cmd/bucket-handlers.go @@ -32,6 +32,7 @@ import ( "github.com/minio/minio-go/v6/pkg/set" "github.com/minio/minio/cmd/crypto" + xhttp "github.com/minio/minio/cmd/http" "github.com/minio/minio/cmd/logger" "github.com/minio/minio/pkg/dns" "github.com/minio/minio/pkg/event" @@ -445,7 +446,8 @@ func (api objectAPIHandlers) PutBucketHandler(w http.ResponseWriter, r *http.Req } // Make sure to add Location information here only for bucket - w.Header().Set("Location", getObjectLocation(r, globalDomainNames, bucket, "")) + w.Header().Set(xhttp.Location, + getObjectLocation(r, globalDomainNames, bucket, "")) writeSuccessResponseHeadersOnly(w) return @@ -466,7 +468,7 @@ func (api objectAPIHandlers) PutBucketHandler(w http.ResponseWriter, r *http.Req } // Make sure to add Location information here only for bucket - w.Header().Set("Location", path.Clean(r.URL.Path)) // Clean any trailing slashes. + w.Header().Set(xhttp.Location, path.Clean(r.URL.Path)) // Clean any trailing slashes. writeSuccessResponseHeadersOnly(w) } @@ -681,8 +683,8 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h } location := getObjectLocation(r, globalDomainNames, bucket, object) - w.Header()["ETag"] = []string{`"` + objInfo.ETag + `"`} - w.Header().Set("Location", location) + w.Header()[xhttp.ETag] = []string{`"` + objInfo.ETag + `"`} + w.Header().Set(xhttp.Location, location) // Notify object created event. defer sendEvent(eventArgs{ diff --git a/cmd/gateway-common.go b/cmd/gateway-common.go index dcf099c59..a1f9a1ba4 100644 --- a/cmd/gateway-common.go +++ b/cmd/gateway-common.go @@ -21,6 +21,7 @@ import ( "os" "strings" + xhttp "github.com/minio/minio/cmd/http" "github.com/minio/minio/cmd/logger" "github.com/minio/minio/pkg/hash" @@ -166,7 +167,7 @@ func FromMinioClientListMultipartsInfo(lmur minio.ListMultipartUploadsResult) Li // FromMinioClientObjectInfo converts minio ObjectInfo to gateway ObjectInfo func FromMinioClientObjectInfo(bucket string, oi minio.ObjectInfo) ObjectInfo { userDefined := FromMinioClientMetadata(oi.Metadata) - userDefined["Content-Type"] = oi.ContentType + userDefined[xhttp.ContentType] = oi.ContentType return ObjectInfo{ Bucket: bucket, @@ -176,7 +177,7 @@ func FromMinioClientObjectInfo(bucket string, oi minio.ObjectInfo) ObjectInfo { ETag: canonicalizeETag(oi.ETag), UserDefined: userDefined, ContentType: oi.ContentType, - ContentEncoding: oi.Metadata.Get("Content-Encoding"), + ContentEncoding: oi.Metadata.Get(xhttp.ContentEncoding), StorageClass: oi.StorageClass, Expires: oi.Expires, } diff --git a/cmd/generic-handlers.go b/cmd/generic-handlers.go index 9139d78d5..85bc6a3f8 100644 --- a/cmd/generic-handlers.go +++ b/cmd/generic-handlers.go @@ -28,6 +28,7 @@ import ( humanize "github.com/dustin/go-humanize" "github.com/minio/minio/cmd/crypto" + xhttp "github.com/minio/minio/cmd/http" "github.com/minio/minio/cmd/logger" "github.com/minio/minio/pkg/dns" "github.com/minio/minio/pkg/handlers" @@ -261,10 +262,10 @@ func (h cacheControlHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if hasSuffix(r.URL.Path, ".js") || r.URL.Path == minioReservedBucketPath+"/favicon.ico" { // For assets set cache expiry of one year. For each release, the name // of the asset name will change and hence it can not be served from cache. - w.Header().Set("Cache-Control", "max-age=31536000") + w.Header().Set(xhttp.CacheControl, "max-age=31536000") } else { // For non asset requests we serve index.html which will never be cached. - w.Header().Set("Cache-Control", "no-store") + w.Header().Set(xhttp.CacheControl, "no-store") } } } @@ -380,15 +381,15 @@ type resourceHandler struct { // setCorsHandler handler for CORS (Cross Origin Resource Sharing) func setCorsHandler(h http.Handler) http.Handler { commonS3Headers := []string{ - "Date", - "ETag", - "Server", - "Connection", - "Accept-Ranges", - "Content-Range", - "Content-Encoding", - "Content-Length", - "Content-Type", + xhttp.Date, + xhttp.ETag, + xhttp.ServerInfo, + xhttp.Connection, + xhttp.AcceptRanges, + xhttp.ContentRange, + xhttp.ContentEncoding, + xhttp.ContentLength, + xhttp.ContentType, "X-Amz*", "x-amz*", "*", @@ -765,7 +766,7 @@ func addCustomHeaders(h http.Handler) http.Handler { func (s customHeaderHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Set custom headers such as x-amz-request-id for each request. - w.Header().Set(responseRequestIDKey, mustGetRequestID(UTCNow())) + w.Header().Set(xhttp.AmzRequestID, mustGetRequestID(UTCNow())) s.handler.ServeHTTP(logger.NewResponseWriter(w), r) } diff --git a/cmd/handler-utils.go b/cmd/handler-utils.go index bcc913296..f55c47819 100644 --- a/cmd/handler-utils.go +++ b/cmd/handler-utils.go @@ -27,6 +27,7 @@ import ( "net/url" "strings" + xhttp "github.com/minio/minio/cmd/http" "github.com/minio/minio/cmd/logger" "github.com/minio/minio/pkg/auth" "github.com/minio/minio/pkg/handlers" @@ -219,8 +220,8 @@ func extractReqParams(r *http.Request) map[string]string { func extractRespElements(w http.ResponseWriter) map[string]string { return map[string]string{ - "requestId": w.Header().Get(responseRequestIDKey), - "content-length": w.Header().Get("Content-Length"), + "requestId": w.Header().Get(xhttp.AmzRequestID), + "content-length": w.Header().Get(xhttp.ContentLength), // Add more fields here. } } diff --git a/cmd/http/headers.go b/cmd/http/headers.go new file mode 100644 index 000000000..c2db6ae2f --- /dev/null +++ b/cmd/http/headers.go @@ -0,0 +1,81 @@ +/* + * MinIO Cloud Storage, (C) 2019 MinIO, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package http + +// Standard S3 HTTP response constants +const ( + LastModified = "Last-Modified" + Date = "Date" + ETag = "ETag" + ContentType = "Content-Type" + ContentMD5 = "Content-Md5" + ContentEncoding = "Content-Encoding" + Expires = "Expires" + ContentLength = "Content-Length" + ContentLanguage = "Content-Language" + ContentRange = "Content-Range" + Connection = "Connection" + AcceptRanges = "Accept-Ranges" + AmzBucketRegion = "X-Amz-Bucket-Region" + ServerInfo = "Server" + RetryAfter = "Retry-After" + Location = "Location" + CacheControl = "Cache-Control" + ContentDisposition = "Content-Disposition" + Authorization = "Authorization" + Action = "Action" +) + +// Standard S3 HTTP request constants +const ( + IfModifiedSince = "If-Modified-Since" + IfUnmodifiedSince = "If-Unmodified-Since" + IfMatch = "If-Match" + IfNoneMatch = "If-None-Match" + + // S3 extensions + AmzCopySourceIfModifiedSince = "x-amz-copy-source-if-modified-since" + AmzCopySourceIfUnmodifiedSince = "x-amz-copy-source-if-unmodified-since" + + AmzCopySourceIfNoneMatch = "x-amz-copy-source-if-none-match" + AmzCopySourceIfMatch = "x-amz-copy-source-if-match" + + AmzCopySource = "X-Amz-Copy-Source" + AmzCopySourceVersionID = "X-Amz-Copy-Source-Version-Id" + AmzCopySourceRange = "X-Amz-Copy-Source-Range" + + // Signature V4 related contants. + AmzContentSha256 = "X-Amz-Content-Sha256" + AmzDate = "X-Amz-Date" + AmzAlgorithm = "X-Amz-Algorithm" + AmzExpires = "X-Amz-Expires" + AmzSignedHeaders = "X-Amz-SignedHeaders" + AmzSignature = "X-Amz-Signature" + AmzCredential = "X-Amz-Credential" + AmzSecurityToken = "X-Amz-Security-Token" + AmzDecodedContentLength = "X-Amz-Decoded-Content-Length" + + // Signature v2 related constants + AmzSignatureV2 = "Signature" + AmzAccessKeyID = "AWSAccessKeyId" + + // Response request id. + AmzRequestID = "x-amz-request-id" + + // Deployment id. + MinioDeploymentID = "x-minio-deployment-id" +) diff --git a/cmd/logger/message/audit/entry.go b/cmd/logger/message/audit/entry.go index 161c5477b..8a68e0c23 100644 --- a/cmd/logger/message/audit/entry.go +++ b/cmd/logger/message/audit/entry.go @@ -22,6 +22,7 @@ import ( "time" "github.com/gorilla/mux" + xhttp "github.com/minio/minio/cmd/http" "github.com/minio/minio/pkg/handlers" ) @@ -67,13 +68,13 @@ func ToEntry(w http.ResponseWriter, r *http.Request, api string, statusCode int, for k, v := range w.Header() { respHeader[k] = strings.Join(v, ",") } - respHeader["Etag"] = strings.Trim(respHeader["Etag"], `"`) + respHeader[xhttp.ETag] = strings.Trim(respHeader[xhttp.ETag], `"`) entry := Entry{ Version: Version, DeploymentID: deploymentID, RemoteHost: handlers.GetSourceIP(r), - RequestID: w.Header().Get("x-amz-request-id"), + RequestID: w.Header().Get(xhttp.AmzRequestID), UserAgent: r.UserAgent(), Time: time.Now().UTC().Format(time.RFC3339Nano), ReqQuery: reqQuery, diff --git a/cmd/logger/target/http/http.go b/cmd/logger/target/http/http.go index ef2360e73..803b1d9ef 100644 --- a/cmd/logger/target/http/http.go +++ b/cmd/logger/target/http/http.go @@ -20,6 +20,7 @@ import ( "bytes" "encoding/json" "errors" + "net/http" gohttp "net/http" xhttp "github.com/minio/minio/cmd/http" @@ -49,11 +50,11 @@ func (h *Target) startHTTPLogger() { continue } - req, err := gohttp.NewRequest("POST", h.endpoint, bytes.NewBuffer(logJSON)) + req, err := gohttp.NewRequest(http.MethodPost, h.endpoint, bytes.NewBuffer(logJSON)) if err != nil { continue } - req.Header.Set("Content-Type", "application/json") + req.Header.Set(xhttp.ContentType, "application/json") resp, err := h.client.Do(req) if err != nil { diff --git a/cmd/object-api-utils.go b/cmd/object-api-utils.go index 338494d4f..bbbcf0967 100644 --- a/cmd/object-api-utils.go +++ b/cmd/object-api-utils.go @@ -36,6 +36,7 @@ import ( snappy "github.com/golang/snappy" "github.com/minio/minio-go/v6/pkg/s3utils" "github.com/minio/minio/cmd/crypto" + xhttp "github.com/minio/minio/cmd/http" "github.com/minio/minio/cmd/logger" "github.com/minio/minio/pkg/dns" "github.com/minio/minio/pkg/hash" @@ -346,7 +347,7 @@ func isCompressible(header http.Header, object string) bool { // Eliminate the non-compressible objects. func excludeForCompression(header http.Header, object string) bool { objStr := object - contentType := header.Get("Content-Type") + contentType := header.Get(xhttp.ContentType) if globalIsCompressionEnabled { // We strictly disable compression for standard extensions/content-types (`compressed`). if hasStringSuffixInSlice(objStr, standardExcludeCompressExtensions) || hasPattern(standardExcludeCompressContentTypes, contentType) { diff --git a/cmd/object-handlers-common.go b/cmd/object-handlers-common.go index 04a9fd05d..3541f0048 100644 --- a/cmd/object-handlers-common.go +++ b/cmd/object-handlers-common.go @@ -23,6 +23,7 @@ import ( "time" "github.com/minio/minio/cmd/crypto" + xhttp "github.com/minio/minio/cmd/http" "github.com/minio/minio/pkg/event" "github.com/minio/minio/pkg/handlers" ) @@ -49,7 +50,7 @@ func checkCopyObjectPartPreconditions(ctx context.Context, w http.ResponseWriter // x-amz-copy-source-if-none-match func checkCopyObjectPreconditions(ctx context.Context, w http.ResponseWriter, r *http.Request, objInfo ObjectInfo, encETag string) bool { // Return false for methods other than GET and HEAD. - if r.Method != "PUT" { + if r.Method != http.MethodPut { return false } if encETag == "" { @@ -68,15 +69,15 @@ func checkCopyObjectPreconditions(ctx context.Context, w http.ResponseWriter, r setCommonHeaders(w) // set object-related metadata headers - w.Header().Set("Last-Modified", objInfo.ModTime.UTC().Format(http.TimeFormat)) + w.Header().Set(xhttp.LastModified, objInfo.ModTime.UTC().Format(http.TimeFormat)) if objInfo.ETag != "" { - w.Header()["ETag"] = []string{"\"" + objInfo.ETag + "\""} + w.Header()[xhttp.ETag] = []string{"\"" + objInfo.ETag + "\""} } } // x-amz-copy-source-if-modified-since: Return the object only if it has been modified // since the specified time otherwise return 412 (precondition failed). - ifModifiedSinceHeader := r.Header.Get("x-amz-copy-source-if-modified-since") + ifModifiedSinceHeader := r.Header.Get(xhttp.AmzCopySourceIfModifiedSince) if ifModifiedSinceHeader != "" { if givenTime, err := time.Parse(http.TimeFormat, ifModifiedSinceHeader); err == nil { if !ifModifiedSince(objInfo.ModTime, givenTime) { @@ -90,7 +91,7 @@ func checkCopyObjectPreconditions(ctx context.Context, w http.ResponseWriter, r // x-amz-copy-source-if-unmodified-since : Return the object only if it has not been // modified since the specified time, otherwise return a 412 (precondition failed). - ifUnmodifiedSinceHeader := r.Header.Get("x-amz-copy-source-if-unmodified-since") + ifUnmodifiedSinceHeader := r.Header.Get(xhttp.AmzCopySourceIfUnmodifiedSince) if ifUnmodifiedSinceHeader != "" { if givenTime, err := time.Parse(http.TimeFormat, ifUnmodifiedSinceHeader); err == nil { if ifModifiedSince(objInfo.ModTime, givenTime) { @@ -106,7 +107,7 @@ func checkCopyObjectPreconditions(ctx context.Context, w http.ResponseWriter, r // x-amz-copy-source-if-match : Return the object only if its entity tag (ETag) is the // same as the one specified; otherwise return a 412 (precondition failed). - ifMatchETagHeader := r.Header.Get("x-amz-copy-source-if-match") + ifMatchETagHeader := r.Header.Get(xhttp.AmzCopySourceIfMatch) if ifMatchETagHeader != "" { etag := objInfo.ETag if shouldDecryptEtag { @@ -122,7 +123,7 @@ func checkCopyObjectPreconditions(ctx context.Context, w http.ResponseWriter, r // If-None-Match : Return the object only if its entity tag (ETag) is different from the // one specified otherwise, return a 304 (not modified). - ifNoneMatchETagHeader := r.Header.Get("x-amz-copy-source-if-none-match") + ifNoneMatchETagHeader := r.Header.Get(xhttp.AmzCopySourceIfNoneMatch) if ifNoneMatchETagHeader != "" { etag := objInfo.ETag if shouldDecryptEtag { @@ -147,7 +148,7 @@ func checkCopyObjectPreconditions(ctx context.Context, w http.ResponseWriter, r // If-None-Match func checkPreconditions(ctx context.Context, w http.ResponseWriter, r *http.Request, objInfo ObjectInfo) bool { // Return false for methods other than GET and HEAD. - if r.Method != "GET" && r.Method != "HEAD" { + if r.Method != http.MethodGet && r.Method != http.MethodHead { return false } // If the object doesn't have a modtime (IsZero), or the modtime @@ -163,15 +164,15 @@ func checkPreconditions(ctx context.Context, w http.ResponseWriter, r *http.Requ setCommonHeaders(w) // set object-related metadata headers - w.Header().Set("Last-Modified", objInfo.ModTime.UTC().Format(http.TimeFormat)) + w.Header().Set(xhttp.LastModified, objInfo.ModTime.UTC().Format(http.TimeFormat)) if objInfo.ETag != "" { - w.Header()["ETag"] = []string{"\"" + objInfo.ETag + "\""} + w.Header()[xhttp.ETag] = []string{"\"" + objInfo.ETag + "\""} } } // If-Modified-Since : Return the object only if it has been modified since the specified time, // otherwise return a 304 (not modified). - ifModifiedSinceHeader := r.Header.Get("If-Modified-Since") + ifModifiedSinceHeader := r.Header.Get(xhttp.IfModifiedSince) if ifModifiedSinceHeader != "" { if givenTime, err := time.Parse(http.TimeFormat, ifModifiedSinceHeader); err == nil { if !ifModifiedSince(objInfo.ModTime, givenTime) { @@ -185,7 +186,7 @@ func checkPreconditions(ctx context.Context, w http.ResponseWriter, r *http.Requ // If-Unmodified-Since : Return the object only if it has not been modified since the specified // time, otherwise return a 412 (precondition failed). - ifUnmodifiedSinceHeader := r.Header.Get("If-Unmodified-Since") + ifUnmodifiedSinceHeader := r.Header.Get(xhttp.IfUnmodifiedSince) if ifUnmodifiedSinceHeader != "" { if givenTime, err := time.Parse(http.TimeFormat, ifUnmodifiedSinceHeader); err == nil { if ifModifiedSince(objInfo.ModTime, givenTime) { @@ -199,7 +200,7 @@ func checkPreconditions(ctx context.Context, w http.ResponseWriter, r *http.Requ // If-Match : Return the object only if its entity tag (ETag) is the same as the one specified; // otherwise return a 412 (precondition failed). - ifMatchETagHeader := r.Header.Get("If-Match") + ifMatchETagHeader := r.Header.Get(xhttp.IfMatch) if ifMatchETagHeader != "" { if !isETagEqual(objInfo.ETag, ifMatchETagHeader) { // If the object ETag does not match with the specified ETag. @@ -211,7 +212,7 @@ func checkPreconditions(ctx context.Context, w http.ResponseWriter, r *http.Requ // If-None-Match : Return the object only if its entity tag (ETag) is different from the // one specified otherwise, return a 304 (not modified). - ifNoneMatchETagHeader := r.Header.Get("If-None-Match") + ifNoneMatchETagHeader := r.Header.Get(xhttp.IfNoneMatch) if ifNoneMatchETagHeader != "" { if isETagEqual(objInfo.ETag, ifNoneMatchETagHeader) { // If the object ETag matches with the specified ETag. diff --git a/cmd/object-handlers.go b/cmd/object-handlers.go index 6f3d0a519..4a6c57b87 100644 --- a/cmd/object-handlers.go +++ b/cmd/object-handlers.go @@ -36,6 +36,7 @@ import ( miniogo "github.com/minio/minio-go/v6" "github.com/minio/minio-go/v6/pkg/encrypt" "github.com/minio/minio/cmd/crypto" + xhttp "github.com/minio/minio/cmd/http" "github.com/minio/minio/cmd/logger" "github.com/minio/minio/pkg/dns" "github.com/minio/minio/pkg/event" @@ -50,12 +51,12 @@ import ( // supportedHeadGetReqParams - supported request parameters for GET and HEAD presigned request. var supportedHeadGetReqParams = map[string]string{ - "response-expires": "Expires", - "response-content-type": "Content-Type", - "response-cache-control": "Cache-Control", - "response-content-encoding": "Content-Encoding", - "response-content-language": "Content-Language", - "response-content-disposition": "Content-Disposition", + "response-expires": xhttp.Expires, + "response-content-type": xhttp.ContentType, + "response-cache-control": xhttp.CacheControl, + "response-content-encoding": xhttp.ContentEncoding, + "response-content-language": xhttp.ContentLanguage, + "response-content-disposition": xhttp.ContentDisposition, } const ( @@ -165,7 +166,7 @@ func (api objectAPIHandlers) SelectObjectContentHandler(w http.ResponseWriter, r BucketName: bucket, Key: object, Resource: r.URL.Path, - RequestID: w.Header().Get(responseRequestIDKey), + RequestID: w.Header().Get(xhttp.AmzRequestID), HostID: globalDeploymentID, }) writeResponse(w, serr.HTTPStatusCode(), encodedErrorResponse, mimeXML) @@ -207,7 +208,7 @@ func (api objectAPIHandlers) SelectObjectContentHandler(w http.ResponseWriter, r BucketName: bucket, Key: object, Resource: r.URL.Path, - RequestID: w.Header().Get(responseRequestIDKey), + RequestID: w.Header().Get(xhttp.AmzRequestID), HostID: globalDeploymentID, }) writeResponse(w, serr.HTTPStatusCode(), encodedErrorResponse, mimeXML) @@ -650,7 +651,7 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re // TODO: Reject requests where body/payload is present, for now we don't even read it. // Read escaped copy source path to check for parameters. - cpSrcPath := r.Header.Get("X-Amz-Copy-Source") + cpSrcPath := r.Header.Get(xhttp.AmzCopySource) // Check https://docs.aws.amazon.com/AmazonS3/latest/dev/ObjectVersioning.html // Regardless of whether you have enabled versioning, each object in your bucket @@ -668,7 +669,7 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re // Note that url.Parse does the unescaping cpSrcPath = u.Path } - if vid := r.Header.Get("X-Amz-Copy-Source-Version-Id"); vid != "" { + if vid := r.Header.Get(xhttp.AmzCopySourceVersionID); vid != "" { // Check if versionId header was added, if yes then check if // its non "null" value, we should error out since we do not support // any versions other than "null". @@ -1271,7 +1272,7 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req } else if hasServerSideEncryptionHeader(r.Header) { etag = getDecryptedETag(r.Header, objInfo, false) } - w.Header()["ETag"] = []string{"\"" + etag + "\""} + w.Header()[xhttp.ETag] = []string{"\"" + etag + "\""} if objectAPI.IsEncryptionSupported() { if crypto.IsEncrypted(objInfo.UserDefined) { @@ -1453,7 +1454,7 @@ func (api objectAPIHandlers) CopyObjectPartHandler(w http.ResponseWriter, r *htt } // Read escaped copy source path to check for parameters. - cpSrcPath := r.Header.Get("X-Amz-Copy-Source") + cpSrcPath := r.Header.Get(xhttp.AmzCopySource) // Check https://docs.aws.amazon.com/AmazonS3/latest/dev/ObjectVersioning.html // Regardless of whether you have enabled versioning, each object in your bucket @@ -1471,7 +1472,7 @@ func (api objectAPIHandlers) CopyObjectPartHandler(w http.ResponseWriter, r *htt // Note that url.Parse does the unescaping cpSrcPath = u.Path } - if vid := r.Header.Get("X-Amz-Copy-Source-Version-Id"); vid != "" { + if vid := r.Header.Get(xhttp.AmzCopySourceVersionID); vid != "" { // Check if X-Amz-Copy-Source-Version-Id header was added, if yes then check if // its non "null" value, we should error out since we do not support // any versions other than "null". @@ -1540,7 +1541,7 @@ func (api objectAPIHandlers) CopyObjectPartHandler(w http.ResponseWriter, r *htt // Get request range. var rs *HTTPRangeSpec - rangeHeader := r.Header.Get("x-amz-copy-source-range") + rangeHeader := r.Header.Get(xhttp.AmzCopySourceRange) if rangeHeader != "" { var parseRangeErr error if rs, parseRangeErr = parseCopyPartRangeSpec(rangeHeader); parseRangeErr != nil { @@ -1732,7 +1733,7 @@ func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http object := vars["object"] // X-Amz-Copy-Source shouldn't be set for this call. - if _, ok := r.Header["X-Amz-Copy-Source"]; ok { + if _, ok := r.Header[xhttp.AmzCopySource]; ok { writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidCopySource), r.URL, guessIsBrowserReq(r)) return } @@ -1750,7 +1751,7 @@ func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http rAuthType := getRequestAuthType(r) // For auth type streaming signature, we need to gather a different content length. if rAuthType == authTypeStreamingSigned { - if sizeStr, ok := r.Header["X-Amz-Decoded-Content-Length"]; ok { + if sizeStr, ok := r.Header[xhttp.AmzDecodedContentLength]; ok { if sizeStr[0] == "" { writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMissingContentLength), r.URL, guessIsBrowserReq(r)) return @@ -1952,7 +1953,7 @@ func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http if isEncrypted { etag = tryDecryptETag(objectEncryptionKey, partInfo.ETag, crypto.SSEC.IsRequested(r.Header)) } - w.Header()["ETag"] = []string{"\"" + etag + "\""} + w.Header()[xhttp.ETag] = []string{"\"" + etag + "\""} writeSuccessResponseHeadersOnly(w) } @@ -2267,15 +2268,15 @@ func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWrite case "SlowDown", "XMinioServerNotInitialized", "XMinioReadQuorum", "XMinioWriteQuorum": // Set retry-after header to indicate user-agents to retry request after 120secs. // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After - w.Header().Set("Retry-After", "120") + w.Header().Set(xhttp.RetryAfter, "120") } // Generate error response. errorResponse := getAPIErrorResponse(ctx, err, reqURL.Path, - w.Header().Get(responseRequestIDKey), globalDeploymentID) + w.Header().Get(xhttp.AmzRequestID), globalDeploymentID) encodedErrorResponse, _ := xml.Marshal(errorResponse) setCommonHeaders(w) - w.Header().Set("Content-Type", string(mimeXML)) + w.Header().Set(xhttp.ContentType, string(mimeXML)) w.Write(encodedErrorResponse) w.(http.Flusher).Flush() } @@ -2310,7 +2311,7 @@ func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWrite } // Set etag. - w.Header()["ETag"] = []string{"\"" + objInfo.ETag + "\""} + w.Header()[xhttp.ETag] = []string{"\"" + objInfo.ETag + "\""} // Write success response. writeSuccessResponseXML(w, encodedSuccessResponse) diff --git a/cmd/peer-rest-server.go b/cmd/peer-rest-server.go index 3dbe945d2..11e207bb6 100644 --- a/cmd/peer-rest-server.go +++ b/cmd/peer-rest-server.go @@ -28,6 +28,7 @@ import ( "time" "github.com/gorilla/mux" + xhttp "github.com/minio/minio/cmd/http" "github.com/minio/minio/cmd/logger" "github.com/minio/minio/pkg/event" xnet "github.com/minio/minio/pkg/net" @@ -675,7 +676,7 @@ func (s *peerRESTServer) TraceHandler(w http.ResponseWriter, r *http.Request) { } trcAll := r.URL.Query().Get(peerRESTTraceAll) == "true" - w.Header().Set("Connection", "close") + w.Header().Set(xhttp.Connection, "close") w.WriteHeader(http.StatusOK) w.(http.Flusher).Flush() diff --git a/cmd/server_test.go b/cmd/server_test.go index 58dbfc49b..515c0157d 100644 --- a/cmd/server_test.go +++ b/cmd/server_test.go @@ -34,6 +34,7 @@ import ( "time" humanize "github.com/dustin/go-humanize" + xhttp "github.com/minio/minio/cmd/http" "github.com/minio/minio/pkg/policy" ) @@ -1202,7 +1203,7 @@ func (s *TestSuiteCommon) TestPutObject(c *check) { c.Assert(err, nil) c.Assert(response.StatusCode, http.StatusOK) // The response Etag header should contain Md5sum of an empty string. - c.Assert(response.Header.Get("Etag"), "\""+emptyETag+"\"") + c.Assert(response.Header.Get(xhttp.ETag), "\""+emptyETag+"\"") } // TestListBuckets - Make request for listing of all buckets. @@ -2728,5 +2729,5 @@ func (s *TestSuiteCommon) TestObjectMultipart(c *check) { parts = append(parts, part) } etag := getCompleteMultipartMD5(parts) - c.Assert(canonicalizeETag(response.Header.Get("Etag")), etag) + c.Assert(canonicalizeETag(response.Header.Get(xhttp.ETag)), etag) } diff --git a/cmd/signature-v2.go b/cmd/signature-v2.go index 233b497ae..e1c861056 100644 --- a/cmd/signature-v2.go +++ b/cmd/signature-v2.go @@ -28,6 +28,8 @@ import ( "strconv" "strings" + xhttp "github.com/minio/minio/cmd/http" + "github.com/minio/minio/pkg/auth" ) @@ -68,13 +70,13 @@ var resourceList = []string{ func doesPolicySignatureV2Match(formValues http.Header) APIErrorCode { cred := globalServerConfig.GetCredential() - accessKey := formValues.Get("AWSAccessKeyId") + accessKey := formValues.Get(xhttp.AmzAccessKeyID) cred, _, s3Err := checkKeyValid(accessKey) if s3Err != ErrNone { return s3Err } policy := formValues.Get("Policy") - signature := formValues.Get("Signature") + signature := formValues.Get(xhttp.AmzSignatureV2) if !compareSignatureV2(signature, calculateSignatureV2(policy, cred.SecretKey)) { return ErrSignatureDoesNotMatch } @@ -131,11 +133,11 @@ func doesPresignV2SignatureMatch(r *http.Request) APIErrorCode { return ErrInvalidQueryParams } switch keyval[0] { - case "AWSAccessKeyId": + case xhttp.AmzAccessKeyID: accessKey = keyval[1] - case "Signature": + case xhttp.AmzSignatureV2: gotSignature = keyval[1] - case "Expires": + case xhttp.Expires: expires = keyval[1] default: filteredQueries = append(filteredQueries, query) @@ -177,13 +179,13 @@ func doesPresignV2SignatureMatch(r *http.Request) APIErrorCode { } func getReqAccessKeyV2(r *http.Request) (auth.Credentials, bool, APIErrorCode) { - if accessKey := r.URL.Query().Get("AWSAccessKeyId"); accessKey != "" { + if accessKey := r.URL.Query().Get(xhttp.AmzAccessKeyID); accessKey != "" { return checkKeyValid(accessKey) } // below is V2 Signed Auth header format, splitting on `space` (after the `AWS` string). // Authorization = "AWS" + " " + AWSAccessKeyId + ":" + Signature - authFields := strings.Split(r.Header.Get("Authorization"), " ") + authFields := strings.Split(r.Header.Get(xhttp.Authorization), " ") if len(authFields) != 2 { return auth.Credentials{}, false, ErrMissingFields } @@ -219,7 +221,7 @@ func getReqAccessKeyV2(r *http.Request) (auth.Credentials, bool, APIErrorCode) { func validateV2AuthHeader(r *http.Request) (auth.Credentials, APIErrorCode) { var cred auth.Credentials - v2Auth := r.Header.Get("Authorization") + v2Auth := r.Header.Get(xhttp.Authorization) if v2Auth == "" { return cred, ErrAuthHeaderEmpty } @@ -238,7 +240,7 @@ func validateV2AuthHeader(r *http.Request) (auth.Credentials, APIErrorCode) { } func doesSignV2Match(r *http.Request) APIErrorCode { - v2Auth := r.Header.Get("Authorization") + v2Auth := r.Header.Get(xhttp.Authorization) cred, apiError := validateV2AuthHeader(r) if apiError != ErrNone { return apiError @@ -380,7 +382,7 @@ func getStringToSignV2(method string, encodedResource, encodedQuery string, head date := expires // Date is set to expires date for presign operations. if date == "" { // If expires date is empty then request header Date is used. - date = headers.Get("Date") + date = headers.Get(xhttp.Date) } // From the Amazon docs: @@ -393,8 +395,8 @@ func getStringToSignV2(method string, encodedResource, encodedQuery string, head // CanonicalizedResource; stringToSign := strings.Join([]string{ method, - headers.Get("Content-MD5"), - headers.Get("Content-Type"), + headers.Get(xhttp.ContentMD5), + headers.Get(xhttp.ContentType), date, canonicalHeaders, }, "\n") diff --git a/cmd/signature-v4-utils.go b/cmd/signature-v4-utils.go index 2f89b6ce1..01b12dff2 100644 --- a/cmd/signature-v4-utils.go +++ b/cmd/signature-v4-utils.go @@ -26,6 +26,7 @@ import ( "strconv" "strings" + xhttp "github.com/minio/minio/cmd/http" "github.com/minio/minio/cmd/logger" "github.com/minio/minio/pkg/auth" "github.com/minio/sha256-simd" @@ -44,12 +45,12 @@ func skipContentSha256Cksum(r *http.Request) bool { ) if isRequestPresignedSignatureV4(r) { - v, ok = r.URL.Query()["X-Amz-Content-Sha256"] + v, ok = r.URL.Query()[xhttp.AmzContentSha256] if !ok { - v, ok = r.Header["X-Amz-Content-Sha256"] + v, ok = r.Header[xhttp.AmzContentSha256] } } else { - v, ok = r.Header["X-Amz-Content-Sha256"] + v, ok = r.Header[xhttp.AmzContentSha256] } // If x-amz-content-sha256 is set and the value is not @@ -81,15 +82,15 @@ 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()["X-Amz-Content-Sha256"] + v, ok = r.URL.Query()[xhttp.AmzContentSha256] if !ok { - v, ok = r.Header["X-Amz-Content-Sha256"] + v, ok = r.Header[xhttp.AmzContentSha256] } } else { // X-Amz-Content-Sha256, if not set in signed requests, checksum // will default to sha256([]byte("")). defaultSha256Cksum = emptySHA256 - v, ok = r.Header["X-Amz-Content-Sha256"] + v, ok = r.Header[xhttp.AmzContentSha256] } // We found 'X-Amz-Content-Sha256' return the captured value. diff --git a/cmd/signature-v4.go b/cmd/signature-v4.go index c594f7cfa..06689517e 100644 --- a/cmd/signature-v4.go +++ b/cmd/signature-v4.go @@ -36,6 +36,7 @@ import ( "time" "github.com/minio/minio-go/v6/pkg/s3utils" + xhttp "github.com/minio/minio/cmd/http" sha256 "github.com/minio/sha256-simd" ) @@ -172,7 +173,7 @@ func doesPolicySignatureV4Match(formValues http.Header) APIErrorCode { region := globalServerConfig.GetRegion() // Parse credential tag. - credHeader, err := parseCredentialHeader("Credential="+formValues.Get("X-Amz-Credential"), region, serviceS3) + credHeader, err := parseCredentialHeader("Credential="+formValues.Get(xhttp.AmzCredential), region, serviceS3) if err != ErrNone { return ErrMissingFields } @@ -189,7 +190,7 @@ func doesPolicySignatureV4Match(formValues http.Header) APIErrorCode { newSignature := getSignature(signingKey, formValues.Get("Policy")) // Verify signature. - if !compareSignatureV4(newSignature, formValues.Get("X-Amz-Signature")) { + if !compareSignatureV4(newSignature, formValues.Get(xhttp.AmzSignature)) { return ErrSignatureDoesNotMatch } @@ -223,11 +224,11 @@ func doesPresignedSignatureMatch(hashedPayload string, r *http.Request, region s // Construct new query. query := make(url.Values) - if req.URL.Query().Get("X-Amz-Content-Sha256") != "" { - query.Set("X-Amz-Content-Sha256", hashedPayload) + if req.URL.Query().Get(xhttp.AmzContentSha256) != "" { + query.Set(xhttp.AmzContentSha256, hashedPayload) } - query.Set("X-Amz-Algorithm", signV4Algorithm) + query.Set(xhttp.AmzAlgorithm, signV4Algorithm) // If the host which signed the request is slightly ahead in time (by less than globalMaxSkewTime) the // request should still be allowed. @@ -244,10 +245,10 @@ func doesPresignedSignatureMatch(hashedPayload string, r *http.Request, region s expireSeconds := int(pSignValues.Expires / time.Second) // Construct the query. - query.Set("X-Amz-Date", t.Format(iso8601Format)) - query.Set("X-Amz-Expires", strconv.Itoa(expireSeconds)) - query.Set("X-Amz-SignedHeaders", getSignedHeaders(extractedSignedHeaders)) - query.Set("X-Amz-Credential", cred.AccessKey+"/"+pSignValues.Credential.getScope()) + query.Set(xhttp.AmzDate, t.Format(iso8601Format)) + query.Set(xhttp.AmzExpires, strconv.Itoa(expireSeconds)) + query.Set(xhttp.AmzSignedHeaders, getSignedHeaders(extractedSignedHeaders)) + query.Set(xhttp.AmzCredential, cred.AccessKey+"/"+pSignValues.Credential.getScope()) // Save other headers available in the request parameters. for k, v := range req.URL.Query() { @@ -273,24 +274,24 @@ func doesPresignedSignatureMatch(hashedPayload string, r *http.Request, region s encodedQuery := query.Encode() // Verify if date query is same. - if req.URL.Query().Get("X-Amz-Date") != query.Get("X-Amz-Date") { + if req.URL.Query().Get(xhttp.AmzDate) != query.Get(xhttp.AmzDate) { return ErrSignatureDoesNotMatch } // Verify if expires query is same. - if req.URL.Query().Get("X-Amz-Expires") != query.Get("X-Amz-Expires") { + if req.URL.Query().Get(xhttp.AmzExpires) != query.Get(xhttp.AmzExpires) { return ErrSignatureDoesNotMatch } // Verify if signed headers query is same. - if req.URL.Query().Get("X-Amz-SignedHeaders") != query.Get("X-Amz-SignedHeaders") { + if req.URL.Query().Get(xhttp.AmzSignedHeaders) != query.Get(xhttp.AmzSignedHeaders) { return ErrSignatureDoesNotMatch } // Verify if credential query is same. - if req.URL.Query().Get("X-Amz-Credential") != query.Get("X-Amz-Credential") { + if req.URL.Query().Get(xhttp.AmzCredential) != query.Get(xhttp.AmzCredential) { return ErrSignatureDoesNotMatch } // Verify if sha256 payload query is same. - if req.URL.Query().Get("X-Amz-Content-Sha256") != "" { - if req.URL.Query().Get("X-Amz-Content-Sha256") != query.Get("X-Amz-Content-Sha256") { + if req.URL.Query().Get(xhttp.AmzContentSha256) != "" { + if req.URL.Query().Get(xhttp.AmzContentSha256) != query.Get(xhttp.AmzContentSha256) { return ErrContentSHA256Mismatch } } @@ -311,7 +312,7 @@ func doesPresignedSignatureMatch(hashedPayload string, r *http.Request, region s newSignature := getSignature(presignedSigningKey, presignedStringToSign) // Verify signature. - if !compareSignatureV4(req.URL.Query().Get("X-Amz-Signature"), newSignature) { + if !compareSignatureV4(req.URL.Query().Get(xhttp.AmzSignature), newSignature) { return ErrSignatureDoesNotMatch } return ErrNone @@ -325,7 +326,7 @@ func doesSignatureMatch(hashedPayload string, r *http.Request, region string, st req := *r // Save authorization header. - v4Auth := req.Header.Get("Authorization") + v4Auth := req.Header.Get(xhttp.Authorization) // Parse signature version '4' header. signV4Values, err := parseSignV4(v4Auth, region, stype) @@ -346,8 +347,8 @@ func doesSignatureMatch(hashedPayload string, r *http.Request, region string, st // Extract date, if not present throw error. var date string - if date = req.Header.Get(http.CanonicalHeaderKey("x-amz-date")); date == "" { - if date = r.Header.Get("Date"); date == "" { + if date = req.Header.Get(xhttp.AmzDate); date == "" { + if date = r.Header.Get(xhttp.Date); date == "" { return ErrMissingDateHeader } } diff --git a/cmd/storage-rest-server.go b/cmd/storage-rest-server.go index 1ecc79987..2c61b050b 100644 --- a/cmd/storage-rest-server.go +++ b/cmd/storage-rest-server.go @@ -29,6 +29,7 @@ import ( "time" "github.com/gorilla/mux" + xhttp "github.com/minio/minio/cmd/http" "github.com/minio/minio/cmd/logger" ) @@ -114,7 +115,7 @@ func (s *storageRESTServer) GetInstanceID(w http.ResponseWriter, r *http.Request s.writeErrorResponse(w, err) return } - w.Header().Set("Content-Length", strconv.Itoa(len(s.instanceID))) + w.Header().Set(xhttp.ContentLength, strconv.Itoa(len(s.instanceID))) w.Write([]byte(s.instanceID)) w.(http.Flusher).Flush() } @@ -283,7 +284,7 @@ func (s *storageRESTServer) ReadAllHandler(w http.ResponseWriter, r *http.Reques s.writeErrorResponse(w, err) return } - w.Header().Set("Content-Length", strconv.Itoa(len(buf))) + w.Header().Set(xhttp.ContentLength, strconv.Itoa(len(buf))) w.Write(buf) w.(http.Flusher).Flush() } @@ -327,7 +328,7 @@ func (s *storageRESTServer) ReadFileHandler(w http.ResponseWriter, r *http.Reque s.writeErrorResponse(w, err) return } - w.Header().Set("Content-Length", strconv.Itoa(len(buf))) + w.Header().Set(xhttp.ContentLength, strconv.Itoa(len(buf))) w.Write(buf) w.(http.Flusher).Flush() } @@ -357,7 +358,7 @@ func (s *storageRESTServer) ReadFileStreamHandler(w http.ResponseWriter, r *http return } defer rc.Close() - w.Header().Set("Content-Length", strconv.Itoa(length)) + w.Header().Set(xhttp.ContentLength, strconv.Itoa(length)) io.Copy(w, rc) w.(http.Flusher).Flush() diff --git a/cmd/streaming-signature-v4.go b/cmd/streaming-signature-v4.go index 470b313b4..b3195cec2 100644 --- a/cmd/streaming-signature-v4.go +++ b/cmd/streaming-signature-v4.go @@ -29,6 +29,7 @@ import ( "time" humanize "github.com/dustin/go-humanize" + xhttp "github.com/minio/minio/cmd/http" "github.com/minio/minio/pkg/auth" sha256 "github.com/minio/sha256-simd" ) @@ -69,7 +70,7 @@ func calculateSeedSignature(r *http.Request) (cred auth.Credentials, signature s req := *r // Save authorization header. - v4Auth := req.Header.Get("Authorization") + v4Auth := req.Header.Get(xhttp.Authorization) // Parse signature version '4' header. signV4Values, errCode := parseSignV4(v4Auth, globalServerConfig.GetRegion(), serviceS3) @@ -81,7 +82,7 @@ func calculateSeedSignature(r *http.Request) (cred auth.Credentials, signature s payload := streamingContentSHA256 // Payload for STREAMING signature should be 'STREAMING-AWS4-HMAC-SHA256-PAYLOAD' - if payload != req.Header.Get("X-Amz-Content-Sha256") { + if payload != req.Header.Get(xhttp.AmzContentSha256) { return cred, "", "", time.Time{}, ErrContentSHA256Mismatch } diff --git a/cmd/sts-errors.go b/cmd/sts-errors.go index 7b2e5a8ac..1e605b26a 100644 --- a/cmd/sts-errors.go +++ b/cmd/sts-errors.go @@ -19,12 +19,14 @@ package cmd import ( "encoding/xml" "net/http" + + xhttp "github.com/minio/minio/cmd/http" ) // writeSTSErrorRespone writes error headers func writeSTSErrorResponse(w http.ResponseWriter, err STSError) { // Generate error response. - stsErrorResponse := getSTSErrorResponse(err, w.Header().Get(responseRequestIDKey)) + stsErrorResponse := getSTSErrorResponse(err, w.Header().Get(xhttp.AmzRequestID)) encodedErrorResponse := encodeResponse(stsErrorResponse) writeResponse(w, err.HTTPStatusCode, encodedErrorResponse, mimeXML) } diff --git a/cmd/sts-handlers.go b/cmd/sts-handlers.go index 49dbe95b3..f1522347e 100644 --- a/cmd/sts-handlers.go +++ b/cmd/sts-handlers.go @@ -24,6 +24,7 @@ import ( "net/http" "github.com/gorilla/mux" + xhttp "github.com/minio/minio/cmd/http" "github.com/minio/minio/cmd/logger" "github.com/minio/minio/pkg/auth" iampolicy "github.com/minio/minio/pkg/iam/policy" @@ -53,16 +54,16 @@ func registerSTSRouter(router *mux.Router) { stsRouter := router.NewRoute().PathPrefix("/").Subrouter() // Assume roles with no JWT, handles AssumeRole. - stsRouter.Methods("POST").MatcherFunc(func(r *http.Request, rm *mux.RouteMatch) bool { - ctypeOk := wildcard.MatchSimple("application/x-www-form-urlencoded*", r.Header.Get("Content-Type")) - authOk := wildcard.MatchSimple("AWS4-HMAC-SHA256*", r.Header.Get("Authorization")) + 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 return ctypeOk && authOk && noQueries }).HandlerFunc(httpTraceAll(sts.AssumeRole)) // Assume roles with JWT handler, handles both ClientGrants and WebIdentity. stsRouter.Methods("POST").MatcherFunc(func(r *http.Request, rm *mux.RouteMatch) bool { - ctypeOk := wildcard.MatchSimple("application/x-www-form-urlencoded*", r.Header.Get("Content-Type")) + ctypeOk := wildcard.MatchSimple("application/x-www-form-urlencoded*", r.Header.Get(xhttp.ContentType)) noQueries := len(r.URL.Query()) == 0 return ctypeOk && noQueries }).HandlerFunc(httpTraceAll(sts.AssumeRoleWithJWT)) @@ -227,7 +228,7 @@ func (sts *stsAPIHandlers) AssumeRole(w http.ResponseWriter, r *http.Request) { }, } - assumeRoleResponse.ResponseMetadata.RequestID = w.Header().Get(responseRequestIDKey) + assumeRoleResponse.ResponseMetadata.RequestID = w.Header().Get(xhttp.AmzRequestID) writeSuccessResponseXML(w, encodeResponse(assumeRoleResponse)) } @@ -369,7 +370,7 @@ func (sts *stsAPIHandlers) AssumeRoleWithJWT(w http.ResponseWriter, r *http.Requ SubjectFromToken: subFromToken, }, } - clientGrantsResponse.ResponseMetadata.RequestID = w.Header().Get(responseRequestIDKey) + clientGrantsResponse.ResponseMetadata.RequestID = w.Header().Get(xhttp.AmzRequestID) encodedSuccessResponse = encodeResponse(clientGrantsResponse) case webIdentity: webIdentityResponse := &AssumeRoleWithWebIdentityResponse{ @@ -378,7 +379,7 @@ func (sts *stsAPIHandlers) AssumeRoleWithJWT(w http.ResponseWriter, r *http.Requ SubjectFromWebIdentityToken: subFromToken, }, } - webIdentityResponse.ResponseMetadata.RequestID = w.Header().Get(responseRequestIDKey) + webIdentityResponse.ResponseMetadata.RequestID = w.Header().Get(xhttp.AmzRequestID) encodedSuccessResponse = encodeResponse(webIdentityResponse) } diff --git a/cmd/utils.go b/cmd/utils.go index 428af6292..11a9ff070 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -36,6 +36,7 @@ import ( "strings" "time" + xhttp "github.com/minio/minio/cmd/http" "github.com/minio/minio/cmd/logger" "github.com/minio/minio/pkg/handlers" @@ -426,7 +427,7 @@ func newContext(r *http.Request, w http.ResponseWriter, api string) context.Cont } reqInfo := &logger.ReqInfo{ DeploymentID: globalDeploymentID, - RequestID: w.Header().Get(responseRequestIDKey), + RequestID: w.Header().Get(xhttp.AmzRequestID), RemoteHost: handlers.GetSourceIP(r), UserAgent: r.UserAgent(), API: api, diff --git a/cmd/web-handlers.go b/cmd/web-handlers.go index aa6382501..afd589403 100644 --- a/cmd/web-handlers.go +++ b/cmd/web-handlers.go @@ -41,6 +41,7 @@ import ( "github.com/minio/minio-go/v6/pkg/set" "github.com/minio/minio/browser" "github.com/minio/minio/cmd/crypto" + xhttp "github.com/minio/minio/cmd/http" "github.com/minio/minio/cmd/logger" "github.com/minio/minio/pkg/auth" "github.com/minio/minio/pkg/dns" @@ -1217,7 +1218,7 @@ func (web *webAPIHandlers) Download(w http.ResponseWriter, r *http.Request) { } // Add content disposition. - w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", path.Base(objInfo.Name))) + w.Header().Set(xhttp.ContentDisposition, fmt.Sprintf("attachment; filename=\"%s\"", path.Base(objInfo.Name))) setHeadGetRespHeaders(w, r.URL.Query()) @@ -1879,11 +1880,11 @@ func presignedGet(host, bucket, object string, expiry int64, creds auth.Credenti } query := url.Values{} - query.Set("X-Amz-Algorithm", signV4Algorithm) - query.Set("X-Amz-Credential", credential) - query.Set("X-Amz-Date", dateStr) - query.Set("X-Amz-Expires", expiryStr) - query.Set("X-Amz-SignedHeaders", "host") + query.Set(xhttp.AmzAlgorithm, signV4Algorithm) + query.Set(xhttp.AmzCredential, credential) + query.Set(xhttp.AmzDate, dateStr) + query.Set(xhttp.AmzExpires, expiryStr) + query.Set(xhttp.AmzSignedHeaders, "host") queryStr := s3utils.QueryEncode(query) path := "/" + path.Join(bucket, object) @@ -1897,7 +1898,7 @@ func presignedGet(host, bucket, object string, expiry int64, creds auth.Credenti signature := getSignature(signingKey, stringToSign) // Construct the final presigned URL. - return host + s3utils.EncodePath(path) + "?" + queryStr + "&" + "X-Amz-Signature=" + signature + return host + s3utils.EncodePath(path) + "?" + queryStr + "&" + xhttp.AmzSignature + "=" + signature } // toJSONError converts regular errors into more user friendly