Merge pull request #245 from vapor/joannis-docs

More Documentation and updates for PR
This commit is contained in:
Joannis Orlandos 2017-11-20 18:03:41 +01:00 committed by GitHub
commit 78c6fe4f8c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 471 additions and 456 deletions

View File

@ -1,29 +1,80 @@
# Introduction into Promises and Futures
# Future basics
When working with asynchronous APIs, one of the problems you'll face is not knowing when a variable is set.
Futures are used throughout Vapor, so it is useful to know some of the available helpers. We explain the reasoning and use cases [here](../concepts/async.md).
When querying a database synchronously, the thread is blocked until a result has been received. At which point the result will be returned to you and the thread continues from where you left off querying the database.
## Adding awaiters to all results
If you need to handle the results of an operation regardless of success or failure, you can do so by calling the `.addAwaiter` function on a future.
The awaiter shall be called on completion with a `Result<Expectation>`. This is an enum with either the `Expectation` or an `Error` contained within.
```swift
let user = try database.fetchUser(named: "Admin")
let future = Future("Hello world")
print(user.username)
future.addAwaiter { result in
switch result {
case .expectation(let string):
print(string)
case .error(let error):
print("Error: \(error)")
}
}
```
In the asynchronous world, you won't receive a result immediately. Instead, you'll receive a result in a callback.
## Flat-Mapping results
Nested async callbacks can be a pain to unwind. An example of a painfully complex "callback hell" scenario is demonstrated below:
```swift
// Callback `found` will receive the user. If an error occurred, the `onError` callback will be called instead.
try database.fetchUser(named: "Admin", found: { user in
print(user.username)
}, onError: { error in
print(error)
})
app.get("friends") { request in
let session = try request.getSessionCookie() as UserSession
let promise = Promise<View>()
// Fetch the user
try session.user.resolve().then { user in
// Returns all the user's friends
try user.friends.resolve().then { friends in
return try view.make("friends", context: friends, for: request).then { renderedView in
promise.complete(renderedView)
}.catch(promise.fail)
}.catch(promise.fail)
}.catch(promise.fail)
return promise.future
}
```
You can imagine code becoming complex. Difficult to read and comprehend.
Vapor 3 offers a `flatMap` solution here that will help keep the code readable and maintainable.
Promises and futures are two types that this library introduces to solve this.
```swift
app.get("friends") { request in
let session = try request.getSessionCookie() as UserSession
// Fetch the user
return try session.user.resolve().flatten { user in
// Returns all the user's friends
return try user.friends.resolve()
}.map { friends in
// Flatten replaced this future with
return try view.make("friends", context: friends, for: request)
}
}
```
## Combining multiple futures
If you're expecting the same type of result from multiple sources you can group them using the `flatten` function.
```swift
var futures = [Future<String>]()
futures.append(Future("Hello"))
futures.append(Future("World"))
futures.append(Future("Foo"))
futures.append(Future("Bar"))
let futureResults = futures.flatten() // Future<[String]>
```
## Creating a promise
@ -157,83 +208,3 @@ If you expect a result with a specified duration, say, 30 seconds:
// This will also throw an error if the deadline wasn't met
let user = try future.blocked(timeout: .seconds(30))
```
# Future basics
Futures are used throughout Vapor, so it is useful to know some of the available helpers.
## Adding awaiters to all results
If you need to handle the results of an operation regardless of success or failure, you can do so by calling the `.addAwaiter` function on a future.
The awaiter shall be called on completion with a `Result<Expectation>`. This is an enum with either the `Expectation` or an `Error` contained within.
```swift
let future = Future("Hello world")
future.addAwaiter { result in
switch result {
case .expectation(let string):
print(string)
case .error(let error):
print("Error: \(error)")
}
}
```
## Flat-Mapping results
Nested async callbacks can be a pain to unwind. An example of a painfully complex "callback hell" scenario is demonstrated below:
```swift
app.get("friends") { request in
let session = try request.getSessionCookie() as UserSession
let promise = Promise<View>()
// Fetch the user
try session.user.resolve().then { user in
// Returns all the user's friends
try user.friends.resolve().then { friends in
return try view.make("friends", context: friends, for: request).then { renderedView in
promise.complete(renderedView)
}.catch(promise.fail)
}.catch(promise.fail)
}.catch(promise.fail)
return promise.future
}
```
Vapor 3 offers a `flatMap` solution here that will help keep the code readable and maintainable.
```swift
app.get("friends") { request in
let session = try request.getSessionCookie() as UserSession
// Fetch the user
return try session.user.resolve().flatten { user in
// Returns all the user's friends
return try user.friends.resolve()
}.map { friends in
// Flatten replaced this future with
return try view.make("friends", context: friends, for: request)
}
}
```
## Combining multiple futures
If you're expecting the same type of result from multiple sources you can group them using the `flatten` function.
```swift
var futures = [Future<String>]()
futures.append(Future("Hello"))
futures.append(Future("World"))
futures.append(Future("Foo"))
futures.append(Future("Bar"))
let futureResults = futures.flatten() // Future<[String]>
```

View File

@ -2,8 +2,9 @@
Async is a library revolving around two main concepts:
- [Streams](stream.md)
- [Promises](promise-future-introduction.md)
- [Promises and Futures](futures.md)
- [Streams](streams.md)
- [Workers](worker.md)
Together they form the foundation of Vapor 3's data flow.

View File

@ -1,159 +0,0 @@
# Introduction into Promises and Futures
When working with asynchronous APIs, one of the problems you'll face is not knowing when a variable is set.
When querying a database synchronously, the thread is blocked until a result has been received. At which point the result will be returned to you and the thread continues from where you left off querying the database.
```swift
let user = try database.fetchUser(named: "Admin")
print(user.username)
```
In the asynchronous world, you won't receive a result immediately. Instead, you'll receive a result in a callback.
```swift
// Callback `found` will receive the user. If an error occurred, the `onError` callback will be called instead.
try database.fetchUser(named: "Admin", found: { user in
print(user.username)
}, onError: { error in
print(error)
})
```
You can imagine code becoming complex. Difficult to read and comprehend.
Promises and futures are two types that this library introduces to solve this.
## Creating a promise
Promises are important if you're implementing a function that returns a result in the future, such as the database shown above.
Promises need to be created without a result. They can then be completed with the expectation or an error at any point.
You can extract a `future` from the `promise` that you can hand to the API consumer.
```swift
// the example `fetchUser` implementation
func fetchUser(named name: String) -> Future<User> {
// Creates a promise that can be fulfilled in the future
let promise = Promise<User>()
do {
// TODO: Run a query asynchronously, looking for the user
// Initialize the user using the datbase result
// This can throw an error if the result is empty or invalid
let user = try User(decodingFrom: databaseResult)
// If initialization is successful, complete the promise.
//
// Completing the promise will notify the promise's associated future with this user
promise.complete(user)
} catch {
// If initialization is successful, fail the promise.
//
// Failing the promise will notify the promise's associated future with an error
promise.fail(error)
}
// After spawning the asynchronous operation, return the promise's associated future
//
// The future can then be used by the API consumer
return promise.future
}
```
## On future completion
When a promise completes, you can chain the result/error into a closure:
```swift
// The future provided by the above function will be used
let future: Future<User> = fetchUser(named: "Admin")
// `.then`'s closure will be executed on success
future.then { user in
print(user.username)
// `.catch` will catch any error on failure
}.catch { error in
print(error)
}
```
## Catching specific errors
Sometimes you only care for specific errors, for example, for logging.
```swift
// The future provided by the above function will be used
let future: Future<User> = fetchUser(named: "Admin")
// `.then`'s closure will be executed on success
future.then { user in
print(user.username)
// This `.catch` will only catch `DatabaseError`s
}.catch(DatabaseError.self) { databaseError in
print(databaseError)
// `.catch` will catch any error on failure, including `DatabaseError` types
}.catch { error in
print(error)
}
```
## Mapping results
Futures can be mapped to different results asynchronously.
```swift
// The future provided by the above function will be used
let future: Future<User> = fetchUser(named: "Admin")
// Maps the user to it's username
let futureUsername: Future<String> = future.map { user in
return user.username
}
// Mapped futures can be mapped and chained, too
futureUsername.then { username in
print(username)
}
```
## Futures without promise
In some scenarios you're required to return a `Future` where a `Promise` isn't necessary as you already have the result.
In these scenarios you can initialize a future with the already completed result.
```swift
// Already completed on initialization
let future = Future("Hello world!")
future.then { string in
print(string)
}
```
## Synchronous APIs
Sometimes, an API needs to be used synchronously in a synchronous envinronment.
Rather than using a synchronous API with all edge cases involved, we recommend using the `try future.blockingAwait()` function.
```swift
// The future provided by the above function will be used
let future: Future<User> = fetchUser(named: "Admin")
// This will either receive the user if the promise was completed or throw an error if the promise was failed.
let user: User = try future.blockingAwait()
```
This will wait for a result indefinitely, blocking the thread.
If you expect a result with a specified duration, say, 30 seconds:
```swift
// This will also throw an error if the deadline wasn't met
let user = try future.blocked(timeout: .seconds(30))
```

View File

@ -1,45 +0,0 @@
# Stream Basics
Streams are a set of events that occur asynchronously in time. Some events may dispatch immediately, and others may have a delay in nanoseconds, milliseconds, minutes or any other duration. Streams can be linked together to create simple, performant and maintainable software.
## Chaining streams
If an `OutputStream`'s Output is the same as an `InputStream`'s input, you can "chain" these streams together to create really performant and readable solutions.
This doesn't work for all situation, but let's look at an example that *does* accept [base64](../crypto/base64.md).
```swift
client.stream(to: base64encoder).stream(to: client)
```
The result is an "echo" server that base64-encodes incoming data, and replies it back in base64-encoded format.
Another good example is how Vapor processes requests internally.
```swift
let server = try TCPServer(port: 8080, worker: Worker(queue: myDispatchQueue))
// Servers are a stream of accepted web client connections
// Clients are an input and output stream of bytes
server.drain { client in
let parser = RequestParser()
let router = try yourApplication.make(Router.self)
let serialiser = ResponseSerializer()
// Parses client-sent bytes into the RequestParser
let requestStream = client.stream(to: parser)
// Parses requests to the Vapor router, creating a response
let responseStream = requestStream.stream(to: router)
// Serializes the responses, creating a byte stream
let serializedResponseStream = responseStream.stream(to: serializer)
// Drains the serialized responses back into the client socket
serializedResponseStream.drain(into: client)
}
```
In the above example, the output of one stream is inputted into another. This ends up taking care of the entire HTTP [Request](../http/request.md)/[Response](../http/response.md) process.
`Socket -> Request-Data -> Request -> Processing -> Response -> Response-Data -> Socket`

View File

@ -1,5 +1,3 @@
# Introduction into Streams
Streams is a mechanism that you can implement on objects that process any information efficiently and asynchronously without bloat.
There are three primary stream protocols:
@ -125,3 +123,15 @@ let stringStream = tcpStream.flatMap(utf8String)
// `optionalStringStream` is a stream outputting `String?`
let optionalStringStream = tcpStream.map(utf8String)
```
## Chaining streams
If an `OutputStream`'s Output is the same as an `InputStream`'s input, you can "chain" these streams together to create really performant and readable solutions.
This doesn't work for all situation, but let's look at an example that *does* accept [base64](../crypto/base64.md).
```swift
client.stream(to: base64encoder).stream(to: client)
```
The result is an "echo" server that base64-encodes incoming data, and replies it back in base64-encoded format.

