As part of testing our Amazon API Gateway deployment, we set up JUnit tests to run automated Swagger/OpenAPI validation using Swagger Request Validator and REST-assured. This allows us to write simple tests in a fluent style, with automatic validation that requests and responses match the Swagger API specification deployed to API Gateway.

given()
        .log().all()
        .filter(validationFilter)
.when()
        .post("/oauth2/token")
.then()
        .assertThat()
        .statusCode(200);

Out of the box, REST-assured does not work with Amazon’s API Gateway endpoints using Java 8. Attempting to use it results in a handshake error when connecting to the API Gateway endpoint.

javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure

To workaround this problem, you need to enable Server Name Indication (SNI) for SSL when creating your SSL sockets, and configure the REST-assured client to use those created sockets.

SSL socket creation is handled by the org.apache.http.conn.ssl.SSLSocketFactory class. We need to extend this class and implement the createSocket method with SNI enabled for API Gateway. API Gateway uses TLSv1.2 encryption, and our socket should be enabled with that encryption protocol.

public class GatewaySslSocketFactory extends SSLSocketFactory {

    GatewaySslSocketFactory(SSLContext sslContext, X509HostnameVerifier hostnameVerifier) {
        super(sslContext, hostnameVerifier);
    }

    @Override
    public Socket createSocket(HttpParams params) throws IOException {
        SSLSocket sslSocket = (SSLSocket) super.createSocket(params);

        // Set the encryption protocol
        sslSocket.setEnabledProtocols(new String[]{"TLSv1.2"});

        // Configure SNI
        SNIHostName serverName = new SNIHostName("4********r.execute-api.us-east-1.amazonaws.com");
        SSLParameters sslParams = sslSocket.getSSLParameters();
        sslParams.setServerNames(Collections.singletonList(serverName));
        sslSocket.setSSLParameters(sslParams);

        return sslSocket;
    }
}

With our SocketFactory ready, we configure REST-assured to use it. In this example, we configure it as part of a base class for unit tests. This base class creates an instance of the SwaggerValidationFilter for automatic verification of your spec, and configures REST-assured to user our socket factory for all connections.

public class BaseApiGatewayTest {

    private static final String SWAGGER_JSON_URL = "/public-swagger.json";
    public static final String API_GATEWAY_HOST_URL = "https://4********r.execute-api.us-east-1.amazonaws.com";
    private static final int API_GATEWAY_HOST_PORT = 443;

    protected final SwaggerValidationFilter validationFilter = new SwaggerValidationFilter(SWAGGER_JSON_URL);

    @Before
    public void setUpBaseApiGateway() throws NoSuchAlgorithmException {
        // Set the host, port, and base path
        RestAssured.baseURI = API_GATEWAY_HOST_URL;
        RestAssured.port = API_GATEWAY_HOST_PORT;
        RestAssured.basePath = "dev";

        // Use our custom socket factory
        SSLSocketFactory customSslFactory = new GatewaySslSocketFactory(
                SSLContext.getDefault(), SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
        RestAssured.config = RestAssured.config().sslConfig(
                SSLConfig.sslConfig().sslSocketFactory(customSslFactory));
        RestAssured.config.getHttpClientConfig().reuseHttpClientInstance();
    }
}

With these pieces in place, writing API Gateway tests is fairly straight forward. Simply extend the base test class and start verifying expected behaviour. The Swagger validation filter will check for conformance to the spec; you will need to check for appropriate response codes and business logic.

public class OAuth2TokenTest extends BaseApiGatewayTest {

    @Test
    public void testGetToken() {
        given()
                .filter(validationFilter)
        .when()
                .post("/oauth2/token")
        .then()
                .assertThat()
                .statusCode(200);
    }
}