Skip to content

Commit ea6d4cc

Browse files
committed
Cleanup handlers and api signatures
1 parent 781a6a4 commit ea6d4cc

4 files changed

Lines changed: 127 additions & 134 deletions

File tree

Package.swift

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,19 @@ let package = Package(
1616
.plugin(name: "VercelPackager", targets: ["VercelPackager"])
1717
],
1818
dependencies: [
19-
.package(url: "https://github.com/vapor/vapor", from: "4.0.0"),
2019
.package(url: "https://github.com/apple/swift-crypto", "1.0.0" ..< "3.0.0"),
21-
.package(url: "https://github.com/apple/swift-nio.git", from: "2.13.0"),
22-
.package(url: "https://github.com/swift-server/swift-aws-lambda-runtime", from: "1.0.0-alpha.1")
20+
.package(url: "https://github.com/swift-server/swift-aws-lambda-runtime", from: "1.0.0-alpha.1"),
21+
.package(url: "https://github.com/vapor/vapor", from: "4.0.0")
2322
],
2423
targets: [
2524
.target(name: "Vercel", dependencies: [
26-
.product(name: "Crypto", package: "swift-crypto"),
2725
.product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime"),
28-
.product(name: "NIOCore", package: "swift-nio")
26+
.product(name: "Crypto", package: "swift-crypto")
2927
]),
3028
.target(name: "VercelVapor", dependencies: [
31-
"Vercel",
3229
.product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime"),
3330
.product(name: "Vapor", package: "vapor"),
34-
.product(name: "NIO", package: "swift-nio"),
35-
.product(name: "NIOHTTP1", package: "swift-nio"),
31+
.byName(name: "Vercel")
3632
]),
3733
.plugin(
3834
name: "VercelPackager",
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
//
2+
// VaporHandler.swift
3+
//
4+
//
5+
// Created by Andrew Barba on 8/25/23.
6+
//
7+
8+
import AWSLambdaRuntime
9+
import Vapor
10+
import Vercel
11+
12+
public protocol VaporHandler: RequestHandler {
13+
14+
static var environment: Environment { get }
15+
16+
static func configure(app: Application) async throws
17+
}
18+
19+
extension VaporHandler {
20+
21+
public static var environment: Environment {
22+
.development
23+
}
24+
25+
public static func setup(context: LambdaInitializationContext) async throws {
26+
let app = Application(environment, .shared(context.eventLoop))
27+
// Request vapor application from user code
28+
try await configure(app: app)
29+
// Configure vercel server
30+
app.servers.use(.vercel)
31+
// Start the application
32+
try app.start()
33+
// Cache the app instance
34+
Shared.app = app
35+
}
36+
37+
public func onRequest(_ req: Vercel.Request) async throws -> Vercel.Response {
38+
guard let app = Shared.app else {
39+
return .status(.serviceUnavailable).send("Vapor application not configured")
40+
}
41+
let vaporRequest = try Vapor.Request.from(request: req, for: app)
42+
let vaporResponse = try await app.responder.respond(to: vaporRequest).get()
43+
return try await .from(response: vaporResponse, on: app.eventLoopGroup.next())
44+
}
45+
}
46+
47+
fileprivate struct Shared {
48+
49+
static var app: Application?
50+
}
51+
52+
extension Vapor.Request {
53+
54+
static func from(request: Vercel.Request, for app: Application) throws -> Self {
55+
let buffer = request.rawBody.map { data in
56+
var _buffer = request.context.allocator.buffer(capacity: data.count)
57+
_buffer.writeBytes(data)
58+
return _buffer
59+
}
60+
61+
let nioHeaders = request.headers.reduce(into: NIOHTTP1.HTTPHeaders()) {
62+
$0.add(name: $1.key, value: $1.value.value)
63+
}
64+
65+
var url: String = request.path
66+
67+
if request.searchParams.count > 0, let search = request.search {
68+
url += "?\(search)"
69+
}
70+
71+
return try .init(
72+
application: app,
73+
method: .init(rawValue: request.method.rawValue),
74+
url: .init(path: url),
75+
version: HTTPVersion(major: 1, minor: 1),
76+
headers: nioHeaders,
77+
collectedBody: buffer,
78+
remoteAddress: .init(ipAddress: request.clientIPAddress, port: 443),
79+
logger: app.logger,
80+
on: app.eventLoopGroup.next()
81+
)
82+
}
83+
}
84+
85+
extension Vercel.Response {
86+
87+
static func from(response: Vapor.Response, on eventLoop: EventLoop) async throws -> Self {
88+
// Create status code
89+
let statusCode = Vercel.HTTPResponseStatus(
90+
code: response.status.code,
91+
reasonPhrase: response.status.reasonPhrase
92+
)
93+
94+
// Create the headers
95+
let headers: [String: HTTPHeaderValue] = response.headers.reduce(into: [:]) {
96+
$0[$1.name] = .init($1.value)
97+
}
98+
99+
// Stream the body to a future
100+
let future = response.body.collect(on: eventLoop).map {
101+
var buffer = $0
102+
let byteLength = buffer?.readableBytes ?? 0
103+
let bytes = buffer?.readBytes(length: byteLength)
104+
return Vercel.Response(
105+
statusCode: statusCode,
106+
headers: headers,
107+
body: bytes?.base64String(),
108+
isBase64Encoded: true
109+
)
110+
}
111+
112+
return try await future.get()
113+
}
114+
}

Sources/VercelVapor/VercelServer.swift

Lines changed: 0 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -6,51 +6,9 @@
66
//
77

88
import AWSLambdaRuntime
9-
import NIO
10-
import NIOHTTP1
119
import Vapor
1210
import Vercel
1311

14-
public protocol VaporHandler: RequestHandler {
15-
16-
static var environment: Environment { get }
17-
18-
static func configure(app: Application) async throws
19-
}
20-
21-
extension VaporHandler {
22-
23-
public static var environment: Environment {
24-
.development
25-
}
26-
27-
public static func setup(context: LambdaInitializationContext) async throws {
28-
let app = Application(environment, .shared(context.eventLoop))
29-
// Request vapor application from user code
30-
try await configure(app: app)
31-
// Configure vercel server
32-
app.servers.use(.vercel)
33-
// Start the application
34-
try app.start()
35-
// Cache the app instance
36-
Shared.app = app
37-
}
38-
39-
public func onRequest(_ req: Vercel.Request) async throws -> Vercel.Response {
40-
guard let app = Shared.app else {
41-
return .status(.serviceUnavailable).send("Vapor application not configured")
42-
}
43-
let vaporRequest = try Vapor.Request(req: req, for: app)
44-
let vaporResponse = try await app.responder.respond(to: vaporRequest).get()
45-
return try await Vercel.Response.from(response: vaporResponse, on: app.eventLoopGroup.next()).get()
46-
}
47-
}
48-
49-
fileprivate struct Shared {
50-
51-
static var app: Application?
52-
}
53-
5412
public final class VercelServer: Server {
5513

5614
private let app: Application
@@ -81,87 +39,3 @@ extension Application.Servers.Provider {
8139
}
8240
}
8341
}
84-
85-
extension Vapor.Request {
86-
87-
convenience init(req: Vercel.Request, for app: Application) throws {
88-
let buffer = req.rawBody.map { data in
89-
var _buffer = req.context.allocator.buffer(capacity: data.count)
90-
_buffer.writeBytes(data)
91-
return _buffer
92-
}
93-
94-
let nioHeaders = req.headers.reduce(into: NIOHTTP1.HTTPHeaders()) {
95-
$0.add(name: $1.key, value: $1.value.value)
96-
}
97-
98-
var url: String = req.path
99-
100-
if req.searchParams.count > 0, let search = req.search {
101-
url += "?\(search)"
102-
}
103-
104-
self.init(
105-
application: app,
106-
method: NIOHTTP1.HTTPMethod(rawValue: req.method.rawValue),
107-
url: Vapor.URI(path: url),
108-
version: HTTPVersion(major: 1, minor: 1),
109-
headers: nioHeaders,
110-
collectedBody: buffer,
111-
remoteAddress: nil,
112-
logger: app.logger,
113-
on: app.eventLoopGroup.next()
114-
)
115-
}
116-
}
117-
118-
extension Vercel.Response {
119-
static func from(response: Vapor.Response, on eventLoop: EventLoop) -> EventLoopFuture<Vercel.Response> {
120-
// Create status code
121-
let statusCode = Vercel.HTTPResponseStatus(
122-
code: response.status.code,
123-
reasonPhrase: response.status.reasonPhrase
124-
)
125-
126-
// Create the headers
127-
let headers: [String: HTTPHeaderValue] = response.headers.reduce(into: [:]) {
128-
$0[$1.name] = .init($1.value)
129-
}
130-
131-
// Can we access the body right away?
132-
if let string = response.body.string {
133-
return eventLoop.makeSucceededFuture(.init(
134-
statusCode: statusCode,
135-
headers: headers,
136-
body: string,
137-
isBase64Encoded: false
138-
))
139-
} else if let bytes = response.body.data {
140-
return eventLoop.makeSucceededFuture(.init(
141-
statusCode: statusCode,
142-
headers: headers,
143-
body: bytes.base64EncodedString(),
144-
isBase64Encoded: true
145-
))
146-
} else {
147-
// See if it is a stream and try to gather the data
148-
return response.body.collect(on: eventLoop).map { buffer -> Vercel.Response in
149-
// Was there any content
150-
guard
151-
var buffer = buffer,
152-
let bytes = buffer.readBytes(length: buffer.readableBytes)
153-
else {
154-
return Vercel.Response(statusCode: statusCode, headers: headers)
155-
}
156-
157-
// Done
158-
return Vercel.Response(
159-
statusCode: statusCode,
160-
headers: headers,
161-
body: bytes.base64String(),
162-
isBase64Encoded: true
163-
)
164-
}
165-
}
166-
}
167-
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//
2+
// VercelVapor.swift
3+
//
4+
//
5+
// Created by Andrew Barba on 8/25/23.
6+
//
7+
8+
@_exported import Vapor
9+
@_exported import Vercel

0 commit comments

Comments
 (0)