in apis/s3/src/main/java/org/jclouds/s3/filters/Aws4SignerForChunkedUpload.java [85:229]
protected HttpRequest sign(HttpRequest request) throws HttpException {
checkNotNull(request, "request is not ready to sign");
checkNotNull(request.getEndpoint(), "request is not ready to sign, request.endpoint not present.");
Payload payload = request.getPayload();
// chunked upload required content-length.
Long contentLength = payload.getContentMetadata().getContentLength();
// check contentLength not null
checkNotNull(contentLength, "request is not ready to sign, payload contentLength not present.");
// get host from request endpoint.
String host = request.getEndpoint().getHost();
Date date = timestampProvider.get();
String timestamp = timestampFormat.format(date);
String datestamp = dateFormat.format(date);
String service = serviceAndRegion.service();
String region = serviceAndRegion.region(host);
String credentialScope = Joiner.on('/').join(datestamp, region, service, "aws4_request");
HttpRequest.Builder<?> requestBuilder = request.toBuilder() //
.removeHeader(AUTHORIZATION) // remove Authorization
.removeHeader(DATE) // remove Date
.removeHeader(CONTENT_LENGTH); // remove Content-Length
ImmutableMap.Builder<String, String> signedHeadersBuilder = ImmutableSortedMap.<String, String>naturalOrder();
// content-encoding
String contentEncoding = CONTENT_ENCODING_HEADER_AWS_CHUNKED;
String originalContentEncoding = payload.getContentMetadata().getContentEncoding();
if (originalContentEncoding != null) {
contentEncoding += "," + originalContentEncoding;
}
requestBuilder.replaceHeader(HttpHeaders.CONTENT_ENCODING, contentEncoding);
signedHeadersBuilder.put(HttpHeaders.CONTENT_ENCODING.toLowerCase(), contentEncoding);
// x-amz-decoded-content-length
requestBuilder.replaceHeader(AMZ_DECODED_CONTENT_LENGTH_HEADER, contentLength.toString());
signedHeadersBuilder.put(AMZ_DECODED_CONTENT_LENGTH_HEADER.toLowerCase(), contentLength.toString());
// how big is the overall request stream going to be once we add the signature
// 'headers' to each chunk?
long totalLength = calculateChunkedContentLength(contentLength, userDataBlockSize);
requestBuilder.replaceHeader(CONTENT_LENGTH, Long.toString(totalLength));
signedHeadersBuilder.put(CONTENT_LENGTH.toLowerCase(), Long.toString(totalLength));
// Content MD5
String contentMD5 = request.getFirstHeaderOrNull(CONTENT_MD5);
if (payload != null) {
HashCode md5 = payload.getContentMetadata().getContentMD5AsHashCode();
if (md5 != null) {
contentMD5 = BaseEncoding.base64().encode(md5.asBytes());
}
}
if (contentMD5 != null) {
requestBuilder.replaceHeader(CONTENT_MD5, contentMD5);
signedHeadersBuilder.put(CONTENT_MD5.toLowerCase(), contentMD5);
}
// Content Type
// content-type is not a required signing param. However, examples use this, so we include it to ease testing.
String contentType = getContentType(request);
if (!Strings.isNullOrEmpty(contentType)) {
requestBuilder.replaceHeader(HttpHeaders.CONTENT_TYPE, contentType);
signedHeadersBuilder.put(HttpHeaders.CONTENT_TYPE.toLowerCase(), contentType);
} else {
requestBuilder.removeHeader(HttpHeaders.CONTENT_TYPE);
}
// host
host = hostHeaderFor(request.getEndpoint());
requestBuilder.replaceHeader(HttpHeaders.HOST, host);
signedHeadersBuilder.put(HttpHeaders.HOST.toLowerCase(), host);
// user-agent, not a required signing param
String userAgent = request.getFirstHeaderOrNull(HttpHeaders.USER_AGENT);
if (userAgent != null) {
signedHeadersBuilder.put(HttpHeaders.USER_AGENT.toLowerCase(), userAgent);
}
// all x-amz-* headers
appendAmzHeaders(request, signedHeadersBuilder);
// x-amz-security-token
Credentials credentials = creds.get();
if (credentials instanceof SessionCredentials) {
String token = SessionCredentials.class.cast(credentials).getSessionToken();
requestBuilder.replaceHeader(AMZ_SECURITY_TOKEN_HEADER, token);
signedHeadersBuilder.put(AMZ_SECURITY_TOKEN_HEADER.toLowerCase(), token);
}
// x-amz-content-sha256
String contentSha256 = getPayloadHash();
requestBuilder.replaceHeader(AMZ_CONTENT_SHA256_HEADER, contentSha256);
signedHeadersBuilder.put(AMZ_CONTENT_SHA256_HEADER.toLowerCase(), contentSha256);
// put x-amz-date
requestBuilder.replaceHeader(AMZ_DATE_HEADER, timestamp);
signedHeadersBuilder.put(AMZ_DATE_HEADER.toLowerCase(), timestamp);
ImmutableMap<String, String> signedHeaders = signedHeadersBuilder.build();
String stringToSign = createStringToSign(request.getMethod(), request.getEndpoint(), signedHeaders, timestamp,
credentialScope, contentSha256);
signatureWire.getWireLog().debug("<< " + stringToSign);
byte[] signatureKey = signatureKey(credentials.credential, datestamp, region, service);
// init hmacSHA256 processor for seed signature and chunked block signature
ByteProcessor<byte[]> hmacSHA256;
try {
hmacSHA256 = hmacSHA256(crypto, signatureKey);
} catch (InvalidKeyException e) {
throw new ChunkedUploadException("invalid key", e);
}
// Calculating the Seed Signature
String signature;
try {
signature = hex(readBytes(toInputStream(stringToSign), hmacSHA256));
} catch (IOException e) {
throw new ChunkedUploadException("hmac sha256 seed signature error", e);
}
StringBuilder authorization = new StringBuilder(AMZ_ALGORITHM_HMAC_SHA256).append(" ");
authorization.append("Credential=").append(Joiner.on("/").join(credentials.identity, credentialScope))
.append(", ");
authorization.append("SignedHeaders=").append(Joiner.on(";").join(signedHeaders.keySet()))
.append(", ");
authorization.append("Signature=").append(signature);
// replace request payload with chunked upload payload
ChunkedUploadPayload chunkedPayload = new ChunkedUploadPayload(payload, userDataBlockSize, timestamp,
credentialScope, hmacSHA256, signature);
chunkedPayload.getContentMetadata().setContentEncoding(null);
return requestBuilder
.replaceHeader(HttpHeaders.AUTHORIZATION, authorization.toString())
.payload(chunkedPayload)
.build();
}