Skip to content

Commit

Permalink
Add test cases for sni host name configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
Bhashinee authored and TharmiganK committed Dec 5, 2024
1 parent 780ea9a commit ebeb5b7
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 20 deletions.
3 changes: 1 addition & 2 deletions ballerina-tests/http-security-tests/tests/http2_ssl_test.bal
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@ http:ClientConfiguration http2SslClientConf1 = {
cert: {
path: common:TRUSTSTORE_PATH,
password: "ballerina"
},
sniHostName: "localhost2"
}
}
};

Expand Down
108 changes: 108 additions & 0 deletions ballerina-tests/http-security-tests/tests/ssl_sni_host_name_test.bal
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Copyright (c) 2024 WSO2 Inc. (http://www.wso2.org).
//
// WSO2 Inc. licenses this file to you under the Apache License,
// Version 2.0 (the "License"); you may not use this file except
// in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

import ballerina/http;
import ballerina/test;
import ballerina/http_test_common as common;

listener http:Listener http2SniListener = new (http2SniListenerPort, http2SslServiceConf);

http:ListenerConfiguration http1SniServiceConf = {
httpVersion: http:HTTP_1_1,
secureSocket: {
key: {
path: common:KEYSTORE_PATH,
password: "ballerina"
}
}
};

listener http:Listener http1SniListener = new (http1SniListenerPort, http1SniServiceConf);

service /http2SniService on http2SniListener {
resource function get .() returns string {
return "Sni works with HTTP_2!";
}
}

service /http1SniService on http1SniListener {
resource function get .() returns string {
return "Sni works with HTTP_1.1!";
}
}

http:ClientConfiguration http2SniClientConf = {
secureSocket: {
cert: {
path: common:TRUSTSTORE_PATH,
password: "ballerina"
},
serverName: "localhost"
}
};

http:ClientConfiguration http1SniClientConf = {
httpVersion: http:HTTP_1_1,
secureSocket: {
cert: {
path: common:TRUSTSTORE_PATH,
password: "ballerina"
},
serverName: "localhost"
}
};

http:ClientConfiguration http1SniClientConf2 = {
httpVersion: http:HTTP_1_1,
secureSocket: {
cert: {
path: common:TRUSTSTORE_PATH,
password: "ballerina"
},
serverName: "xxxx"
}
};

http:ClientConfiguration http2SniClientConf3 = {
secureSocket: {
serverName: "localhost"
}
};

@test:Config {}
public function testHttp2WithSni() returns error? {
http:Client clientEP = check new ("https://127.0.0.1:9207", http2SniClientConf);
string resp = check clientEP->get("/http2SniService/");
common:assertTextPayload(resp, "Sni works with HTTP_2!");
}

@test:Config {}
public function testHttp1WithSni() returns error? {
http:Client clientEP = check new ("https://127.0.0.1:9208", http1SniClientConf);
string resp = check clientEP->get("/http1SniService/");
common:assertTextPayload(resp, "Sni works with HTTP_1.1!");
}

@test:Config {}
public function testSniFailure() returns error? {
http:Client clientEP = check new ("https://127.0.0.1:9208", http1SniClientConf2);
string|error resp = clientEP->get("/http1SniService/");
if resp is error {
test:assertEquals(resp.message(), "SSL connection failed:No subject alternative names present /127.0.0.1:9208");
} else {
test:assertFail("Test `testSniFailure` is expecting an error. But received a success response");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@ const int stsPort = 9445;

const int http2GeneralPort = 9100;
const int http2SslGeneralPort = 9107;

const int http2SniListenerPort = 9207;
const int http1SniListenerPort = 9208;
4 changes: 2 additions & 2 deletions ballerina/http_client_config.bal
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public type RetryConfig record {|
# + shareSession - Enable/disable new SSL session creation
# + handshakeTimeout - SSL handshake time out
# + sessionTimeout - SSL session time out
# + sniHostName - Server name indication(SNI) to be used. If this is not present, hostname from the target URL will be used
# + serverName - Server name indication(SNI) to be used. If this is not present, hostname from the target URL will be used
public type ClientSecureSocket record {|
boolean enable = true;
crypto:TrustStore|string cert?;
Expand All @@ -115,7 +115,7 @@ public type ClientSecureSocket record {|
boolean shareSession = true;
decimal handshakeTimeout?;
decimal sessionTimeout?;
string sniHostName?;
string serverName?;
|};

# Provides configurations for controlling the endpoint's behaviour in response to HTTP redirect related responses.
Expand Down
4 changes: 2 additions & 2 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Change Log
This file contains all the notable changes done to the Ballerina HTTP package through the releases.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [2.11.6] - 2024-11-26
Expand Down Expand Up @@ -381,7 +381,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
- [Fix missing error of invalid inbound request parameter for forward() method](https://github.com/ballerina-platform/ballerina-standard-library/issues/311)
- [Fix HTTP Circuit Breaker failure when status codes are not provided in the configuration](https://github.com/ballerina-platform/ballerina-standard-library/issues/339)
- [Fix HTTP FailOver client failure when status codes are overridden by an empty array](https://github.com/ballerina-platform/ballerina-standard-library/issues/1598)
- [Fix already built incompatible payload thrown error](https://github.com/ballerina-platform/ballerina-standard-library/issues/1600)
- [Fix already built incompatible payload thrown error](https://github.com/ballerina-platform/ballerina-standard-library/issues/1600)
- [Optional Types Not Supported in HTTP Client Request Operation Target Type](https://github.com/ballerina-platform/ballerina-standard-library/issues/1433)

### Changed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,7 @@ public final class HttpConstants {
public static final BString SECURESOCKET_CONFIG_VERIFY_CLIENT = StringUtils.fromString("verifyClient");
public static final BString SECURESOCKET_CONFIG_CERT_VALIDATION_TYPE_OCSP_STAPLING =
StringUtils.fromString("OCSP_STAPLING");
public static final BString SECURESOCKET_CONFIG_SNI_HOST_NAME = StringUtils.fromString("serverName");

//Socket Config
public static final BString SOCKET_CONFIG = StringUtils.fromString("socketConfig");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public final class Constants {
public static final String CLIENT_SUPPORT_CIPHERS = "ciphers";
public static final String CLIENT_SUPPORT_SSL_PROTOCOLS = "sslEnabledProtocols";
public static final String CLIENT_ENABLE_SESSION_CREATION = "shareSession";
public static final String SNI_SERVER_NAME = "sniHostName";
public static final String SNI_SERVER_NAME = "serverName";
public static final String MUTUAL_SSL_PASSED = "passed";
public static final String MUTUAL_SSL_FAILED = "failed";
public static final String MUTUAL_SSL_DISABLED = "disabled";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,7 @@ private static SSLEngine getSslEngineForCerts(SocketChannel socketChannel, Strin
SslHandler sslHandler = sslContext.newHandler(socketChannel.alloc(), host, port);
SSLEngine sslEngine = sslHandler.engine();
sslHandlerFactory.addCommonConfigs(sslEngine);
setSniServerName(sslConfig, host, sslHandlerFactory, sslEngine);
configureSniServerName(sslConfig, host, sslHandlerFactory, sslEngine);
if (sslConfig.isHostNameVerificationEnabled()) {
setHostNameVerfication(sslEngine);
}
Expand Down Expand Up @@ -507,20 +507,18 @@ private static SSLEngine instantiateAndConfigSSL(SSLConfig sslConfig, String hos
if (sslConfig != null) {
sslEngine = sslHandlerFactory.buildClientSSLEngine(host, port);
sslEngine.setUseClientMode(true);
setSniServerName(sslConfig, host, sslHandlerFactory, sslEngine);
configureSniServerName(sslConfig, host, sslHandlerFactory, sslEngine);
if (hostNameVerificationEnabled) {
sslHandlerFactory.setHostNameVerfication(sslEngine);
}
}
return sslEngine;
}

private static void setSniServerName(SSLConfig sslConfig, String host, SSLHandlerFactory sslHandlerFactory, SSLEngine sslEngine) {
if (sslConfig.getSniHostName().isEmpty()) {
sslHandlerFactory.setSNIServerNames(sslEngine, host);
} else {
sslHandlerFactory.setSNIServerNames(sslEngine, sslConfig.getSniHostName());
}
private static void configureSniServerName(SSLConfig sslConfig, String host, SSLHandlerFactory sslHandlerFactory,
SSLEngine sslEngine) {
sslHandlerFactory.setSNIServerNames(sslEngine,
sslConfig.getSniHostName() != null ? sslConfig.getSniHostName() : host);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,11 +209,8 @@ private void configureSslForHttp2(SocketChannel ch, ChannelPipeline clientPipeli
SslContext sslCtx = sslHandlerFactory.createHttp2TLSContextForClient(false);
SslHandler sslHandler = sslCtx.newHandler(ch.alloc(), httpRoute.getHost(), httpRoute.getPort());
SSLEngine sslEngine = sslHandler.engine();
if (sslConfig.getSniHostName().isEmpty()) {
sslHandlerFactory.setSNIServerNames(sslEngine, httpRoute.getHost());
} else {
sslHandlerFactory.setSNIServerNames(sslEngine, sslConfig.getSniHostName());
}
sslHandlerFactory.setSNIServerNames(sslEngine,
sslConfig.getSniHostName() != null ? sslConfig.getSniHostName() : httpRoute.getHost());
if (sslConfig.isHostNameVerificationEnabled()) {
setHostNameVerfication(sslEngine);
}
Expand Down

0 comments on commit ebeb5b7

Please sign in to comment.