-
-
Save jleskovar-tyro/620e18d05c69ee9568ece8609c21be66 to your computer and use it in GitHub Desktop.
/** | |
* An [HttpRequestInterceptor] that signs requests using any AWS [Aws4Signer] and [AwsCredentialsProvider]. | |
* | |
* NOTE: Ported to kotlin and v2 SDK from https://github.com/awslabs/aws-request-signing-apache-interceptor/ | |
*/ | |
class AwsRequestSigningInterceptor( | |
private val region: Region, | |
private val service: String, | |
private val signer: Aws4Signer, | |
private val awsCredentialsProvider: AwsCredentialsProvider | |
) : HttpRequestInterceptor { | |
override fun process(request: HttpRequest, context: HttpContext) { | |
val uriBuilder = URIBuilder(request.requestLine.uri) | |
// Copy Apache HttpRequest to an SDK request | |
val sdkRequest = createSignableRequest(context, request, uriBuilder) | |
// Sign it | |
val credentials = awsCredentialsProvider.resolveCredentials() | |
val signerParams = Aws4SignerParams.builder() | |
.signingRegion(region) | |
.signingName(service) | |
.awsCredentials(credentials) | |
.doubleUrlEncode(true) | |
.build() | |
val response = signer.sign(sdkRequest, signerParams) | |
// Now copy everything back | |
request.setHeaders(mapToHeaderList(response.headers()).toTypedArray()) | |
if (request is HttpEntityEnclosingRequest) { | |
if (request.entity != null) { | |
val basicHttpEntity = BasicHttpEntity() | |
basicHttpEntity.content = response.contentStreamProvider().get().newStream() | |
request.entity = basicHttpEntity | |
} | |
} | |
} | |
private fun createSignableRequest(context: HttpContext, request: HttpRequest, uriBuilder: URIBuilder): SdkHttpFullRequest { | |
val sdkHttpFullRequest = SdkHttpFullRequest.builder() | |
(context.getAttribute(HTTP_TARGET_HOST) as? HttpHost)?.let { | |
sdkHttpFullRequest.uri(URI.create(it.toURI())) | |
} | |
val httpMethod = HttpMethodName.fromValue(request.requestLine.method) | |
sdkHttpFullRequest.method(SdkHttpMethod.fromValue(httpMethod.name)) | |
sdkHttpFullRequest.encodedPath(uriBuilder.build().rawPath) | |
sdkHttpFullRequest.contentStreamProvider { ByteArrayInputStream(ByteArray(0)) } | |
(request as? HttpEntityEnclosingRequest)?.let { | |
it.entity?.content?.let { sdkHttpFullRequest.contentStreamProvider { it } } | |
} | |
uriBuilder.queryParams.forEach { sdkHttpFullRequest.appendRawQueryParameter(it.name, it.value) } | |
request.allHeaders.filter { !skipHeader(it) }.forEach { sdkHttpFullRequest.appendHeader(it.name, it.value) } | |
return sdkHttpFullRequest.build() | |
} | |
private fun mapToHeaderList(mapHeaders: MutableMap<String, MutableList<String>>): List<Header?> = | |
mapHeaders.map { BasicHeader(it.key, it.value.joinToString(",")) } | |
private fun skipHeader(header: Header): Boolean = | |
(("content-length".equals(header.name, ignoreCase = true) && "0" == header.value) // Strip Content-Length: 0 | |
|| "host".equals(header.name, ignoreCase = true)) // Host comes from endpoint | |
} |
Hi there,
This snippet is using Kotlin’s shorthand syntax for creating inline implementations of @FunctionalInterface interfaces using lambda syntax (see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions)
Here, the contentStreamProvider builder method takes in a ContentStreamProvider interface. This interface is marked as @FunctionalInterface. “{ it }” is defining a ContentStreamProvider whose newStream implementation (https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/ContentStreamProvider.html#newStream) returns “it”, which in this is input stream provided by the HttpEntity (HttpEntityEnclosingRequest.getEntity().getContent())
I see thank you very much for your valuable input
Thank you so much, it's really a pity that Amazon doesn't care about keeping their code/documentation up to date...
One remark: I think you can remove line 47 and instead use SdkHttpMethod.fromValue(request.requestLine.method)
directly. This removes every to AWS-SDK-V1.
Thank you so much, it's really a pity that Amazon doesn't care about keeping their code/documentation up to date... One remark: I think you can remove line 47 and instead use
SdkHttpMethod.fromValue(request.requestLine.method)
directly. This removes every to AWS-SDK-V1.
Also found that issue, it can be removed
Hello there,
Thank you so much because this helped us write our own adapter for v2. I just had one question since I am not familiar with Kotlin:
it.entity?.content?.let { sdkHttpFullRequest.contentStreamProvider { it } }
how is the conversion done here. Is it implicit?