1package v4 2 3import ( 4 "bytes" 5 "io" 6 "io/ioutil" 7 "net/http" 8 "net/http/httptest" 9 "reflect" 10 "strconv" 11 "strings" 12 "testing" 13 "time" 14 15 "github.com/aws/aws-sdk-go/aws" 16 "github.com/aws/aws-sdk-go/aws/credentials" 17 "github.com/aws/aws-sdk-go/aws/request" 18 "github.com/aws/aws-sdk-go/awstesting" 19) 20 21func epochTime() time.Time { return time.Unix(0, 0) } 22 23func TestStripExcessHeaders(t *testing.T) { 24 vals := []string{ 25 "", 26 "123", 27 "1 2 3", 28 "1 2 3 ", 29 " 1 2 3", 30 "1 2 3", 31 "1 23", 32 "1 2 3", 33 "1 2 ", 34 " 1 2 ", 35 "12 3", 36 "12 3 1", 37 "12 3 1", 38 "12 3 1abc123", 39 } 40 41 expected := []string{ 42 "", 43 "123", 44 "1 2 3", 45 "1 2 3", 46 "1 2 3", 47 "1 2 3", 48 "1 23", 49 "1 2 3", 50 "1 2", 51 "1 2", 52 "12 3", 53 "12 3 1", 54 "12 3 1", 55 "12 3 1abc123", 56 } 57 58 stripExcessSpaces(vals) 59 for i := 0; i < len(vals); i++ { 60 if e, a := expected[i], vals[i]; e != a { 61 t.Errorf("%d, expect %v, got %v", i, e, a) 62 } 63 } 64} 65 66func buildRequest(serviceName, region, body string) (*http.Request, io.ReadSeeker) { 67 reader := strings.NewReader(body) 68 return buildRequestWithBodyReader(serviceName, region, reader) 69} 70 71func buildRequestReaderSeeker(serviceName, region, body string) (*http.Request, io.ReadSeeker) { 72 reader := &readerSeekerWrapper{strings.NewReader(body)} 73 return buildRequestWithBodyReader(serviceName, region, reader) 74} 75 76func buildRequestWithBodyReader(serviceName, region string, body io.Reader) (*http.Request, io.ReadSeeker) { 77 var bodyLen int 78 79 type lenner interface { 80 Len() int 81 } 82 if lr, ok := body.(lenner); ok { 83 bodyLen = lr.Len() 84 } 85 86 endpoint := "https://" + serviceName + "." + region + ".amazonaws.com" 87 req, _ := http.NewRequest("POST", endpoint, body) 88 req.URL.Opaque = "//example.org/bucket/key-._~,!@#$%^&*()" 89 req.Header.Set("X-Amz-Target", "prefix.Operation") 90 req.Header.Set("Content-Type", "application/x-amz-json-1.0") 91 92 if bodyLen > 0 { 93 req.Header.Set("Content-Length", strconv.Itoa(bodyLen)) 94 } 95 96 req.Header.Set("X-Amz-Meta-Other-Header", "some-value=!@#$%^&* (+)") 97 req.Header.Add("X-Amz-Meta-Other-Header_With_Underscore", "some-value=!@#$%^&* (+)") 98 req.Header.Add("X-amz-Meta-Other-Header_With_Underscore", "some-value=!@#$%^&* (+)") 99 100 var seeker io.ReadSeeker 101 if sr, ok := body.(io.ReadSeeker); ok { 102 seeker = sr 103 } else { 104 seeker = aws.ReadSeekCloser(body) 105 } 106 107 return req, seeker 108} 109 110func buildSigner() Signer { 111 return Signer{ 112 Credentials: credentials.NewStaticCredentials("AKID", "SECRET", "SESSION"), 113 } 114} 115 116func removeWS(text string) string { 117 text = strings.Replace(text, " ", "", -1) 118 text = strings.Replace(text, "\n", "", -1) 119 text = strings.Replace(text, "\t", "", -1) 120 return text 121} 122 123func assertEqual(t *testing.T, expected, given string) { 124 if removeWS(expected) != removeWS(given) { 125 t.Errorf("\nExpected: %s\nGiven: %s", expected, given) 126 } 127} 128 129func TestPresignRequest(t *testing.T) { 130 req, body := buildRequest("dynamodb", "us-east-1", "{}") 131 132 signer := buildSigner() 133 signer.Presign(req, body, "dynamodb", "us-east-1", 300*time.Second, epochTime()) 134 135 expectedDate := "19700101T000000Z" 136 expectedHeaders := "content-length;content-type;host;x-amz-meta-other-header;x-amz-meta-other-header_with_underscore" 137 expectedSig := "122f0b9e091e4ba84286097e2b3404a1f1f4c4aad479adda95b7dff0ccbe5581" 138 expectedCred := "AKID/19700101/us-east-1/dynamodb/aws4_request" 139 expectedTarget := "prefix.Operation" 140 141 q := req.URL.Query() 142 if e, a := expectedSig, q.Get("X-Amz-Signature"); e != a { 143 t.Errorf("expect %v, got %v", e, a) 144 } 145 if e, a := expectedCred, q.Get("X-Amz-Credential"); e != a { 146 t.Errorf("expect %v, got %v", e, a) 147 } 148 if e, a := expectedHeaders, q.Get("X-Amz-SignedHeaders"); e != a { 149 t.Errorf("expect %v, got %v", e, a) 150 } 151 if e, a := expectedDate, q.Get("X-Amz-Date"); e != a { 152 t.Errorf("expect %v, got %v", e, a) 153 } 154 if a := q.Get("X-Amz-Meta-Other-Header"); len(a) != 0 { 155 t.Errorf("expect %v to be empty", a) 156 } 157 if e, a := expectedTarget, q.Get("X-Amz-Target"); e != a { 158 t.Errorf("expect %v, got %v", e, a) 159 } 160} 161 162func TestPresignBodyWithArrayRequest(t *testing.T) { 163 req, body := buildRequest("dynamodb", "us-east-1", "{}") 164 req.URL.RawQuery = "Foo=z&Foo=o&Foo=m&Foo=a" 165 166 signer := buildSigner() 167 signer.Presign(req, body, "dynamodb", "us-east-1", 300*time.Second, epochTime()) 168 169 expectedDate := "19700101T000000Z" 170 expectedHeaders := "content-length;content-type;host;x-amz-meta-other-header;x-amz-meta-other-header_with_underscore" 171 expectedSig := "e3ac55addee8711b76c6d608d762cff285fe8b627a057f8b5ec9268cf82c08b1" 172 expectedCred := "AKID/19700101/us-east-1/dynamodb/aws4_request" 173 expectedTarget := "prefix.Operation" 174 175 q := req.URL.Query() 176 if e, a := expectedSig, q.Get("X-Amz-Signature"); e != a { 177 t.Errorf("expect %v, got %v", e, a) 178 } 179 if e, a := expectedCred, q.Get("X-Amz-Credential"); e != a { 180 t.Errorf("expect %v, got %v", e, a) 181 } 182 if e, a := expectedHeaders, q.Get("X-Amz-SignedHeaders"); e != a { 183 t.Errorf("expect %v, got %v", e, a) 184 } 185 if e, a := expectedDate, q.Get("X-Amz-Date"); e != a { 186 t.Errorf("expect %v, got %v", e, a) 187 } 188 if a := q.Get("X-Amz-Meta-Other-Header"); len(a) != 0 { 189 t.Errorf("expect %v to be empty, was not", a) 190 } 191 if e, a := expectedTarget, q.Get("X-Amz-Target"); e != a { 192 t.Errorf("expect %v, got %v", e, a) 193 } 194} 195 196func TestSignRequest(t *testing.T) { 197 req, body := buildRequest("dynamodb", "us-east-1", "{}") 198 signer := buildSigner() 199 signer.Sign(req, body, "dynamodb", "us-east-1", epochTime()) 200 201 expectedDate := "19700101T000000Z" 202 expectedSig := "AWS4-HMAC-SHA256 Credential=AKID/19700101/us-east-1/dynamodb/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date;x-amz-meta-other-header;x-amz-meta-other-header_with_underscore;x-amz-security-token;x-amz-target, Signature=a518299330494908a70222cec6899f6f32f297f8595f6df1776d998936652ad9" 203 204 q := req.Header 205 if e, a := expectedSig, q.Get("Authorization"); e != a { 206 t.Errorf("expect\n%v\nactual\n%v\n", e, a) 207 } 208 if e, a := expectedDate, q.Get("X-Amz-Date"); e != a { 209 t.Errorf("expect\n%v\nactual\n%v\n", e, a) 210 } 211} 212 213func TestSignBodyS3(t *testing.T) { 214 req, body := buildRequest("s3", "us-east-1", "hello") 215 signer := buildSigner() 216 signer.Sign(req, body, "s3", "us-east-1", time.Now()) 217 hash := req.Header.Get("X-Amz-Content-Sha256") 218 if e, a := "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824", hash; e != a { 219 t.Errorf("expect %v, got %v", e, a) 220 } 221} 222 223func TestSignBodyGlacier(t *testing.T) { 224 req, body := buildRequest("glacier", "us-east-1", "hello") 225 signer := buildSigner() 226 signer.Sign(req, body, "glacier", "us-east-1", time.Now()) 227 hash := req.Header.Get("X-Amz-Content-Sha256") 228 if e, a := "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824", hash; e != a { 229 t.Errorf("expect %v, got %v", e, a) 230 } 231} 232 233func TestPresign_SignedPayload(t *testing.T) { 234 req, body := buildRequest("glacier", "us-east-1", "hello") 235 signer := buildSigner() 236 signer.Presign(req, body, "glacier", "us-east-1", 5*time.Minute, time.Now()) 237 hash := req.Header.Get("X-Amz-Content-Sha256") 238 if e, a := "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824", hash; e != a { 239 t.Errorf("expect %v, got %v", e, a) 240 } 241} 242 243func TestPresign_UnsignedPayload(t *testing.T) { 244 req, body := buildRequest("service-name", "us-east-1", "hello") 245 signer := buildSigner() 246 signer.UnsignedPayload = true 247 signer.Presign(req, body, "service-name", "us-east-1", 5*time.Minute, time.Now()) 248 hash := req.Header.Get("X-Amz-Content-Sha256") 249 if e, a := "UNSIGNED-PAYLOAD", hash; e != a { 250 t.Errorf("expect %v, got %v", e, a) 251 } 252} 253 254func TestPresign_UnsignedPayload_S3(t *testing.T) { 255 req, body := buildRequest("s3", "us-east-1", "hello") 256 signer := buildSigner() 257 signer.Presign(req, body, "s3", "us-east-1", 5*time.Minute, time.Now()) 258 if a := req.Header.Get("X-Amz-Content-Sha256"); len(a) != 0 { 259 t.Errorf("expect no content sha256 got %v", a) 260 } 261} 262 263func TestSignUnseekableBody(t *testing.T) { 264 req, body := buildRequestWithBodyReader("mock-service", "mock-region", bytes.NewBuffer([]byte("hello"))) 265 signer := buildSigner() 266 _, err := signer.Sign(req, body, "mock-service", "mock-region", time.Now()) 267 if err == nil { 268 t.Fatalf("expect error signing request") 269 } 270 271 if e, a := "unseekable request body", err.Error(); !strings.Contains(a, e) { 272 t.Errorf("expect %q to be in %q", e, a) 273 } 274} 275 276func TestSignUnsignedPayloadUnseekableBody(t *testing.T) { 277 req, body := buildRequestWithBodyReader("mock-service", "mock-region", bytes.NewBuffer([]byte("hello"))) 278 279 signer := buildSigner() 280 signer.UnsignedPayload = true 281 282 _, err := signer.Sign(req, body, "mock-service", "mock-region", time.Now()) 283 if err != nil { 284 t.Fatalf("expect no error, got %v", err) 285 } 286 287 hash := req.Header.Get("X-Amz-Content-Sha256") 288 if e, a := "UNSIGNED-PAYLOAD", hash; e != a { 289 t.Errorf("expect %v, got %v", e, a) 290 } 291} 292 293func TestSignPreComputedHashUnseekableBody(t *testing.T) { 294 req, body := buildRequestWithBodyReader("mock-service", "mock-region", bytes.NewBuffer([]byte("hello"))) 295 296 signer := buildSigner() 297 298 req.Header.Set("X-Amz-Content-Sha256", "some-content-sha256") 299 _, err := signer.Sign(req, body, "mock-service", "mock-region", time.Now()) 300 if err != nil { 301 t.Fatalf("expect no error, got %v", err) 302 } 303 304 hash := req.Header.Get("X-Amz-Content-Sha256") 305 if e, a := "some-content-sha256", hash; e != a { 306 t.Errorf("expect %v, got %v", e, a) 307 } 308} 309 310func TestSignPrecomputedBodyChecksum(t *testing.T) { 311 req, body := buildRequest("dynamodb", "us-east-1", "hello") 312 req.Header.Set("X-Amz-Content-Sha256", "PRECOMPUTED") 313 signer := buildSigner() 314 signer.Sign(req, body, "dynamodb", "us-east-1", time.Now()) 315 hash := req.Header.Get("X-Amz-Content-Sha256") 316 if e, a := "PRECOMPUTED", hash; e != a { 317 t.Errorf("expect %v, got %v", e, a) 318 } 319} 320 321func TestAnonymousCredentials(t *testing.T) { 322 svc := awstesting.NewClient(&aws.Config{Credentials: credentials.AnonymousCredentials}) 323 r := svc.NewRequest( 324 &request.Operation{ 325 Name: "BatchGetItem", 326 HTTPMethod: "POST", 327 HTTPPath: "/", 328 }, 329 nil, 330 nil, 331 ) 332 SignSDKRequest(r) 333 334 urlQ := r.HTTPRequest.URL.Query() 335 if a := urlQ.Get("X-Amz-Signature"); len(a) != 0 { 336 t.Errorf("expect %v to be empty, was not", a) 337 } 338 if a := urlQ.Get("X-Amz-Credential"); len(a) != 0 { 339 t.Errorf("expect %v to be empty, was not", a) 340 } 341 if a := urlQ.Get("X-Amz-SignedHeaders"); len(a) != 0 { 342 t.Errorf("expect %v to be empty, was not", a) 343 } 344 if a := urlQ.Get("X-Amz-Date"); len(a) != 0 { 345 t.Errorf("expect %v to be empty, was not", a) 346 } 347 348 hQ := r.HTTPRequest.Header 349 if a := hQ.Get("Authorization"); len(a) != 0 { 350 t.Errorf("expect %v to be empty, was not", a) 351 } 352 if a := hQ.Get("X-Amz-Date"); len(a) != 0 { 353 t.Errorf("expect %v to be empty, was not", a) 354 } 355} 356 357func TestIgnoreResignRequestWithValidCreds(t *testing.T) { 358 svc := awstesting.NewClient(&aws.Config{ 359 Credentials: credentials.NewStaticCredentials("AKID", "SECRET", "SESSION"), 360 Region: aws.String("us-west-2"), 361 }) 362 r := svc.NewRequest( 363 &request.Operation{ 364 Name: "BatchGetItem", 365 HTTPMethod: "POST", 366 HTTPPath: "/", 367 }, 368 nil, 369 nil, 370 ) 371 372 SignSDKRequest(r) 373 sig := r.HTTPRequest.Header.Get("Authorization") 374 375 SignSDKRequestWithCurrentTime(r, func() time.Time { 376 // Simulate one second has passed so that signature's date changes 377 // when it is resigned. 378 return time.Now().Add(1 * time.Second) 379 }) 380 if e, a := sig, r.HTTPRequest.Header.Get("Authorization"); e == a { 381 t.Errorf("expect %v to be %v, but was not", e, a) 382 } 383} 384 385func TestIgnorePreResignRequestWithValidCreds(t *testing.T) { 386 svc := awstesting.NewClient(&aws.Config{ 387 Credentials: credentials.NewStaticCredentials("AKID", "SECRET", "SESSION"), 388 Region: aws.String("us-west-2"), 389 }) 390 r := svc.NewRequest( 391 &request.Operation{ 392 Name: "BatchGetItem", 393 HTTPMethod: "POST", 394 HTTPPath: "/", 395 }, 396 nil, 397 nil, 398 ) 399 r.ExpireTime = time.Minute * 10 400 401 SignSDKRequest(r) 402 sig := r.HTTPRequest.URL.Query().Get("X-Amz-Signature") 403 404 SignSDKRequestWithCurrentTime(r, func() time.Time { 405 // Simulate one second has passed so that signature's date changes 406 // when it is resigned. 407 return time.Now().Add(1 * time.Second) 408 }) 409 if e, a := sig, r.HTTPRequest.URL.Query().Get("X-Amz-Signature"); e == a { 410 t.Errorf("expect %v to be %v, but was not", e, a) 411 } 412} 413 414func TestResignRequestExpiredCreds(t *testing.T) { 415 creds := credentials.NewStaticCredentials("AKID", "SECRET", "SESSION") 416 svc := awstesting.NewClient(&aws.Config{Credentials: creds}) 417 r := svc.NewRequest( 418 &request.Operation{ 419 Name: "BatchGetItem", 420 HTTPMethod: "POST", 421 HTTPPath: "/", 422 }, 423 nil, 424 nil, 425 ) 426 SignSDKRequest(r) 427 querySig := r.HTTPRequest.Header.Get("Authorization") 428 var origSignedHeaders string 429 for _, p := range strings.Split(querySig, ", ") { 430 if strings.HasPrefix(p, "SignedHeaders=") { 431 origSignedHeaders = p[len("SignedHeaders="):] 432 break 433 } 434 } 435 if a := origSignedHeaders; len(a) == 0 { 436 t.Errorf("expect not to be empty, but was") 437 } 438 if e, a := origSignedHeaders, "authorization"; strings.Contains(a, e) { 439 t.Errorf("expect %v to not be in %v, but was", e, a) 440 } 441 origSignedAt := r.LastSignedAt 442 443 creds.Expire() 444 445 SignSDKRequestWithCurrentTime(r, func() time.Time { 446 // Simulate one second has passed so that signature's date changes 447 // when it is resigned. 448 return time.Now().Add(1 * time.Second) 449 }) 450 updatedQuerySig := r.HTTPRequest.Header.Get("Authorization") 451 if e, a := querySig, updatedQuerySig; e == a { 452 t.Errorf("expect %v to be %v, was not", e, a) 453 } 454 455 var updatedSignedHeaders string 456 for _, p := range strings.Split(updatedQuerySig, ", ") { 457 if strings.HasPrefix(p, "SignedHeaders=") { 458 updatedSignedHeaders = p[len("SignedHeaders="):] 459 break 460 } 461 } 462 if a := updatedSignedHeaders; len(a) == 0 { 463 t.Errorf("expect not to be empty, but was") 464 } 465 if e, a := updatedQuerySig, "authorization"; strings.Contains(a, e) { 466 t.Errorf("expect %v to not be in %v, but was", e, a) 467 } 468 if e, a := origSignedAt, r.LastSignedAt; e == a { 469 t.Errorf("expect %v to be %v, was not", e, a) 470 } 471} 472 473func TestPreResignRequestExpiredCreds(t *testing.T) { 474 provider := &credentials.StaticProvider{Value: credentials.Value{ 475 AccessKeyID: "AKID", 476 SecretAccessKey: "SECRET", 477 SessionToken: "SESSION", 478 }} 479 creds := credentials.NewCredentials(provider) 480 svc := awstesting.NewClient(&aws.Config{Credentials: creds}) 481 r := svc.NewRequest( 482 &request.Operation{ 483 Name: "BatchGetItem", 484 HTTPMethod: "POST", 485 HTTPPath: "/", 486 }, 487 nil, 488 nil, 489 ) 490 r.ExpireTime = time.Minute * 10 491 492 SignSDKRequest(r) 493 querySig := r.HTTPRequest.URL.Query().Get("X-Amz-Signature") 494 signedHeaders := r.HTTPRequest.URL.Query().Get("X-Amz-SignedHeaders") 495 if a := signedHeaders; len(a) == 0 { 496 t.Errorf("expect not to be empty, but was") 497 } 498 origSignedAt := r.LastSignedAt 499 500 creds.Expire() 501 502 SignSDKRequestWithCurrentTime(r, func() time.Time { 503 // Simulate the request occurred 15 minutes in the past 504 return time.Now().Add(-48 * time.Hour) 505 }) 506 if e, a := querySig, r.HTTPRequest.URL.Query().Get("X-Amz-Signature"); e == a { 507 t.Errorf("expect %v to be %v, was not", e, a) 508 } 509 resignedHeaders := r.HTTPRequest.URL.Query().Get("X-Amz-SignedHeaders") 510 if e, a := signedHeaders, resignedHeaders; e != a { 511 t.Errorf("expect %v, got %v", e, a) 512 } 513 if e, a := signedHeaders, "x-amz-signedHeaders"; strings.Contains(a, e) { 514 t.Errorf("expect %v to not be in %v, but was", e, a) 515 } 516 if e, a := origSignedAt, r.LastSignedAt; e == a { 517 t.Errorf("expect %v to be %v, was not", e, a) 518 } 519} 520 521func TestResignRequestExpiredRequest(t *testing.T) { 522 creds := credentials.NewStaticCredentials("AKID", "SECRET", "SESSION") 523 svc := awstesting.NewClient(&aws.Config{Credentials: creds}) 524 r := svc.NewRequest( 525 &request.Operation{ 526 Name: "BatchGetItem", 527 HTTPMethod: "POST", 528 HTTPPath: "/", 529 }, 530 nil, 531 nil, 532 ) 533 534 SignSDKRequest(r) 535 querySig := r.HTTPRequest.Header.Get("Authorization") 536 origSignedAt := r.LastSignedAt 537 538 SignSDKRequestWithCurrentTime(r, func() time.Time { 539 // Simulate the request occurred 15 minutes in the past 540 return time.Now().Add(15 * time.Minute) 541 }) 542 if e, a := querySig, r.HTTPRequest.Header.Get("Authorization"); e == a { 543 t.Errorf("expect %v to be %v, was not", e, a) 544 } 545 if e, a := origSignedAt, r.LastSignedAt; e == a { 546 t.Errorf("expect %v to be %v, was not", e, a) 547 } 548} 549 550func TestSignWithRequestBody(t *testing.T) { 551 creds := credentials.NewStaticCredentials("AKID", "SECRET", "SESSION") 552 signer := NewSigner(creds) 553 554 expectBody := []byte("abc123") 555 556 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 557 b, err := ioutil.ReadAll(r.Body) 558 r.Body.Close() 559 if err != nil { 560 t.Errorf("expect no error, got %v", err) 561 } 562 if e, a := expectBody, b; !reflect.DeepEqual(e, a) { 563 t.Errorf("expect %v, got %v", e, a) 564 } 565 w.WriteHeader(http.StatusOK) 566 })) 567 defer server.Close() 568 569 req, err := http.NewRequest("POST", server.URL, nil) 570 if err != nil { 571 t.Errorf("expect not no error, got %v", err) 572 } 573 574 _, err = signer.Sign(req, bytes.NewReader(expectBody), "service", "region", time.Now()) 575 if err != nil { 576 t.Errorf("expect not no error, got %v", err) 577 } 578 579 resp, err := http.DefaultClient.Do(req) 580 if err != nil { 581 t.Errorf("expect not no error, got %v", err) 582 } 583 if e, a := http.StatusOK, resp.StatusCode; e != a { 584 t.Errorf("expect %v, got %v", e, a) 585 } 586} 587 588func TestSignWithRequestBody_Overwrite(t *testing.T) { 589 creds := credentials.NewStaticCredentials("AKID", "SECRET", "SESSION") 590 signer := NewSigner(creds) 591 592 var expectBody []byte 593 594 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 595 b, err := ioutil.ReadAll(r.Body) 596 r.Body.Close() 597 if err != nil { 598 t.Errorf("expect not no error, got %v", err) 599 } 600 if e, a := len(expectBody), len(b); e != a { 601 t.Errorf("expect %v, got %v", e, a) 602 } 603 w.WriteHeader(http.StatusOK) 604 })) 605 defer server.Close() 606 607 req, err := http.NewRequest("GET", server.URL, strings.NewReader("invalid body")) 608 if err != nil { 609 t.Errorf("expect not no error, got %v", err) 610 } 611 612 _, err = signer.Sign(req, nil, "service", "region", time.Now()) 613 req.ContentLength = 0 614 615 if err != nil { 616 t.Errorf("expect not no error, got %v", err) 617 } 618 619 resp, err := http.DefaultClient.Do(req) 620 if err != nil { 621 t.Errorf("expect not no error, got %v", err) 622 } 623 if e, a := http.StatusOK, resp.StatusCode; e != a { 624 t.Errorf("expect %v, got %v", e, a) 625 } 626} 627 628func TestBuildCanonicalRequest(t *testing.T) { 629 req, body := buildRequest("dynamodb", "us-east-1", "{}") 630 req.URL.RawQuery = "Foo=z&Foo=o&Foo=m&Foo=a" 631 ctx := &signingCtx{ 632 ServiceName: "dynamodb", 633 Region: "us-east-1", 634 Request: req, 635 Body: body, 636 Query: req.URL.Query(), 637 Time: time.Now(), 638 ExpireTime: 5 * time.Second, 639 } 640 641 ctx.buildCanonicalString() 642 expected := "https://example.org/bucket/key-._~,!@#$%^&*()?Foo=z&Foo=o&Foo=m&Foo=a" 643 if e, a := expected, ctx.Request.URL.String(); e != a { 644 t.Errorf("expect %v, got %v", e, a) 645 } 646} 647 648func TestSignWithBody_ReplaceRequestBody(t *testing.T) { 649 creds := credentials.NewStaticCredentials("AKID", "SECRET", "SESSION") 650 req, seekerBody := buildRequest("dynamodb", "us-east-1", "{}") 651 req.Body = ioutil.NopCloser(bytes.NewReader([]byte{})) 652 653 s := NewSigner(creds) 654 origBody := req.Body 655 656 _, err := s.Sign(req, seekerBody, "dynamodb", "us-east-1", time.Now()) 657 if err != nil { 658 t.Fatalf("expect no error, got %v", err) 659 } 660 661 if req.Body == origBody { 662 t.Errorf("expeect request body to not be origBody") 663 } 664 665 if req.Body == nil { 666 t.Errorf("expect request body to be changed but was nil") 667 } 668} 669 670func TestSignWithBody_NoReplaceRequestBody(t *testing.T) { 671 creds := credentials.NewStaticCredentials("AKID", "SECRET", "SESSION") 672 req, seekerBody := buildRequest("dynamodb", "us-east-1", "{}") 673 req.Body = ioutil.NopCloser(bytes.NewReader([]byte{})) 674 675 s := NewSigner(creds, func(signer *Signer) { 676 signer.DisableRequestBodyOverwrite = true 677 }) 678 679 origBody := req.Body 680 681 _, err := s.Sign(req, seekerBody, "dynamodb", "us-east-1", time.Now()) 682 if err != nil { 683 t.Fatalf("expect no error, got %v", err) 684 } 685 686 if req.Body != origBody { 687 t.Errorf("expect request body to not be chagned") 688 } 689} 690 691func TestRequestHost(t *testing.T) { 692 req, body := buildRequest("dynamodb", "us-east-1", "{}") 693 req.URL.RawQuery = "Foo=z&Foo=o&Foo=m&Foo=a" 694 req.Host = "myhost" 695 ctx := &signingCtx{ 696 ServiceName: "dynamodb", 697 Region: "us-east-1", 698 Request: req, 699 Body: body, 700 Query: req.URL.Query(), 701 Time: time.Now(), 702 ExpireTime: 5 * time.Second, 703 } 704 705 ctx.buildCanonicalHeaders(ignoredHeaders, ctx.Request.Header) 706 if !strings.Contains(ctx.canonicalHeaders, "host:"+req.Host) { 707 t.Errorf("canonical host header invalid") 708 } 709} 710 711func BenchmarkPresignRequest(b *testing.B) { 712 signer := buildSigner() 713 req, body := buildRequestReaderSeeker("dynamodb", "us-east-1", "{}") 714 for i := 0; i < b.N; i++ { 715 signer.Presign(req, body, "dynamodb", "us-east-1", 300*time.Second, time.Now()) 716 } 717} 718 719func BenchmarkSignRequest(b *testing.B) { 720 signer := buildSigner() 721 req, body := buildRequestReaderSeeker("dynamodb", "us-east-1", "{}") 722 for i := 0; i < b.N; i++ { 723 signer.Sign(req, body, "dynamodb", "us-east-1", time.Now()) 724 } 725} 726 727var stripExcessSpaceCases = []string{ 728 `AWS4-HMAC-SHA256 Credential=AKIDFAKEIDFAKEID/20160628/us-west-2/s3/aws4_request, SignedHeaders=host;x-amz-date, Signature=1234567890abcdef1234567890abcdef1234567890abcdef`, 729 `123 321 123 321`, 730 ` 123 321 123 321 `, 731 ` 123 321 123 321 `, 732 "123", 733 "1 2 3", 734 " 1 2 3", 735 "1 2 3", 736 "1 23", 737 "1 2 3", 738 "1 2 ", 739 " 1 2 ", 740 "12 3", 741 "12 3 1", 742 "12 3 1", 743 "12 3 1abc123", 744} 745 746func BenchmarkStripExcessSpaces(b *testing.B) { 747 for i := 0; i < b.N; i++ { 748 // Make sure to start with a copy of the cases 749 cases := append([]string{}, stripExcessSpaceCases...) 750 stripExcessSpaces(cases) 751 } 752} 753 754// readerSeekerWrapper mimics the interface provided by request.offsetReader 755type readerSeekerWrapper struct { 756 r *strings.Reader 757} 758 759func (r *readerSeekerWrapper) Read(p []byte) (n int, err error) { 760 return r.r.Read(p) 761} 762 763func (r *readerSeekerWrapper) Seek(offset int64, whence int) (int64, error) { 764 return r.r.Seek(offset, whence) 765} 766 767func (r *readerSeekerWrapper) Len() int { 768 return r.r.Len() 769} 770