View File

@ -1 +1,11 @@
TODO
# Worker
Workers are any type that keep track of the current [EventLoop](../concepts/async.md).
Worker is a simple protocol and can be conformed to if the context can return (by means of a computed property) or contain an EventLoop.
There are three primary Workers that you can use to access the `EventLoop` and it's queue or context.
- [Request](../http/request.md) can be used, usually from within a [Route](../routing/basics.md)
- [EventLoop](../concepts/async.md) is itself a worker to ensure the extensions and consumers of Worker can be used, too.
- `DispatchQueue` is a worker that will return a *new and clean* EventLoop based on the current DispatchQueue

View File

@ -2,19 +2,19 @@
Async APIs are one of the most important aspects of Vapor 3. You have likely noticed that they are
the biggest change to the API from previous versions. To learn more about how to use the new async
APIs check out the [Async &rarr; Getting Started](../async/getting-started.md) section. This document
APIs check out the [Async &rarr; Getting Started](../async/package.md) section. This document
aims to explain _why_ Vapor 3 moved to async APIs and _how_ it has led to huge performance gains.
## Differences
Let's first make sure we understand the different between a "sync" and "async" API.
Let's first make sure we understand the different between a "sync" and "async" API.
```swift
// sync
let users = try api.getUsers()
```
Sync APIs return values immediately.
Sync APIs return values immediately.
```swift
// async
@ -29,7 +29,7 @@ async APIs (such as [futures](../getting-started/futures.md)), but we will use c
## Concurrency
It's fairly obvious from the above snippet that sync APIs are more concise. So why would anyone use an async API?
To understand why, you must first understand how concurrency works with these two API types.
To understand why, you must first understand how concurrency works with these two API types.
### Sync (Blocking)
@ -40,7 +40,7 @@ let hello = "Hello".uppercased()
```
There is no problem with this API being synchronous because at no point in converting "Hello" to uppercase is
the computer ever _waiting_. Waiting, also called "blocking", is an important concept. Most simple functions,
the computer ever _waiting_. Waiting, also called "blocking", is an important concept. Most simple functions,
like `.uppercased()` never wait&mdash;they are busy doing the requested work (i.e., capitalizing letters) the entire time.
However, some functions do wait. The classic example of this is a function that makes a network request.
@ -50,8 +50,8 @@ let res = try HTTPClient.getSync("http://google.com")
```
In the above example, the `.getSync` function must block. After it sends the request to `google.com`, it must wait for
their server to generate a response. While this function is waiting, it can't do anything. In other words, the CPU
is idle. If you're trying to optimize your application, you'd quickly notice that letting your (expensive) CPU sit
their server to generate a response. While this function is waiting, it can't do anything. In other words, the CPU
is idle. If you're trying to optimize your application, you'd quickly notice that letting your (expensive) CPU sit
idle is not a great idea.
### Async (Non-blocking)
@ -65,7 +65,7 @@ HTTPClient.getAsync("http://google.com", onCompletion: { res, error in
})
```
Now, after the `.getAsync` function is done sending the request to `google.com`, it can simply continue executing your
Now, after the `.getAsync` function is done sending the request to `google.com`, it can simply continue executing your
program. When Google's servers return a response, the CPU will be notified, and it will give the response to your callback.
## Advanced
@ -77,14 +77,14 @@ Let's now take a deeper look at why Vapor prefers async (non-blocking) APIs over
You may be thinking, what's the problem with `.getSync`? Why not just call that function on a background thread.
That is indeed a valid solution, and it would work. This style of threading is widely used and taught. However, the problem is
that threads are not "free" to create. When you're programming for iOS, this doesn't (usually) matter. You can
often spin up as many threads as you like. You have a whole CPU to yourself! Things are different on the server though.
often spin up as many threads as you like. You have a whole CPU to yourself! Things are different on the server though.
Web applications must respond to thousands (even millions) of requests _at the same time_. Imagine running a million instances
of your iOS app on one device. Even relatively small costs, like creating a thread, become extremely important as a web server.
### Multi-Reactor
To achieve optimal performance, Vapor only creates one thread per CPU core on your machine. This means there will be no
wasted time allocating new threads and very little wasted time switching between them.
wasted time allocating new threads and very little wasted time switching between them.
Each one of these threads is called an event loop, similar to Node.js. The difference is that Vapor has multiple event loops
(one for each core) where as Node.js just has one. This means you don't need to spin up multiple instances of your app

