aws / aws-sdk-java-v2

The official AWS SDK for Java - Version 2
Apache License 2.0
2.12k stars 804 forks source link

When connecting with AWS Transcribe Streaming HTTP/2, the AWS SDK HTTP/2 client does not provide ALPN "h2" #2436

Open ottokruse opened 3 years ago

ottokruse commented 3 years ago

Describe the bug

When connecting using HTTP/2 over TLS, clients are supposed to use ALPN in the TLS handshake, and provide value "h2" (this is explained in the HTTP/2 specs). The AWS Java SDK for Transcribe Streaming however, does not do this. It does not send ALPN strings.

This hurts us because we run a "mock" AWS Transcribe Streaming Endpoint, that runs a HTTP/2 server. The AWS Java SDK for Transcribe Streaming, cannot connect to our mock because our mock expects the "h2" ALPN string (as per HTTP/2 standard). Basically our mock, which uses a library to implement HTTP/2, requires ALPN "h2" (in line with HTTP/2 spec).

It seems the real AWS Transcribe streaming endpoint is more lenient than our mock, as obviously the AWS Java SDK is able to connect with the AWS Transcribe streaming endpoint. Apparently the AWS Transcribe streaming endpoint ignores the omission of ALPN "h2" and talks HTTP/2 with the client anyway.

We are logging this bug, because we think the AWS Java SDK for Transcribe Streaming should work any compliant HTTP/2 endpoints, such as the mock we have. This is useful for e.g. local integration testing, and other purposes.

Expected Behavior

When connecting to AWS Transcribe, that AWS SDK for Java uses APLN "h2" to indicate it wants to start talking HTTP/2

Current Behavior

The TLS connection is established without setting ALPN

Steps to Reproduce

Run a HTTP/2 server yourself, turn on TLS logging. Connect to it with the JAVA SDK. Observe no ALPN sent.

import software.amazon.awssdk.services.transcribestreaming.*;
TranscribeStreamingAsyncClient client = TranscribeStreamingAsyncClient.builder()
                    .endpointOverride(new URI("https://localhost:8443")).build();

Example HTTP/2 server in NodeJS that logs TLS info:

const http2 = require("http2");
const fs = require("fs");
const path = require("path");

const server = http2.createSecureServer({
  key: fs.readFileSync(path.join(__dirname, "cert", "selfsigned.key")),
  cert: fs.readFileSync(path.join(__dirname, "cert", "selfsigned.crt")),
  enableTrace: true,
});

server.on("stream", (stream, headers) => {
  // We're not getting here ever, because of the TLS ALPN issue
  stream.destroy();
});

console.log("Listening for connections on port 8443 ...");
server.listen(8443);

Possible Solution

Use another HTTP/2 client, that does do ALPN correctly.

Context

Besides local testing, we have in fact a more intricate use case where we have a proxy in front of Transcribe, that processes Transcribe responses in a way that suits our business needs

Your Environment

AWS SDK for Java v 2.15.15 Various OS-es tried (Windows, Mac)

dagnir commented 3 years ago

Hey @ottokruse, you're correct, the SDK currently relies on prior knowledge to determine whether or not it should initiate an HTTP/2 connection with the service. The SDK targets Java 8 as the minimum supported Java version, and when we released the SDK v2 in 2018, Java 8 did not have support for ALPN. It appears that ALPN support has since been backported to Java 8 so we may be able to support this in some way, at least for customers that have a supported JDK8 version.

HTTP protocol support is dependent of SdkAsyncHttpClient used by the Transcribe client, so it's not so much an issue with the Transcribe Streaming client, as it is with the default async client, NettyNioAsyncHttpClient.

I think ALPN support itself leans more toward a feature request given the history for ALPN support in Java 8 and since no service requires at at the moment, so I'm adding the feature-request label to this.

etspaceman commented 3 years ago

I'm running into this myself. I have a mock for Kinesis, and the server I am using depends on ALPN for Http2 connections. Without ALPN support here, I am unable to leverage Http2 when communicating with my mock.