Changelog History
Page 6
-
v4.0.0-beta.1 Changes
October 24, 2019Application
is now a global container. (#2079)
๐ See below for a more in-depth explanation of this change, but these code examples explain it best:
Vapor 4 alpha:
let app = Application { s in s.register(Foo.self) { ...} }defer { app.shutdown() }let container = try app.makeContainer().wait()defer { container.shutdown() }let foo = try container.make(Foo.self)
Vapor 4 beta:
let app = Application()defer { app.shutdown() } app.register(Foo.self) { ... }let foo = app.make(Foo.self)
๐ In Vapor 3 and Vapor 4 alpha, Vapor's service architecture enforced a 1:1 relation between containers and event loops. This is a useful pattern for minimizing the amount of effort that needs to be spent on synchronizing concurrent access to services.
๐ However, as Swift on the server continues to evolve it is becoming evident that this pattern may hinder Vapor's ability to take full advantage of packages designed to be thread-safe. For example, the swift-server/async-http-client package which recently saw a 1.0.0 release. This HTTP client supports being used across many event loops on a given event loop group. To avoid inefficiencies caused by hopping between event loops, the API allows users to specify event loop preferences with varying degrees of strictness. By giving the API consumer the ability to communicate exactly what they need, the HTTP client can choose the most performant option. This decision may change depending on the current state of its internal connection pool and other parameters.
For example, imagine the following scenario: Request A comes into your application on event loop 1 resulting in an external API request to foo.com. Request A completes and the HTTP client stores event loop 1's connection to foo.com into its pool. Request B comes into your application on event loop 2 and also wants to make an external API request to foo.com. In this situation, two things could happen:
1: Request B could make a new connection to foo.com on event loop 2 at the cost of TCP handshake.
2: Request B could re-use event loop 1's connection to foo.com at the cost of thread-hopping.In this case option 2 is the clear winner as establishing new TCP connections is much more time consuming.
๐ฆ Requiring distinct copies of each service on a per event loop basis prevents these kinds of optimizations. Exceptions could be made for things like HTTP client, but that would mean adding extra complexity to the already complex services architecture. Additionally,
async-http-client
was built as a model for what good server-side Swift packages should look like.Application
is now aRoutesBuilder
. (#2079)
๐ Now that services are global,
Application
can be a routes builder. This makes single-file Vapor applications a lot more concise and is more similar to Vapor 3.import Vaporlet app = try Application(environment: .detect()) app.get("hello") { req inreturn "Hello, world!"}try app.run()
Request
now has access to theapplication
. (#2079)
Since all reference type, singleton services are now expected to be thread-safe,
Request
can safely use theApplication
to create services as needed. Where this is especially useful is in creating request-specific contexts into services. A good example of this is howDatabase
works in Fluent 4 beta.app.get("todos") { req inreturn Todo.query(on: req.db).all() }
This is powered by the following extension to
Request
:extension Request { var db: Database { return self.application.make(Database.self).with(req) } }
The key here is that
Database
is passed a reference to the currentRequest
for context. This enables database operations to do things like:- Delegate callbacks to the request event loop
- ๐ฒ Log query information and errors to the request's logger
- Report metrics information on a per-request basis
๐ Having explicit access to the context provided by request may be critical in production use cases. (See http://asim-malik.com/the-perils-of-node-continuation-local-storage/)
- Service creation methods no longer throw. (#2079)
๐ง Errors thrown during service creation indicate a configuration failure. These errors should only happen during development-time and to make them easier to track down, they will now result in
fatalError
. This also makes it easier to use providers to extendApplication
in ways that feel native.For example, this is how
Application
now conforms toRoutesBuilder
:extension Application: RoutesBuilder { public var routes: Routes { self.make() } public func add(route: Route) { self.routes.add(route: route) } }
- ๐ macOS 10.14+ and Linux are now the only officially supported platforms. (#2067, #2070)
- โ Multipart parsing and serialization was broken out into vapor/multipart-kit (#2080)
- โ WebSocket client module was broken out into vapor/websocket-kit (#2074)
UUID
is nowLosslessStringConvertible
.XCTVapor
is now exported as a product.- Vapor repos are moving to GitHub actions for CI. (#2072)
- ๐ HTTP body stream strategy
.collect
now supports an optional max size. (#2076)
-
v4.0.0-alpha.3 Changes
August 26, 2019Request
no longer requires aChannel
to init. Now, it requires anEventLoop
andSocketAddress?
. (#2037)
โ > Note: This makes testing a
Request
easier since you can pass in eitherEmbeddedEventLoop
or a real event loop from a group used elsewhere in your tests. Prior to this change, you were required to useEmbeddedChannel
andEmbeddedEventLoop
both of which are incompatible with real event loops.๐ > Note:
Application.running
allows you to programmatically shutdown your HTTP server from anywhere that you can accessApplication
. This includes routes, commands, etc. Because this can be accessed from any thread, it required synchronization (NSLock
) to access.- โ
XCTApplication
test helpers now require explicit start / shutdown. (#2037)
โ > Note: Although a bit more verbose, explicitly starting and shutting down test helpers gives the user more options for how they test their application. It also cuts down on edge cases in the testing implementation.
โ Example from Vapor's tests with explicit start / shutdown:
let app = Application.create(routes: { r, c in r.get("hello", ":a") { req inreturn req.parameters.get("a") ?? "" } r.get("hello", ":a", ":b") { req inreturn [req.parameters.get("a") ?? "", req.parameters.get("b") ?? ""] } })defer { app.shutdown() }let server = try app.testable().start()defer { server.shutdown() }try server.test(.GET, "/hello/vapor") { res inXCTAssertEqual(res.status, .ok) XCTAssertContains(res.body.string, "vapor") }.test(.POST, "/hello/vapor") { res inXCTAssertEqual(res.status, .notFound) }.test(.GET, "/hello/vapor/development") { res inXCTAssertEqual(res.status, .ok) XCTAssertEqual(res.body.string, #"["vapor","development"]"#) }
- ๐ Fixed an issue causing
Request.query.get
/Request.query.subscript
to crash. (#2018)
-
v4.0.0-alpha.2 Changes
August 02, 2019- โก๏ธ Updated to OpenCrypto alpha 2 (#2031)
- โก๏ธ Updated to SSWG's official
AsyncHTTPClient
package (#2031) ๐ Merged server and client websocket code into
WebSocket
(#2031)// client return WebSocket.connect( to: "ws://echo.websocket.org/", on: req.eventLoop) { ws in ws.send("Hello, world!") ws.onText { ws, text in promise.succeed(text) ws.close().cascadeFailure(to: promise) } }// serverrouter.webSocket("bar") { req, ws in ws.send("Hello, world!") ws.onText { ws, text in promise.succeed(text) ws.close().cascadeFailure(to: promise) } }
BCrypt
renamed toBcrypt
and included in Vapor (#2031)let hash = try Bcrypt.hash("vapor")print(hash) // $2b$12$Lmw/Zx2jSXgxE.r/8uipROCoh64KdPL7/mdEz38EqEFZDEu5JsAH2try Bcrypt.verify("vapor", created: hash) // truetry Bcrypt.verify("foo", created: hash) // false
-
v4.0.0-alpha.1 Changes
May 31, 2019๐ More information on Vapor 4 alpha releases:
๐ https://medium.com/@codevapor/vapor-4-alpha-1-releases-begin-94a4bc79dd9a
๐ API Docs:
-
v3.3.3 Changes
February 15, 2020- ๐ฒ Log the request path of a failed request in the
ErrorMiddleware
(#2170).
- ๐ฒ Log the request path of a failed request in the
-
v3.3.2 Changes
February 10, 2020โก๏ธ Update due to vulnerability in NIO
๐ See https://forums.swift.org/t/swiftnio-security-releases-2-13-1-and-1-14-2/33671 for more information.
-
v3.3.1 Changes
September 18, 2019 -
v3.3.0 Changes
February 28, 2019 -
v3.2.2 Changes
February 15, 2019๐ New:
- โ Vapor Compatibility Suite: All PRs to vapor/vapor:3 will run against our new compatibility suite that verifies projects built on Vapor continue to compile and pass tests.
- โ Submit your project here: https://forums.swift.org/t/vapor-compatibility-test-suite/20500
๐ Fixed:
- โช Reverted retain cycle fix that could cause Application to not deinit. (#1898).