View File

@ -12,15 +12,15 @@ HTTP/2 is a protocol with security and performance in mind. Designed with experi
## How it works
At the heart of HTTP lie the [Request](../http/request.md) and [Response](../http/response,d). Both of them are "HTTP Messages". Both HTTP messages consists of [Headers](../http/headers.md) and [a body](../http/body.md).
At the heart of HTTP lie the [Request](../http/request.md) and [Response](../http/response.md). Both of them are "HTTP Messages". Both HTTP messages consists of [Headers](../http/headers.md) and [a body](../http/body.md).
HTTP clients connect to an HTTP server. The clients can send a request to which the server will send a response.
Bodies contain the concrete information being transferred. Think of the web-page, images, videos, [JSON](../vapor/json.md) and [forms](../http/multipart.md).
Bodies contain the concrete information being transferred. Think of the web-page, images, videos, [JSON](../getting-started/json.md) and [forms](../http/multipart.md).
Headers contain metadata.
[Cookies](../http/cookies.md) are metadataabout the client that, for example, can be used for identifying users after they've (successfully) logged in. One of these methods are [session tokens](../jwt/index.md).
[Cookies](../http/cookies.md) are metadataabout the client that, for example, can be used for identifying users after they've (successfully) logged in. One of these methods are [session tokens](../jwt/package.md).
Another type of metadata can be related to the content. For example, defining the type of content transferred in the body.
@ -38,7 +38,7 @@ Responses have one additional property in addition to the message's properties.
## Handling requests
Requests in Vapor will be handled by a [router](../vapor/routing.md). This allows registering a path to a method. For example, registering `.get("users")` will register the path `/users/` to the method `GET`. The responder/closure associated with this route can then handle requests sent to `/users/` with the `GET` method.
Requests in Vapor will be handled by a [router](../getting-started/routing.md). This allows registering a path to a method. For example, registering `.get("users")` will register the path `/users/` to the method `GET`. The responder/closure associated with this route can then handle requests sent to `/users/` with the `GET` method.
## Types of endpoints
@ -50,12 +50,12 @@ iOS and Android apps usually communicate with an API, where a web browser such a
Websites come in two major flavours. Server and client rendered pages. "Rendering" in this context doesn't mean the graphical rendering on your monitor, but instead the way information is injected into the HTML DOM to display the information to the users.
Server rendered pages make use of a templating system such as [leaf](../leaf/index.md) whereas client rendered pages communicate with an API.
Server rendered pages make use of a templating system such as [leaf](../leaf/package.md) whereas client rendered pages communicate with an API.
### API
APIs are endpoints that sometimes receive but always reply with raw data. The raw data can be in any format. Most commonly, APIs communicate with [JSON](../vapor/json.md). Sometimes, they communicate with XML or other data types. Vapor can flexibly switch between supported formats, both by official or by community made libraries.
APIs are endpoints that sometimes receive but always reply with raw data. The raw data can be in any format. Most commonly, APIs communicate with [JSON](../getting-started/json.md). Sometimes, they communicate with XML or other data types. Vapor can flexibly switch between supported formats, both by official or by community made libraries.
APIs in Vapor are (almost) always creating using a "MVC" or "Model View Controller" model [which we explain here.](controllers.md)
APIs in Vapor are (almost) always creating using a "MVC" or "Model View Controller" model [which we explain here.](../getting-started/controllers.md)
Designing an API in Vapor is really simple. [We dive into this more here.](application-design.md)
Designing an API in Vapor is really simple. [We dive into this from here.](../getting-started/application.md)

View File

@ -1,6 +1,6 @@
# What is Vapor?
Vapor is a [high performance](../supplementary/performance.md), type-safe and [easy to use](setup.md) web framework written in and for Swift.
Vapor is a [high performance](../supplementary/performance.md), type-safe and [easy to use](../getting-started/hello-world.md) web framework written in and for Swift.
Vapor is designed for both big and small services, providing a low entry barrier to get started in addition to high performance, well tested and well documented APIs.

View File

@ -2,7 +2,7 @@
Crypto is a library containing all common APIs related to cryptography and security.
This project does **not** support TLS. For that, please see [the TLS package](../tls/index.md).
This project does **not** support TLS. For that, please see [the TLS package](../tls/package.md).
### Index

View File

