Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add HTTP default listener #2289

Merged
merged 5 commits into from
Jan 31, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright (c) 2025 WSO2 LLC. (http://www.wso2.org).
//
// WSO2 LLC. 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/http_test_common.service1 as _;
import ballerina/http_test_common.service2 as _;
import ballerina/test;

listener http:Listener defaultListener = http:getDefaultListener();

service /api/v3 on defaultListener {

resource function get greeting() returns string {
return "Hello, World from service 3!";
}
}

service /api/v4 on defaultListener {

resource function get greeting() returns string {
return "Hello, World from service 4!";
}
}

listener http:Listener defaultListenerNew = http:getDefaultListener();

service /api/v5 on defaultListenerNew {

resource function get greeting() returns string {
return "Hello, World from service 5!";
}
}

final http:Client defaultListenerClient = check new(string `localhost:${http:defaultListenerPort}/api`);

@test:Config {}
function testDefaultListener() returns error? {
foreach int i in 1...5 {
string response = check defaultListenerClient->/[string `v${i}`]/greeting;
test:assertEquals(response, string `Hello, World from service ${i}!`);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ import ballerina/mime;
import ballerina/test;
import ballerina/http_test_common as common;

listener http:Listener mockEP2 = new (9091, httpVersion = http:HTTP_1_1);
final http:Client multipartRespClient = check new ("http://localhost:9091", httpVersion = http:HTTP_1_1);
listener http:Listener mockEP2 = new (multipartResponseTestPort, httpVersion = http:HTTP_1_1);
final http:Client multipartRespClient = check new (string `http://localhost:${multipartResponseTestPort}`, httpVersion = http:HTTP_1_1);

service /multipart on mockEP2 {
resource function get encode_out_response(http:Caller caller, http:Request request) returns error? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,10 @@ const int trailingHeaderTestPort2 = 9527;

const int corsConfigTestPort = 9013;
const int multipartRequestTestPort = 9018;
const int multipartResponseTestPort = 9019;
const int serviceMediaTypeSubtypePrefixPort = 9579;

const int statusCodeErrorUseCasePort = 9090;
const int statusCodeErrorUseCasePort = 9089;
const int statusCodeErrorPort = 9092;

const int identicalCookiePort = 9093;
Expand Down
10 changes: 10 additions & 0 deletions ballerina-tests/http-security-tests/tests/Config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,13 @@ scopes=["read"]
[[ballerina.auth.users]]
username="eve"
password="123"

[ballerina.http]
defaultListenerPort = 8080

[ballerina.http.defaultListenerConfig]
httpVersion = "1.1"

[ballerina.http.defaultListenerConfig.secureSocket.key]
path = "../resources/certsandkeys/ballerinaKeystore.p12"
password = "ballerina"
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright (c) 2025 WSO2 LLC. (http://www.wso2.org).
//
// WSO2 LLC. 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/http_test_common as common;
import ballerina/http_test_common.service1 as _;
import ballerina/http_test_common.service2 as _;
import ballerina/test;

listener http:Listener defaultListener = http:getDefaultListener();

service /api/v3 on defaultListener {

resource function get greeting() returns string {
return "Hello, World from service 3!";
}
}

service /api/v4 on defaultListener {

resource function get greeting() returns string {
return "Hello, World from service 4!";
}
}

listener http:Listener defaultListenerNew = http:getDefaultListener();

service /api/v5 on defaultListenerNew {

resource function get greeting() returns string {
return "Hello, World from service 5!";
}
}

final http:Client defaultListenerClient = check new(string `localhost:${http:defaultListenerPort}/api`,
secureSocket = {
cert: {
path: common:TRUSTSTORE_PATH,
password: "ballerina"
}
},
httpVersion = "1.1"
);

@test:Config {}
function testDefaultListenerWithConfiguration() returns error? {
foreach int i in 1...5 {
string response = check defaultListenerClient->/[string `v${i}`]/greeting;
test:assertEquals(response, string `Hello, World from service ${i}!`);
}
}
3 changes: 3 additions & 0 deletions ballerina-tests/http-test-common/modules/service1/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## Module overview

This module provides a mock HTTP service - service1.
26 changes: 26 additions & 0 deletions ballerina-tests/http-test-common/modules/service1/service.bal
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) 2025 WSO2 LLC. (http://www.wso2.org).
//
// WSO2 LLC. 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;

listener http:Listener defaultListener = http:getDefaultListener();

service /api/v1 on defaultListener {

resource function get greeting() returns string {
return "Hello, World from service 1!";
}
}
3 changes: 3 additions & 0 deletions ballerina-tests/http-test-common/modules/service2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## Module overview

This module provides a mock HTTP service - service2.
26 changes: 26 additions & 0 deletions ballerina-tests/http-test-common/modules/service2/service.bal
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) 2025 WSO2 LLC. (http://www.wso2.org).
//
// WSO2 LLC. 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;

listener http:Listener defaultListener = http:getDefaultListener();

service /api/v2 on defaultListener {

resource function get greeting() returns string {
return "Hello, World from service 2!";
}
}
4 changes: 2 additions & 2 deletions ballerina/Ballerina.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ path = "../native/build/libs/http-native-2.13.0-SNAPSHOT.jar"
groupId = "io.ballerina.stdlib"
artifactId = "mime-native"
version = "2.11.0"
path = "./lib/mime-native-2.11.0-20241218-125100-e28a03b.jar"
path = "./lib/mime-native-2.11.0-20250127-182500-46c9896.jar"

[[platform.java21.dependency]]
groupId = "io.ballerina.stdlib"
artifactId = "constraint-native"
version = "1.6.0"
path = "./lib/constraint-native-1.6.0-20241218-112400-cd313f2.jar"
path = "./lib/constraint-native-1.6.0-20250127-170900-48ad9ae.jar"

[[platform.java21.dependency]]
groupId = "io.netty"
Expand Down
2 changes: 1 addition & 1 deletion ballerina/CompilerPlugin.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ class = "io.ballerina.stdlib.http.compiler.HttpCompilerPlugin"
path = "../compiler-plugin/build/libs/http-compiler-plugin-2.13.0-SNAPSHOT.jar"

[[dependency]]
path = "../compiler-plugin/build/libs/ballerina-to-openapi-2.2.0-20241126-081700-8e808fd.jar"
path = "../compiler-plugin/build/libs/ballerina-to-openapi-2.2.0-20250127-223930-4c87b7d.jar"
2 changes: 1 addition & 1 deletion ballerina/Dependencies.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

[ballerina]
dependencies-toml-version = "2"
distribution-version = "2201.11.0-20241218-101200-109f6cc7"
distribution-version = "2201.11.0-20250127-101700-a4b67fe5"

[[package]]
org = "ballerina"
Expand Down
54 changes: 54 additions & 0 deletions ballerina/http_default_listener.bal
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright (c) 2025 WSO2 LLC. (http://www.wso2.org).
//
// WSO2 LLC. 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.

isolated Listener? defaultListener = ();

# Default HTTP listener port used by the HTTP Default Listener.
# The default value is 9090.
public configurable int defaultListenerPort = 9090;

# Default HTTP listener configuration used by the HTTP Default Listener.
public configurable ListenerConfiguration defaultListenerConfig = {};

# Returns the default HTTP listener. If the default listener is not already created, a new
# listener will be created with the port and configuration. An error will be returned if
# the listener creation fails.
#
# The default listener configuration can be changed in the `Config.toml` file. Example:
# ```toml
# [ballerina.http]
# defaultListenerPort = 8080
#
# [ballerina.http.defaultListenerConfig]
# httpVersion = "1.1"
#
# [ballerina.http.defaultListenerConfig.secureSocket.key]
# path = "resources/certs/key.pem"
# password = "password"
# ```
#
# + return - The default HTTP listener or an error if the listener creation fails.
public isolated function getDefaultListener() returns Listener|ListenerError {
lock {
Listener? tempListener = defaultListener;
if tempListener is Listener {
return tempListener;
}
Listener 'listener = check new Listener(defaultListenerPort, defaultListenerConfig);
defaultListener = 'listener;
return 'listener;
}
}
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
- [Add connection eviction feature to handle connections that receive GO_AWAY from the client](https://github.com/ballerina-platform/ballerina-library/issues/6734)
- [Enhance the configurability of Ballerina access logging by introducing multiple configuration options.](https://github.com/ballerina-platform/ballerina-library/issues/6111)
- [Introduce HTTP service contract object type](https://github.com/ballerina-platform/ballerina-library/issues/6378)
- [Add default HTTP listener](https://github.com/ballerina-platform/ballerina-library/issues/7514)

### Fixed

Expand Down
35 changes: 35 additions & 0 deletions docs/spec/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ The conforming implementation of the specification is released and included in t
* 2.1. [Listener](#21-listener)
* 2.1.1. [Automatically starting the service](#211-automatically-starting-the-service)
* 2.1.2. [Programmatically starting the service](#212-programmatically-starting-the-service)
* 2.1.3. [Default listener](#213-default-listener)
* 2.2. [Service](#22-service)
* 2.2.1. [Service type](#221-service-type)
* 2.2.2. [Service-base-path](#222-service-base-path)
Expand Down Expand Up @@ -212,6 +213,40 @@ http:Service s = service object {
};
```

#### 2.1.3. Default listener

The default listener can be created by calling the `getDefaultListener()` method. Once the default listener is created,
the subsequent calls to the `getDefaultListener()` method will return the same listener object. With this approach,
the user can attach multiple services to the same listener and configure the listener as required. The default listener
port is 9090.

```ballerina
import ballerina/http;

listener http:Listener httpListener = http:getDefaultListener();

service /api/v1 on httpListener {

resource function get greeting() returns string {
return "Hello, World from Service 1!";
}
}
```

The port and listener configuration of the default listener can be changed in the `Config.toml` as follows:

```toml
[ballerina.http]
defaultListenerPort = 8080

[ballerina.http.defaultListenerConfig]
httpVersion = "1.1"

[ballerina.http.defaultListenerConfig.secureSocket.key]
path = "resources/certs/ballerinaKeystore.p12"
password = "ballerina"
```

### 2.2. Service
Service is a collection of resources functions, which are the network entry points of a ballerina program.
In addition to that a service can contain public and private functions which can be accessed by calling with `self`.
Expand Down
Loading