Skip to content

Commit a97f69b

Browse files
author
Andrea Scuderi
committed
Simplify Implementation - Graceful shutdown not working
1 parent 8b28a03 commit a97f69b

15 files changed

+349
-185
lines changed

Package.swift

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,8 @@ let package = Package(
1414
platforms: platforms,
1515
products: [
1616
.library(
17-
name: "BreezeLambdaWebHookService",
18-
targets: ["BreezeLambdaWebHookService"]
19-
),
20-
.library(
21-
name: "BreezeHTTPClientService",
22-
targets: ["BreezeHTTPClientService"]
17+
name: "BreezeLambdaWebHook",
18+
targets: ["BreezeLambdaWebHook"]
2319
),
2420
.executable(
2521
name: "BreezeDemoHTTPApplication",
@@ -28,38 +24,32 @@ let package = Package(
2824
],
2925
dependencies: [
3026
.package(url: "https://github.com/swift-server/swift-aws-lambda-runtime.git", branch: "main"),
27+
// .package(path: "../swift-aws-lambda-runtime"),
3128
.package(url: "https://github.com/swift-server/swift-aws-lambda-events.git", from: "0.5.0"),
3229
.package(url: "https://github.com/swift-server/async-http-client.git", from: "1.11.2"),
33-
.package(url: "https://github.com/apple/swift-log.git", from: "1.6.2"),
30+
.package(url: "https://github.com/swift-server/swift-service-lifecycle.git", from: "2.6.3")
3431
],
3532
targets: [
3633
.target(
37-
name: "BreezeLambdaWebHookService",
34+
name: "BreezeLambdaWebHook",
3835
dependencies: [
39-
"BreezeHTTPClientService",
4036
.product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime"),
4137
.product(name: "AWSLambdaEvents", package: "swift-aws-lambda-events"),
4238
.product(name: "AsyncHTTPClient", package: "async-http-client"),
4339
]
4440
),
45-
.target(
46-
name: "BreezeHTTPClientService",
47-
dependencies: [
48-
.product(name: "AsyncHTTPClient", package: "async-http-client"),
49-
.product(name: "Logging", package: "swift-log")
50-
]
51-
),
5241
.executableTarget(
5342
name: "BreezeDemoHTTPApplication",
5443
dependencies: [
55-
"BreezeLambdaWebHookService"
44+
"BreezeLambdaWebHook"
5645
]
5746
),
5847
.testTarget(
5948
name: "BreezeLambdaWebHookTests",
6049
dependencies: [
6150
.product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime"),
62-
"BreezeLambdaWebHookService"
51+
.product(name: "ServiceLifecycleTestKit", package: "swift-service-lifecycle"),
52+
"BreezeLambdaWebHook"
6353
],
6454
resources: [.copy("Fixtures")]
6555
),

Sources/BreezeDemoHTTPApplication/BreezeDemoHTTPApplication.swift

Lines changed: 40 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,17 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
import BreezeLambdaWebHookService
16-
import BreezeHTTPClientService
15+
import BreezeLambdaWebHook
1716
import AWSLambdaEvents
1817
import AWSLambdaRuntime
18+
import AsyncHTTPClient
1919
import Logging
20+
import NIOCore
21+
import ServiceLifecycle
2022

23+
/// This is a simple example of a Breeze Lambda WebHook handler.
24+
/// It uses the BreezeHTTPClientService to make an HTTP request to example.com
25+
/// and returns the response body as a string.
2126
struct DemoLambdaHandler: BreezeLambdaWebHookHandler, Sendable {
2227
var handlerContext: HandlerContext
2328

@@ -27,26 +32,43 @@ struct DemoLambdaHandler: BreezeLambdaWebHookHandler, Sendable {
2732

2833
func handle(_ event: APIGatewayV2Request, context: LambdaContext) async throws -> APIGatewayV2Response {
2934
context.logger.info("Received event: \(event)")
30-
return APIGatewayV2Response(with: "Hello World", statusCode: .ok)
35+
let request = HTTPClientRequest(url: "https://example.com")
36+
let response = try await handlerContext.httpClient.execute(request, timeout: .seconds(5))
37+
let bytes = try await response.body.collect(upTo: 1024 * 1024) // 1 MB Buffer
38+
let body = String(buffer: bytes)
39+
context.logger.info("Response body: \(body)")
40+
return APIGatewayV2Response(with: body, statusCode: .ok)
3141
}
3242
}
3343

44+
/// This is the main entry point for the Breeze Lambda WebHook application.
45+
/// It creates an instance of the BreezeHTTPApplication and runs it.
46+
/// The application name is used for logging and metrics.
47+
/// The timeout is used to set the maximum time allowed for the Lambda function to run.
48+
/// The default timeout is 30 seconds, but it can be changed to any value.
49+
///
50+
/// Local Testing:
51+
///
52+
/// The application will listen for incoming HTTP requests on port 7000 when run locally.
53+
///
54+
/// Use CURL to invoke the Lambda function, passing a JSON file containg API Gateway V2 request:
55+
///
56+
/// `curl -X POST 127.0.0.1:7000/invoke -H "Content-Type: application/json" -d @Tests/BreezeLambdaWebHookTests/Fixtures/get_webhook_api_gtw.json`
3457
@main
35-
struct BreezeDemoApplication {
58+
struct BreezeDemoHTTPApplication {
59+
60+
static let applicationName = "BreezeDemoHTTPApplication"
61+
static let logger = Logger(label: "BreezeDemoHTTPApplication")
62+
3663
static func main() async throws {
37-
do {
38-
let logger = Logger(label: "BreezeDemoApplication")
39-
let httpClientService = BreezeHTTPClientService(
40-
timeout: .seconds(60),
41-
logger: logger
42-
)
43-
let serviceConfig = BreezeClientServiceConfig(httpClientService: httpClientService)
44-
let lambdaService = BreezeLambdaWebHookService<DemoLambdaHandler>(
45-
serviceConfig: serviceConfig
46-
)
47-
try await lambdaService.run()
48-
} catch {
49-
print(error.localizedDescription)
50-
}
64+
let config = BreezeHTTPClientConfig(
65+
timeout: .seconds(30),
66+
logger: logger
67+
)
68+
let app = BreezeLambdaWebHook<DemoLambdaHandler>(
69+
name: applicationName,
70+
config: config,
71+
)
72+
try await app.run()
5173
}
5274
}

Sources/BreezeHTTPClientService/BreezeHTTPClientService.swift

Lines changed: 0 additions & 56 deletions
This file was deleted.

Sources/BreezeHTTPClientService/BreezeClientServiceConfig.swift renamed to Sources/BreezeLambdaWebHook/BreezeHTTPClientConfig.swift

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,18 @@
1313
// limitations under the License.
1414

1515
import Logging
16+
import NIOCore
1617

1718
public enum BreezeClientServiceError: Error {
1819
case invalidHttpClient
1920
}
2021

21-
public struct BreezeClientServiceConfig: Sendable {
22-
23-
public let httpClientService: BreezeHTTPClientServing
24-
25-
public init(httpClientService: BreezeHTTPClientServing) {
26-
self.httpClientService = httpClientService
22+
public struct BreezeHTTPClientConfig: Sendable {
23+
public init(timeout: TimeAmount, logger: Logger) {
24+
self.timeout = timeout
25+
self.logger = logger
2726
}
27+
28+
public let timeout: TimeAmount
29+
public let logger: Logger
2830
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright 2024 (c) Andrea Scuderi - https://github.com/swift-serverless
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import AWSLambdaEvents
16+
import AWSLambdaRuntime
17+
import ServiceLifecycle
18+
import Logging
19+
import NIOCore
20+
21+
public struct BreezeLambdaWebHook<LambdaHandler: BreezeLambdaWebHookHandler>: Service {
22+
23+
public let name: String
24+
public let config: BreezeHTTPClientConfig
25+
26+
public init(name: String, config: BreezeHTTPClientConfig) {
27+
self.name = name
28+
self.config = config
29+
}
30+
31+
public func run() async throws {
32+
do {
33+
let lambdaService = BreezeLambdaWebHookService<LambdaHandler>(
34+
config: config
35+
)
36+
let serviceGroup = ServiceGroup(
37+
services: [lambdaService],
38+
gracefulShutdownSignals: [.sigterm],
39+
logger: config.logger
40+
)
41+
config.logger.error("Starting \(name) ...")
42+
try await serviceGroup.run()
43+
} catch {
44+
config.logger.error("Error running \(name): \(error.localizedDescription)")
45+
}
46+
}
47+
}

Sources/BreezeLambdaWebHookService/BreezeLambdaWebHookService.swift renamed to Sources/BreezeLambdaWebHook/BreezeLambdaWebHookService.swift

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import AWSLambdaEvents
1717
import AWSLambdaRuntime
1818
import Foundation
1919
import ServiceLifecycle
20-
import BreezeHTTPClientService
2120
import Logging
2221

2322
public struct HandlerContext: Sendable {
@@ -30,19 +29,37 @@ public struct HandlerContext: Sendable {
3029

3130
public actor BreezeLambdaWebHookService<Handler: BreezeLambdaWebHookHandler>: Service {
3231

33-
private let serviceConfig: BreezeClientServiceConfig
34-
private var handlerContext: HandlerContext?
32+
let config: BreezeHTTPClientConfig
33+
var handlerContext: HandlerContext?
3534

36-
public init(serviceConfig: BreezeClientServiceConfig) {
37-
self.serviceConfig = serviceConfig
35+
public init(config: BreezeHTTPClientConfig) {
36+
self.config = config
3837
}
3938

4039
public func run() async throws {
41-
let httpClient = await serviceConfig.httpClientService.httpClient
40+
let timeout = HTTPClient.Configuration.Timeout(
41+
connect: config.timeout,
42+
read: config.timeout
43+
)
44+
let configuration = HTTPClient.Configuration(timeout: timeout)
45+
let httpClient = HTTPClient(
46+
eventLoopGroupProvider: .singleton,
47+
configuration: configuration
48+
)
4249
let handlerContext = HandlerContext(httpClient: httpClient)
4350
self.handlerContext = handlerContext
4451
let runtime = LambdaRuntime(body: handler)
45-
try await runtime.run()
52+
try await withGracefulShutdownHandler {
53+
try await runtime.run()
54+
} onGracefulShutdown: {
55+
do {
56+
self.config.logger.info("Shutting down HTTP client...")
57+
try httpClient.syncShutdown()
58+
self.config.logger.info("HTTP client has been shut down.")
59+
} catch {
60+
self.config.logger.error("Error shutting down HTTP client: \(error)")
61+
}
62+
}
4663
}
4764

4865
func handler(event: APIGatewayV2Request, context: LambdaContext) async throws -> APIGatewayV2Response {

0 commit comments

Comments
 (0)