@ -6,16 +6,38 @@ This guide assumes you've set up MySQL and are connected to MySQL using a connec
The MySQL driver is written to embrace type-safety and Codable. We currently *only* expose Codable based results until we've found a good design for the non-codable API.
## Retaining a connection
Before you can send queries, you first need to retain a connection from the pool.
You are *required* to return a [Future](../../async/futures.md) from within the closure. This closure's completion will be used to determine when the connection can be released back into the pool for the next request.
```swift
// The result from within the closure
// Future<Response>
let result = pool.retain { connection in
// query the connection
return Future(Response(status: .ok))
}
```
The future returned from the retain call will be equal to the future returned within the retain call.
This way you can return the connection back into the pool after one or more successive operations and return the (final) result.
## Queries
Queries are any type conforming to the protocol `Query`, which requires being convertible to a `String`.
`String` is a Query by default.
You can receive results from Queries in 3 kinds of formats.
You can receive results from Queries in 4 kinds of formats.
- Stream
- Future
- forEach
- Stream<Result>
- Future<Result>
- forEach<Result>
- Future<Void>
All examples assume the following model:
@ -27,28 +49,15 @@ struct User {
}
```
### Streams
Streams, [as described on this page](../async/streams-introduction.md), are a source of information that calls a single reader's callback. Streams are best used in larger datasets to prevent the query from consuming a large amount of memory. The downside of a stream is that you cannot return all results in a single future. You'll need to stream the results to the other endpoint, too. For HTTP [this is described here.](../http/streaming-response.md)
Querying a database for a stream of results is achieved through the `stream` function and requires specifying the `Decodable` type that the results need to be deserialized into.
```swift
// `ModelStream<User>`
let usersStream = connectionPool.stream(User.self, in: "SELECT * FROM users")
```
This stream will return all results in the ModelStream's output callback which you can drain. You can register a callback on `usersStream.onClose` that will trigger when the end of the `ModelStream` has been reached.
### Futures
Futures are often easier to use but significantly heavier on your memory and thus performance. [They are thoroughly described here](../async/promise-future-introduction.md)
Futures are often easier to use but significantly heavier on your memory and thus performance. [They are thoroughly described here](../../async/futures.md)
Querying a database for a future is achieved through the `all` function and requires specifying the `Decodable` type that the results need to be deserialized into.
```swift
// Future<[User]>
let users = connectionPool.all(User.self, in: "SELECT * FROM users")
let users = connection.all(User.self, in: "SELECT * FROM users")
```
For partial results (`SELECT username, age FROM`) it is recommended to create a second decodable struct specifically for this query to ensure correctness and type-safety.
@ -60,12 +69,25 @@ struct UserLoginDetails: Decodable {
}
```
### Streams
Streams, [as described on this page](../../async/streams.md), are a source of information that calls a single reader's callback. Streams are best used in larger datasets to prevent the query from consuming a large amount of memory. The downside of a stream is that you cannot return all results in a single future. You'll need to stream the results to the other endpoint, too. For HTTP [this is described here.](../../http/body-stream.md)
Querying a database for a stream of results is achieved through the `stream` function and requires specifying the `Decodable` type that the results need to be deserialized into.
```swift
// `ModelStream<User>`
let usersStream = connection.stream(User.self, in: "SELECT * FROM users")
```
This stream will return all results in the ModelStream's output callback which you can drain. You can register a callback on `usersStream.onClose` that will trigger when the end of the `ModelStream` has been reached.
### ForEach
If you don't need to stream complex results to a third party such as using an HTTP Response you can use `forEach`. This is particularly useful for asynchronous actions such as sending a lot of email to the results of a query without depending on the completion/success of one email for the next email.
```swift
connectionPool.forEach(User.self, in: "SELECT * FROM users") { user in
connection.forEach(User.self, in: "SELECT * FROM users") { user in
print(user.username)
}
```
@ -73,7 +95,7 @@ connectionPool.forEach(User.self, in: "SELECT * FROM users") { user in
`forEach` returns a future that you can optionally capture. It will be completed when all users have been processed.
```swift
let completed = connectionPool.forEach(User.self, in: "SELECT * FROM users") { user in
let completed = connection.forEach(User.self, in: "SELECT * FROM users") { user in
print(user.username)
}
@ -86,16 +108,16 @@ completed.then {
Some queries (mostly administrative queries) do not require/return a response. Instead, they only indicate success or error.
You can execute these queries using the `query` command.
You can execute these queries using the `administrativeQuery` command.
```swift
connectionPool.query("DROP TABLE users")
connection.administrativeQuery("DROP TABLE users")
```
You can handle success or response using the returned future.
```swift
connectionPool.query("DROP TABLE users").then {
connection.administrativeQuery("DROP TABLE users").then {
print("success")
}.catch {
print("failure")

View File

@ -1 +0,0 @@
TODO

View File

@ -1,37 +0,0 @@
# MySQL
This package is a driver for the [MySQL Database](https://en.wikipedia.org/wiki/MySQL), an [RDBMS](https://en.wikipedia.org/wiki/Relational_database_management_system) oriented towards stability and robustness.
This driver supports both MySQL and MariaDB. These two databases are almost identical towards the user.
!!! warning
This documentation provides an overview for the MySQL API.
If you are using MySQL with Fluent, you will likely never need to use
this API. Use [Fluent's APIs](../fluent/overview.md) instead.
### Index
- [Setup](setup.md)
- [Basics](basics.md)
## Package.swift
To include it in your package, add the following to your `Package.swift` file.
```swift
// swift-tools-version:4.0
import PackageDescription
let package = Package(
name: "Project",
dependencies: [
...
.package(url: "https://github.com/vapor/mysql.git", .upToNextMajor(from: "3.0.0")),
],
targets: [
.target(name: "Project", dependencies: ["MySQL", ... ])
]
)
```
Use `import MySQL` to access MySQL' APIs.

View File

@ -1,3 +1,56 @@
# MySQL Package
# Using MySQL
Coming soon.
The [vapor/mysql](https://github.com/vapor/mysql) package is a lightweight, async pure swift MySQL/MariaDB driver. It provides an intuitive Swift interface for working with MySQL that can be used with any Swift project.
<!-- On top of [vapor/mysql](https://github.com/vapor/mysql), we have built [vapor/fluent-sqlite](https://github.com/vapor/fluent-sqlite) which allows SQLite databases to be used with Fluent. -->
<!-- ## With Fluent
SQLite works great with Fluent, you just need to make sure to add the [vapor/fluent-sqlite](https://github.com/vapor/fluent-sqlite)
package to your project.
To do this, add the Fluent SQLite package to your Package manifest.
```swift
// swift-tools-version:4.0
import PackageDescription
let package = Package(
name: "Project",
dependencies: [
...
.package(url: "https://github.com/vapor/fluent-sqlite.git", .upToNextMajor(from: "3.0.0")),
],
targets: [
.target(name: "Project", dependencies: ["FluentSQLite", ... ])
]
)
```
If this is your first time adding a dependency, you should read our introduction to [Package.swift](../../getting-started/spm.md).
Use `import FluentSQLite` to access SQLite's Fluent compatible APIs. -->
## Just MySQL
This package was built to be a powerful interface for MySQL. To include this MySQL package in your project, simply add it to your Package manifest.
```swift
// swift-tools-version:4.0
import PackageDescription
let package = Package(
name: "Project",
dependencies: [
...
.package(url: "https://github.com/vapor/mysql.git", .upToNextMajor(from: "3.0.0")),
],
targets: [
.target(name: "Project", dependencies: ["MySQL", ... ])
]
)
```
If this is your first time adding a dependency, you should read our introduction to [Package.swift](../../getting-started/spm.md).
Use `import SQLite` to access the Swift SQLite APIs.

View File

@ -0,0 +1,37 @@
# Prepared statements
Preparing statements is important in many SQL operations to prevent SQL injection.
You first have to set up your query to make use of statement binding.
To design your query for preparation you must replace all user inputted values with a `?` such as the following statement:
```sql
SELECT * FROM users WHERE username = ?
```
## Preparing a statement
To prepare a statement from a query you call the `withPreparation` function on `Connection`.
```swift
try connection.withPreparation(statement: "SELECT * FROM users WHERE username = ?") { statement in
// Bind
}
```
## Binding to a statement
The statement can be bound by calling the `bind` function on `statement`. This will provide you with a temporary binding context.
```swift
try statement.bind { binding in
try binding.bind("ExampleUser")
}
```
Bindings will throw an error if the inputted value did not meet the query's required type.
## Reading the query's results
You can then use the [Future or Streaming query functions as described in the basics](basics.md) to receive the queried results from the prepared and bound `statement` object.

View File

@ -28,12 +28,18 @@ The following code creates a new connectionpool to `localhost` and the default M
The database is the database that is selected and authenticated to. Any future queries will be sent to this database.
The `worker` is defined in [the async documentation](../async/worker.md).
The `worker` is defined in [the async documentation](../../async/worker.md).
```swift
let connectionPool = ConnectionPool(hostname: "localhost", user: "root", password: nil, database: "test-db", worker: worker)
```
You can set the maximum amount of connections after creating the `ConnectionPool`.
```swift
connectionPool.maxConnections = 50
```
Creating a connection pool successfully does not imply that the configuration is correct. The (first) query's success or failure will indicate the successful or unsuccessful connection. This way the API stays much simpler than it would otherwise be.
[Learn how you can execute queries here](basics.md)

View File

View File

View File

@ -1,12 +1,12 @@
# SQLite Overview
Let's dive into the [vapor/sqlite](https://github.com/vapor/sqlite) package and
Let's dive into the [vapor/sqlite](https://github.com/vapor/sqlite) package and
see how to connect to and query a database.
!!! warning
This documentation provides an overview for the SQLite API.
This documentation provides an overview for the SQLite API.
If you are using SQLite with Fluent, you will likely never need to use
this API. Use [Fluent's APIs](../fluent/overview.md) instead.
this API. Use [Fluent's APIs](../../fluent/getting-started/package.md) instead.
Follow the instructions in the [package](package.md) section to add the SQLite package to your project. Once its added, you should be able to use `import SQLite.`
@ -46,11 +46,11 @@ let conn = try db.makeConnection(on: .global())
```
!!! note
Pay special attention to which `DispatchQueue` you pass to `makeConnection(on:)`.
Pay special attention to which `DispatchQueue` you pass to `makeConnection(on:)`.
This will be the queue SQLite calls you back on.
!!! tip
If you are using SQLite with Vapor, make sure to pass the [worker](async/worker.md)'s queue here.
If you are using SQLite with Vapor, make sure to pass the [worker](../../async/worker.md)'s queue here.
## Query
@ -62,7 +62,7 @@ let query = conn.query("SELECT * FROM users")
### Binding Values
If you are executing a query that has input values, you should bind these using parameters.
If you are executing a query that has input values, you should bind these using parameters.
```swift
let query = conn.query("INSERT INTO users (name, age) VALUES (?, ?)")
@ -130,7 +130,7 @@ let name = row["name"]?.text // String
### Run
Once your query is ready to execute, you simply call `.execute()`. This returns a `Future<Void>`
Once your query is ready to execute, you simply call `.execute()`. This returns a `Future<Void>`
that will be completed when the query is done executing.
```swift
@ -162,7 +162,7 @@ let rows = try conn.query("SELECT * FROM users").sync()
### Example
Now for the complete example:
Now for the complete example:
```swift
import SQLite

View File

@ -6,7 +6,7 @@ On top of [vapor/sqlite](https://github.com/vapor/sqlite), we have built [vapor/
## With Fluent
SQLite works great with Fluent, you just need to make sure to add the [vapor/fluent-sqlite](https://github.com/vapor/fluent-sqlite)
SQLite works great with Fluent, you just need to make sure to add the [vapor/fluent-sqlite](https://github.com/vapor/fluent-sqlite)
package to your project.
To do this, add the Fluent SQLite package to your Package manifest.
@ -27,7 +27,7 @@ let package = Package(
)
```
If this is your first time adding a dependency, you should read our introduction to [Package.swift](../spm/manfiest.md).
If this is your first time adding a dependency, you should read our introduction to [Package.swift](../../getting-started/spm.md).
Use `import FluentSQLite` to access SQLite's Fluent compatible APIs.
@ -46,9 +46,11 @@ let package = Package(
.package(url: "https://github.com/vapor/sqlite.git", .upToNextMajor(from: "3.0.0")),
],
targets: [
.target(name: "Project", dependencies: ["Async", ... ])
.target(name: "Project", dependencies: ["SQLite", ... ])
]
)
```
If this is your first time adding a dependency, you should read our introduction to [Package.swift](../../getting-started/spm.md).
Use `import SQLite` to access the Swift SQLite APIs.

View File

@ -1,12 +1,12 @@
# Adding Fluent to your Project
Fluent ([vapor/fluent](https://github.com/vapor/fluent)) is a type-safe, fast, and easy-to-use ORM built for Swift.
Fluent ([vapor/fluent](https://github.com/vapor/fluent)) is a type-safe, fast, and easy-to-use ORM built for Swift.
It takes advantage of Swift's strong type system to provide an elegant API for your database.
## Database
In addition to adding Fluent to your project, you must also add a Fluent compatible database.
Fluent does not include any databases by default. All official databases have a getting started guide similar to this one.
In addition to adding Fluent to your project, you must also add a Fluent compatible database.
Fluent does not include any databases by default. All official databases have a getting started guide similar to this one.
| database | library | driver | guide |
|------------|-------------------------|--------------------------|------------------------------------------------------------------|
@ -16,7 +16,7 @@ Fluent does not include any databases by default. All official databases have a
| MongoDB | mongokitten/mongokitten | vapor/fluent-mongokitten | [README.md](http://github.com/vapor/fluent-mongokitten/readme.md)|
!!! tip
Any database can be made to work with Fluent by conforming to its [Database](database-protocol.md) protocol.
Any database can be made to work with Fluent by conforming to its [Database](../database.md) protocol.
For a list of all compatible database types, search GitHub for the [fluent-driver](https://github.com/topics/fluent-driver) topic.
## Fluent
@ -39,9 +39,9 @@ let package = Package(
)
```
If this is your first time adding a dependency, you should read our introduction to [Package.swift](../spm/manfiest.md).
If this is your first time adding a dependency, you should read our introduction to [Package.swift](../../getting-started/spm.md).
!!! note
!!! note
Use `import Fluent` to access Fluent's APIs.
Once you have Fluent added to your project, you are ready to [configure your database(s)](provider.md).

View File

@ -1,6 +1,6 @@
# Configuring Fluent
Fluent integrates seamlessly into your Vapor project using [services](../getting-started/application.md#services).
Fluent integrates seamlessly into your Vapor project using [services](../getting-started/application.md#services).
In this section we will add the Fluent service provider to your application and configure your databases.
!!! warning
@ -18,21 +18,21 @@ import Fluent
try services.register(FluentProvider())
```
Register the `FluentProvider` in the [configure section](../getting-started/structure.md#configure) of your application.
Register the `FluentProvider` in the [configure section](../../getting-started/structure.md#configure) of your application.
!!! question
Learn more about how service providers work in [Getting Started: Application](../getting-started/application.md#providers)
and [Concepts: Services](../concepts/services.md#providers).
Learn more about how service providers work in [Getting Started: Application](../../getting-started/application.md#providers)
and [Concepts: Services](../../concepts/services.md#providers).
## Config
Once the service provider has been added, we can configure one or more databases
Once the service provider has been added, we can configure one or more databases
to be used with Fluent.
### Identifier
Each database you use with Fluent must have a unique identifier. The easiest way to
Each database you use with Fluent must have a unique identifier. The easiest way to
keep track of this identifier is to add it as static extension to `DatabaseIdentifier`.
```swift
@ -57,13 +57,13 @@ Now we can use the identifier anywhere in our project:
req.database(.foo) { ... }
```
The [configure section](../getting-started/structure.md#configure) of your project is a good place to put this extension.
The [configure section](../../getting-started/structure.md#configure) of your project is a good place to put this extension.
### Databases
Now that we have created a unique identifier for our database, we can register it
to our application using `DatabaseConfig`. A good place to do this is in the
[configure section](../getting-started/structure.md#configure) of your project.
[configure section](../../getting-started/structure.md#configure) of your project.
You can add databases to the `DatabaseConfig` using either a type (`.self`) or an instance.
@ -87,7 +87,7 @@ services.register(databaseConfig)
#### Instance
You can also register a pre-initialized database. This is especially useful if you'd
You can also register a pre-initialized database. This is especially useful if you'd
like to configure two instances of the same database type.
```swift

View File

@ -1,13 +1,13 @@
# Futures
You may have noticed some APIs in Vapor expect or return a `Future<T>` type.
You may have noticed some APIs in Vapor expect or return a `Future<T>` type.
If this is your first time hearing about futures, they might seem a little confusing at first.
But don't worry, Vapor makes them easy to use.
## Callbacks
Futures are a way of representing an object that you don't have yet. You have probably seen
APIs that use callbacks before (especially if you have developed iOS apps). Futures are just a
APIs that use callbacks before (especially if you have developed iOS apps). Futures are just a
way to make APIs with callbacks more user-friendly.
This is a what a common callback API looks like.
@ -27,7 +27,7 @@ print(users) // Future<[User]>
```
!!! info
Futures use fewer system resources than synchronous APIs. This is part of
Futures use fewer system resources than synchronous APIs. This is part of
what makes Vapor so fast!
## Chaining
@ -77,5 +77,4 @@ four.do { four in
```
Learn more about Vapor's async architecture in [Concepts &rarr; Async](../concepts/async.md).
Or take a deeper look at Futures and Promises in [Async &rarr; Getting Started](../async/getting-started.md).
Or take a deeper look at Futures and Promises in [Async &rarr; Getting Started](../async/futures.md).

View File

View File

View File

@ -1 +1,46 @@
TODO
# HTTP Client
HTTP Clients are often used to communicate with external APIs such as PayPal, Stripe or Mailgun.
## Connecting
Connecting only requires a hostname and a boolean indicating if you want to use SSL. For almost every use case it is recommended to use SSL. If you're processing any sensitive data such as payments, emails and other personal data you will need to use SSL by setting it to `true`.
HTTP clients require a [Worker](../async/worker.md), too, so it can run on the current [EventLoop](../concepts/async.md)
```swift
// Future<HTTPClient>
let client = try HTTPClient.connect(
to: "example.com",
ssl: true,
worker: worker
)
```
You can override the port by specifying a custom port using the following parameters:
```swift
// Future<HTTPClient>
let client = try HTTPClient.connect(
to: "localhost",
port: 8080,
ssl: false,
worker: worker
)
```
## Sending Requests
From here, you can send [Requests](../http/request.md). You can only send one request at a time. Sending a request before a [Response](../http/response.md) has been received has unpredictable consequences.
```swift
// Future<Response>
let response = client.flatMap { connectedClient in
let request = Request(
method: .get,
uri: "https://example.com/"
)
return connectedClient.send(request: request)
}
```

View File

@ -43,4 +43,6 @@ let package = Package(
)
```
If this is your first time adding a dependency, you should read our introduction to [Package.swift](../getting-started/spm.md).
Use `import HTTP` to access HTTP's APIs.

View File

@ -1,8 +1,8 @@
# Request
When a client connects with an HTTP Server it sends a `Request`. This HTTP request will be processed [as discussed here](../getting-started/http.md) and resolved into a [`Response`](response.md). This is the response in the http [Request/Response model](../getting-started/http.md).
When a client connects with an HTTP Server it sends a `Request`. This HTTP request will be processed [as discussed here](../concepts/http.md) and resolved into a [`Response`](response.md). This is the response in the http [Request/Response model](../concepts/http.md).
Requests consist of a [Method](method.md), [URI](uri.md) and [Headers](../web/headers.md).
Requests consist of a [Method](method.md), [URI](uri.md) and [Headers](headers.md).
Requests can optionally also contain a [Body](body.md).

View File

@ -2,7 +2,7 @@
Responders are a type capable of [responding](response.md) to a [Request](request.md).
Responders are always [async](../async/promise-future-introduction.md) by returning a `Future<Response>` by either transforming/mapping an existing future or creating it's own promise.
Responders are always [async](../async/futures.md) by returning a `Future<Response>` by either transforming/mapping an existing future or creating it's own promise.
## Implementing a static Responder

View File

@ -1,8 +1,8 @@
# HTTP Response
When a client connects with an HTTP Server it sends a [`Request`](request.md). This HTTP request will be processed [as discussed here](../getting-started/http.md) and resolved into a `Response`. This is the response in the http Request/Response model.
When a client connects with an HTTP Server it sends a [`Request`](request.md). This HTTP request will be processed [as discussed here](../concepts/http.md) and resolved into a `Response`. This is the response in the http Request/Response model.
HTTP's Response object contains a [Status](status.md), [Headers](../web/headers.md) and a [Body](body.md). Before further reading this page, you must have read and understood the previous pages for Status, Headers and Body.
HTTP's Response object contains a [Status](status.md), [Headers](headers.md) and a [Body](body.md). Before further reading this page, you must have read and understood the previous pages for Status, Headers and Body.
Responses are Extensible using the `extend` property. This allow storing additional data for use by integrating libraries.

View File

@ -44,4 +44,6 @@ let package = Package(
)
```
If this is your first time adding a dependency, you should read our introduction to [Package.swift](../getting-started/spm.md).
Use `import JWT` to access JSON Web Token's APIs.

View File

@ -7,10 +7,10 @@ To connect to Redis you can use a variety of methods. The Redis library primaril
This requires a hostname, port and [worker](../async/worker.md). The worker's DispatchQueue will be used for Redis' Socket. The hostname and port have a default. The hostname is defaulted to `localhost`, and the port to Redis' default port `6379`.
```swift
let client = try RedisClient<TCPClient>.connect(worker: worker) // Future<RedisClient<TCPClient>>
let client = try RedisClient.connect(worker: worker) // Future<RedisClient>
```
The `connect` method will return a [Future](../async/promise-future-introduction.md) containing the TCP based Redis Client.
The `connect` method will return a [Future](../async/futures.md) containing the TCP based Redis Client.
## Redis Data Types
@ -64,7 +64,7 @@ let array = RedisData.array([
## CRUD using Redis
From here on it is assumed that your client has been successfully created and is available in the variable `client` as a `RedisClient<TCPClient>`.
From here on it is assumed that your client has been successfully created and is available in the variable `client` as a `RedisClient`.
### Creating a record

View File

@ -16,4 +16,4 @@ let future = client.run(command: "GET", arguments: ["my-key"]) // Future<RedisDa
This future will contain the result as specified in the article on the redis command page or an error.
The future can be used as described in the [Async API](../async/index.md).
The future can be used as described in the [Async API](../async/package.md).

View File

@ -35,4 +35,6 @@ let package = Package(
)
```
If this is your first time adding a dependency, you should read our introduction to [Package.swift](../getting-started/spm.md).
Use `import Redis` to access Redis' APIs.

View File

@ -43,7 +43,7 @@ let notifications = client.subscribe(to: ["some-notification-channel", "other-no
If you try to use the client after subscribing, all operations will fail. These errors are usually emitted through the Future.
This stream will receive messages asynchronously from the point of `draining`. This works like [any other async stream](../async/stream-basics.md)
This stream will receive messages asynchronously from the point of `draining`. This works like [any other async stream](../async/streams.md)
Notifications consist of the channel and payload.

View File

@ -24,7 +24,7 @@ The `.get` represents the [Method](../http/method.md) you want to use. `to: "hel
For variable path components you can use [parameters](parameters.md).
The trailing closure receives a [Request](../http/request.md). The route can throw errors and needs to return a [`Future<ResponseRepresentable>`](../vapor/response.md) conforming type.
The trailing closure receives a [Request](../http/request.md). The route can throw errors and needs to return a [`Future<ResponseRepresentable>`](../http/response.md) conforming type.
## Registering a route using Vapor

View File

@ -4,8 +4,7 @@ Routing is a library containing all Routing related APIs.
### Index
- [Aync routing](async.md)
- [Sync routing](sync.md)
- [Basics](basics.md)
- [Route Parameters](parameters.md)
- [Route](route.md)
- [TrieRouter](router.md)
@ -38,4 +37,6 @@ let package = Package(
)
```
If this is your first time adding a dependency, you should read our introduction to [Package.swift](../getting-started/spm.md).
Use `import Routing` to access Routing's APIs.

View File

@ -6,17 +6,17 @@ Router is a protocol that you can conform your own routers to.
First, create a [Route](route.md) using a [Method](../http/method.md), path and a responder.
The following example shows an [async route](async.md) with a constant path.
The following example shows a route with a constant path.
```swift
let responder = BasicAsyncResponder { request in
return Future("Hello world")
return "Hello world"
}
let route = Route(method: .get, path: [.constant("hello"), .constant("world")], responder: responder)
```
The following example shows a [synchronous route](sync.md) with a [Parameter](parameters.md):
The following example shows a with a [Parameter](parameters.md):
```swift
let responder = BasicSyncResponder { request in

View File

@ -36,7 +36,7 @@ Both are (partially) prevented by default using the `PeerValidator` which is par
### How PeerValidator works
As part of Vapor 3's design goals, all notification-like I/O is implemented using [Streams](../async/streams-basics.md). This also includes the [TCP Server](../sockets/tcp-server.md). The TCP server is seen as a stream of clients/peers that are accepted and then sent to the client. It has a hook called `willAccept`. This closure's input is a `TCPClient`, and the output is a `Bool`. If the returned boolean is `true`, the peer will be accepted where `false` will deny the peer and will close the connection.
As part of Vapor 3's design goals, all notification-like I/O is implemented using [Streams](../async/streams.md). This also includes the [TCP Server](../sockets/tcp-server.md). The TCP server is seen as a stream of clients/peers that are accepted and then sent to the client. It has a hook called `willAccept`. This closure's input is a `TCPClient`, and the output is a `Bool`. If the returned boolean is `true`, the peer will be accepted where `false` will deny the peer and will close the connection.
`PeerValidator` hooks into this capability by looking the peer's address up in it's cache and keeps track of the amount of connections this peer has currently opened to this server. If the counter exceeds a threshold as specified in the `PeerValidator` initializer, the connection will be rejected.

View File

@ -1,12 +1,13 @@
# Security
Security is a critical part of any software and comes in many forms. Denial of Service attacks and code injection are only the tip of the iceberg.
It is always important to understand some basics of security. Vapor 3 is designed to be secure by default, but anything you build on top of Vapor 3 might not be.
Security is a critical part of any software and comes in many forms.
It is always important to understand some basics of security. Vapor 3 is designed to be secure by default and has many tools to support building secure applications.
### Recommended information
- [Password management](../crypto/passwords.md)
- [JWT (Session) Tokens](../jwt/index.md)
- [JWT (Session) Tokens](../jwt/package.md)
### Additional information

View File

@ -1,6 +1,6 @@
## Configure
You configure the application in the [`configure.swift`](structure.md#configureswift) file. Here you can
You configure the application in the [`configure.swift`](structure.md) file. Here you can
register your own services, or override the ones provided by default.
```swift
@ -21,4 +21,3 @@ let foo = try app.make(FooService.self)
```
### Providers

View File

@ -36,4 +36,6 @@ let package = Package(
)
```
If this is your first time adding a dependency, you should read our introduction to [Package.swift](../getting-started/spm.md).
Use `import Sockets` to access Sockets's APIs.

View File

@ -34,7 +34,7 @@ let client = TCPClient(socket: socket, worker: worker)
Now that your socket is connected you can start communicating. First, you'll need to start by setting up the handlers for incoming data.
Since `TCPClient` is a stream, you can use [the introduction](../async/streams-introduction.md) and [the basics](../async/streams-basics.md) of streams for reading the socket's output (incoming data).
Since `TCPClient` is a stream, you can use [the introduction](../async/streams.md) of streams for reading the socket's output (incoming data).
Sending data is done through the `inputStream` function.

0
3.0/docs/tls/client.md Normal file
View File

31
3.0/docs/tls/package.md Normal file
View File

@ -0,0 +1,31 @@
# TLS
The TLS packages allow writing TLS/SSL clients and servers.
### Index
- [Client](client.md)
## Package.swift
To include it in your package, add the following to your `Package.swift` file.
```swift
// swift-tools-version:4.0
import PackageDescription
let package = Package(
name: "Project",
dependencies: [
...
.package(url: "https://github.com/vapor/mysql.git", .upToNextMajor(from: "3.0.0")),
],
targets: [
.target(name: "Project", dependencies: ["MySQL", ... ])
]
)
```
If this is your first time adding a dependency, you should read our introduction to [Package.swift](../getting-started/spm.md).
Use `import MySQL` to access MySQL' APIs.

View File

@ -6,10 +6,13 @@ WebSocket clients work the same on the client side as the [server side](server.m
WebSockets require an [URI](../http/uri.md) to connect to and a [Worker](../async/worker.md) to run on.
!!! warning
Vapor does not retain the WebSocket. It is the responsibility of the user to keep the WebSocket active by means of strong references and pings.
```swift
let worker: Worker = ...
let futureWebSocket: Future<WebSocket> = try WebSocket.connect(to: "ws://localhost/path", queue: queue)
let futureWebSocket: Future<WebSocket> = try WebSocket.connect(to: "ws://localhost/path", worker: worker)
```
## Using websockets

View File

@ -1 +0,0 @@
# TODO

View File

@ -1,5 +1,51 @@
# WebSocket Upgrading
Servers can upgrade HTTP requests to a WebSocket if the client indicated an upgrade.
## Determining an upgrade
You will need to `import WebSocket` for the upgrade functionality.
Create a basic `GET` route. WebSockets always connect with a GET [method](../http/method.md).
```swift
import WebSocket
drop.get("api/v1/websocket") { req in
let shouldUpgrade = WebSocket.shouldUpgrade(for: req)
}
```
## Upgrading the connection
The WebSocket library can generate an appropriate [Response](../http/response.md) for you. You can return this in your route.
You will be able to set a handler inside `onUpgrade` in which a websocket will be returned after completion of the upgrade.
!!! warning
Vapor does not retain the WebSocket. It is the responsibility of the user to keep the WebSocket active by means of strong references and pings.
```swift
if shouldUpgrade {
let response = try WebSocket.upgradeResponse(for: req)
response.onUpgrade = { client in
let websocket = WebSocket(client: client)
websocket.onText { text in
let rev = String(text.reversed())
websocket.send(rev)
}
websocket.onBinary { buffer in
websocket.send(buffer)
}
}
return response
}
```
## Using websockets
WebSockets are interacted with using [binary streams](binary-stream.md) or [text streams](text-stream.md).
All other information about websockets [is defined here.](websocket.md)

View File

@ -1,6 +1,8 @@
# WebSocket
WebSockets are a type of connection that can be instantiated by upgrading an existing HTTP/1 connection. They're used to dispatch notifications and communicate real-time binary and textual Data.
WebSockets are a type of connection that can be instantiated by upgrading an existing HTTP/1.1 connection. They're used to dispatch notifications and communicate real-time binary and textual Data.
Vapor 3 supports both WebSocket [Client](client.md) and [Server](upgrade.md).
## Using websockets

View File

@ -14,6 +14,7 @@ pages:
- 'Folder Structure': 'getting-started/structure.md'
- 'Application': 'getting-started/application.md'
- 'Controllers': 'getting-started/controllers.md'
- 'JSON': 'getting-started/json.md'
- 'Routing': 'getting-started/routing.md'
- 'Content': 'getting-started/content.md'
- 'Futures': 'getting-started/futures.md'
@ -26,12 +27,11 @@ pages:
- 'HTTP': 'concepts/http.md'
- 'Async':
- 'Package': 'async/package.md'
- 'Future Basics': 'async/futures-basics.md'
- 'Streams Introduction': 'async/streams-introduction.md'
- 'Stream Basics': 'async/streams-basics.md'
- 'Futures': 'async/futures.md'
- 'Streams': 'async/streams.md'
- 'Worker': 'async/worker.md'
- 'HTTP':
- 'Index': 'http/index.md'
- 'Package': 'http/package.md'
- 'Body': 'http/body.md'
- 'Client': 'http/client.md'
- 'Cookies': 'http/cookies.md'
@ -43,6 +43,7 @@ pages:
- 'Response': 'http/response.md'
- 'Responder': 'http/responder.md'
- 'Status codes': 'http/status.md'
- 'Streaming Body': 'http/body-stream.md'
- 'URI': 'http/uri.md'
- 'Fluent':
- 'Getting Started':
@ -65,11 +66,15 @@ pages:
- 'Overview': 'databases/sqlite/overview.md'
- 'MySQL':
- 'Package': 'databases/mysql/package.md'
- 'Index': 'databases/mysql/index.md'
- 'Setup': 'databases/mysql/setup.md'
- 'Basics': 'databases/mysql/basics.md'
- 'Prepared Statements': 'databases/mysql/prepared-statements.md'
- 'PostgreSQL':
- 'Package': 'databases/postgres/package.md'
- 'Setup': 'databases/postgres/setup.md'
- 'Basics': 'databases/postgres/basics.md'
- 'Redis':
- 'Index': 'redis/index.md'
- 'Package': 'redis/package.md'
- 'Basics': 'redis/basics.md'
- 'Custom commands': 'redis/custom-commands.md'
- 'Publish and Subscribe': 'redis/pub-sub.md'
@ -79,9 +84,9 @@ pages:
- 'Binary': 'websocket/binary-stream.md'
- 'Text': 'websocket/text-stream.md'
- 'Client': 'websocket/client.md'
- 'Server': 'websocket/server.md'
- 'Server Upgrades': 'websocket/upgrade.md'
- 'Routing':
- 'Package': 'routing/package.md'
- 'Basics': 'routing/basics.md'
- 'Parameters': 'routing/parameters.md'
- 'Route': 'routing/route.md'
@ -89,17 +94,23 @@ pages:
- 'Services':
- 'Getting Started': 'services/getting-started.md'
- 'JWT':
- 'Package': 'jwt/package.md'
- 'Signed Tokens': 'jwt/jws.md'
- 'Crypto':
- 'Index': 'crypto/index.md'
- 'Package': 'crypto/package.md'
- 'Base64': 'crypto/base64.md'
- 'Hashes': 'crypto/hash.md'
- 'Message authentication': 'crypto/mac.md'
- 'Password hashing': 'crypto/passwords.md'
- 'Random': 'crypto/random.md'
- 'TLS':
- 'Package': 'tls/package.md'
- 'Client': 'tls/client.md'
- 'Sockets':
- 'Index': 'sockets/index.md'
- 'Package': 'sockets/package.md'
- 'TCP Client': 'sockets/tcp-client.md'
- 'TCP Server': 'sockets/tcp-server.md'
- 'TCP Socket': 'sockets/tcp-socket.md'
- 'Testing':
- 'Getting Started': 'testing/getting-started.md'
- 'Security':
@ -122,7 +133,7 @@ markdown_extensions:
- meta
- toc(permalink=true)
theme:
theme:
name: 'material'
palette:
primary: 'blue'