|
@@ -210,40 +210,68 @@ func calculateSignature(r *http.Request, body []byte, creds AWSCredentials, sigV
|
|
return finalSig, nil
|
|
return finalSig, nil
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+// Add this helper function to detect virtual-hosted-style requests
|
|
|
|
+func isVirtualHostedStyle(host string) bool {
|
|
|
|
+ // Virtual-hosted-style: bucketname.s3.domain.com
|
|
|
|
+ // Path-style: s3.domain.com
|
|
|
|
+ parts := strings.Split(host, ".")
|
|
|
|
+ // If host starts with a bucket name (has more than 2 parts before domain)
|
|
|
|
+ // and contains "s3", it's virtual-hosted-style
|
|
|
|
+ return len(parts) > 2 && strings.Contains(host, "s3")
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// Add this helper to extract bucket from host
|
|
|
|
+func extractBucketFromHost(host string) string {
|
|
|
|
+ // Extract bucket name from virtual-hosted-style host
|
|
|
|
+ // photosbucket.s3.alanyeung.co -> photosbucket
|
|
|
|
+ parts := strings.Split(host, ".")
|
|
|
|
+ if len(parts) > 0 {
|
|
|
|
+ return parts[0]
|
|
|
|
+ }
|
|
|
|
+ return ""
|
|
|
|
+}
|
|
|
|
+
|
|
func buildCanonicalRequest(r *http.Request, body []byte, signedHeaders []string) string {
|
|
func buildCanonicalRequest(r *http.Request, body []byte, signedHeaders []string) string {
|
|
// Method
|
|
// Method
|
|
method := r.Method
|
|
method := r.Method
|
|
|
|
|
|
- // Canonical URI - Use RequestURI which preserves URL encoding
|
|
|
|
- // Split RequestURI to get just the path (before the query string)
|
|
|
|
|
|
+ // Canonical URI - Handle both virtual-hosted and path-style
|
|
canonicalURI := r.RequestURI
|
|
canonicalURI := r.RequestURI
|
|
if idx := strings.Index(canonicalURI, "?"); idx != -1 {
|
|
if idx := strings.Index(canonicalURI, "?"); idx != -1 {
|
|
canonicalURI = canonicalURI[:idx]
|
|
canonicalURI = canonicalURI[:idx]
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ // NEW: Check if this is a virtual-hosted-style request
|
|
|
|
+ if isVirtualHostedStyle(r.Host) {
|
|
|
|
+ // For virtual-hosted-style, strip the bucket name from the URI
|
|
|
|
+ bucketName := extractBucketFromHost(r.Host)
|
|
|
|
+ if bucketName != "" {
|
|
|
|
+ // Remove /bucketname/ prefix from the URI
|
|
|
|
+ prefix := "/" + bucketName + "/"
|
|
|
|
+ if strings.HasPrefix(canonicalURI, prefix) {
|
|
|
|
+ canonicalURI = "/" + strings.TrimPrefix(canonicalURI, prefix)
|
|
|
|
+ } else if canonicalURI == "/"+bucketName {
|
|
|
|
+ canonicalURI = "/"
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
if canonicalURI == "" {
|
|
if canonicalURI == "" {
|
|
canonicalURI = "/"
|
|
canonicalURI = "/"
|
|
}
|
|
}
|
|
|
|
|
|
- // Canonical query string - MUST BE SORTED
|
|
|
|
|
|
+ // ... rest of the function remains the same
|
|
canonicalQueryString := buildCanonicalQueryString(r)
|
|
canonicalQueryString := buildCanonicalQueryString(r)
|
|
-
|
|
|
|
- // Canonical headers (already includes trailing newlines)
|
|
|
|
canonicalHeaders := buildCanonicalHeaders(r, signedHeaders)
|
|
canonicalHeaders := buildCanonicalHeaders(r, signedHeaders)
|
|
-
|
|
|
|
- // Signed headers
|
|
|
|
signedHeadersStr := strings.Join(signedHeaders, ";")
|
|
signedHeadersStr := strings.Join(signedHeaders, ";")
|
|
|
|
|
|
- // Payload hash - Check if client sent UNSIGNED-PAYLOAD
|
|
|
|
var payloadHash string
|
|
var payloadHash string
|
|
amzContentSha256 := r.Header.Get("X-Amz-Content-SHA256")
|
|
amzContentSha256 := r.Header.Get("X-Amz-Content-SHA256")
|
|
if amzContentSha256 == "UNSIGNED-PAYLOAD" {
|
|
if amzContentSha256 == "UNSIGNED-PAYLOAD" {
|
|
- // Use the literal string for streaming/multipart uploads
|
|
|
|
payloadHash = "UNSIGNED-PAYLOAD"
|
|
payloadHash = "UNSIGNED-PAYLOAD"
|
|
} else if amzContentSha256 != "" {
|
|
} else if amzContentSha256 != "" {
|
|
- // Use the hash provided by the client
|
|
|
|
payloadHash = amzContentSha256
|
|
payloadHash = amzContentSha256
|
|
} else {
|
|
} else {
|
|
- // Calculate hash from the actual body
|
|
|
|
payloadHash = sha256Hash(body)
|
|
payloadHash = sha256Hash(body)
|
|
}
|
|
}
|
|
|
|
|
|
@@ -258,6 +286,8 @@ func buildCanonicalRequest(r *http.Request, body []byte, signedHeaders []string)
|
|
|
|
|
|
log.Printf("=== Canonical Request ===")
|
|
log.Printf("=== Canonical Request ===")
|
|
log.Printf("Method: %s", method)
|
|
log.Printf("Method: %s", method)
|
|
|
|
+ log.Printf("Host: %s (Virtual-hosted: %v)", r.Host, isVirtualHostedStyle(r.Host))
|
|
|
|
+ log.Printf("Original RequestURI: %s", r.RequestURI)
|
|
log.Printf("Canonical URI: %s", canonicalURI)
|
|
log.Printf("Canonical URI: %s", canonicalURI)
|
|
log.Printf("Canonical Query String: %s", canonicalQueryString)
|
|
log.Printf("Canonical Query String: %s", canonicalQueryString)
|
|
log.Printf("Canonical Headers:\n%s", canonicalHeaders)
|
|
log.Printf("Canonical Headers:\n%s", canonicalHeaders)
|