matsluni/aws-spi-akka-http

CloudwatchException triggered by default content-type header

Closed this issue · 6 comments

Looks like when Cloudwatch is trying to POST some metrics it fails because the default content-type is set to application/x-www-form-urlencoded.

software.amazon.awssdk.services.cloudwatch.model.CloudWatchException: When Content-Type:application/x-www-form-urlencoded, URL cannot include query-string parameters (after '?'): '/?Action=PutMetricData&Version=2010-08-01&Namespace=...

This is a POST to https://monitoring.us-west-2.amazonaws.com?Action=PutMetricData&Version=2010-08-...

Seems to fall through this code with the final None condition:

private def contentTypeHeaderToContentType(headers: List[HttpHeader]): ContentType = {
    headers.find(_.lowercaseName() == "content-type").map(_.value()) match {
      case Some("application/x-amz-json-1.0") => AkkaHttpClient.xAmzJson
      case Some("application/x-amz-json-1.1") => AkkaHttpClient.xAmzJson11
      case Some("application/x-www-form-urlencoded; charset=UTF-8") => AkkaHttpClient.formUrlEncoded
      case Some("application/x-www-form-urlencoded") => AkkaHttpClient.formUrlEncoded
      case Some("application/xml") => AkkaHttpClient.applicationXml
      case Some(s) => tryCreateCustomContentType(s)
      case None => AkkaHttpClient.formUrlEncoded
    }
  }

This looks interesting. Thanks for reporting.
I will have a look at this, I probably have time this friday.

Do you know what the expected content-type this request has or what is expected?

So it didn't seem that there was a content-type defined in the original request so I just changed the default to

 case None => ContentTypes.`application/octet-stream`

Which seems to be the default when using a bytestream entity and it started working. Not sure if that is a general enough solution though.

Screen Shot 2020-06-03 at 1 55 11 PM

Thanks for the investigation.

I remembered that there were some different behaviors between the aws standard http client and how Akka-http works with headers, especially with request signing in the mix.

I will investigate more, if using application/octet-stream is fine or if this will break anything else if used as a default.

@AdrianMF I looked a bit more at this issue. Until now, I was not able to reproduce it on my side.

Does the error happen for every request or was it more like a one off request? If yes, do you know what was maybe "special" for this failing request?

Also, I found out that the request use application/x-www-form-urlencoded as the content-type. I debugged a test case I wrote and saw that the case case Some("application/x-www-form-urlencoded") matched.

So I think its a specific request to Cloudwatch, we are doing some Kinesis streams with the AWS API and when it tries to make a request to cloudwatch, the original request doesn't include a content-type so the fallback is application/x-www-form-urlencoded and that is what causes the error because the request include URL query params. I guess AWS is validating the content-type against the URL params. Unfortunately I'm not sure I can easily hook into the original request to add a content-type

@AdrianMF I just released v0.0.10 of the library with the fallback ContentTypes.NoContentType. Can you please verify, that this change solves your problem. If yes, can you please close this issue.