💧 A server-side Swift web framework.

Overview

Vapor

Documentation Team Chat MIT License Continuous Integration Swift 5.2 Twitter


Vapor is a web framework for Swift. It provides a beautifully expressive and easy to use foundation for your next website, API, or cloud project.

Take a look at some of the awesome stuff created with Vapor.

💧 Community

Join the welcoming community of fellow Vapor developers on Discord.

🚀 Contributing

To contribute a feature or idea to Vapor, create an issue explaining your idea or bring it up on Discord.

If you find a bug, please create an issue.

If you find a security vulnerability, please contact [email protected] as soon as possible.

💛 Sponsors

Support Vapor's development by becoming a sponsor.

Monstarlab Skelpo Transeo Gwynne Raskind Joannis Orlandos Kyle Browning Jari omrd

💚 Backers

Support Vapor's development by becoming a backer.

analytics

Comments
  • Vapor 3 async changes before alpha

    Vapor 3 async changes before alpha

    Discussion topics:

    • Stream notifications for errors
    • Stream close notification
    • Cascading notifications throughout streams (up the stream and/or down the stream)
    • Multiple future types
    • Lightweight versus easy to use futures
    • Timing out promises efficiently
    • Helpers for managing tasks on the event loop

    These topics explained from top to bottom:

    Error stream notifications

    Currently, stream notifications are handled by registering a closure. You can set a closure on the receiving/draining end of the stream, the result. But anywhere else will break the error chain unless explicitly passed on. This also prevents errors from cascading back. An error in TCP will trigger later in the process of handling the HTTP request, but the other way around doesn't work (Database errors cascading to the HTTP layer). That is still a useful feature, for example, for throwing 500 pages by default.

    let requests = socket.stream(to: HTTPParser)
    let responses = request.stream(to: myApp)
    responses.stream(to: socket)
    
    responses.catch { error in
      print("500 error")
    }
    
    // At this moment, the socket does not find any errors, nor do the parser serializer
    

    Stream close notifications

    Closing streams is handled the same way, currently. We should decide if we want to keep that this way.

    // WebSocket.swift
    socket.onClose = {
      websocket.sendCloseMessage()
    }
    
    // WebSocket+SSL.swift
    socket.onClose = {
      tls.deinitialize()
    }
    
    // Chat.swift
    socket.onClose = {
      friends.notifyOfflineStatus()
    }
    

    Cascading notifications

    As discussed in the error stream notification, cascading these errors can interrupt a process causing no additional unnecessary work to be executed. But this must be implemented with care. Interrupting in the middle of a registration form (adding the database entry, sending the email and 2 other ops) can prevent some ops from executing whilst others did execute.

    There should be sensible helpers for that.

    drop.get {
      return database.all(User.self, in: "SELECT * FROM users")
    }
    
    class ErrorMiddleware: Middleware {
      func handle(request: Request, chainingTo responder: Responder) -> Future<Response> {
        let promise = Promise<Response>()
    
        return responder.respond(to: request).do(promise.complete).catch {
            promise.complete(Response(status: 500))
        }
    
        return promise.future
      }
    }
    

    Multiple future types

    As of right now, futures are really easy to use and sensible. They conform to FutureType allowing extensibility and more (generic) integration. One such example is the conformance of Response to FutureType, effectively making Future(Response(...)) unnecessary bloat.

    Pseudocode Problem

    let users = database.all(User.self, in: "SELECT * FROM users LIMIT 100") // [User].count == 100
    

    If User is a struct of 10KB, [User] will be copied around 2 times with an overhead. Once to the future, once from the future to the handler. This copies around 2MB of extra data.

    Timing out promises

    Promises can be timed out whilst blocking, currently. But you cannot easily dispatch a task like this without manually interfacing with the DispatchQueue in a worker's eventloop. Having to block for waiting is not sensible, and against the async design. If an HTTP request is waiting for >30 and especially >60 seconds, you're pretty much guaranteed to have timed out. And if any client-side operation done by Vapor (such as calling stripe) times out after X seconds, we shouldn't keep the promise unfulfilled in memory.

    Use case

    // Throws a timeout after 5 seconds of waiting for a result
    return websocket.connect(to: "ws://localhost:8080/").map(timeout: .seconds(5)) { websocket in
      ...
    }
    

    Helpers for managing tasks

    Finally, playing into this last topic. Many people are using websockets or other features that require cron-jobs. Some people may want to send a notification every minute for new chat messages, regardless of circumstances. Or send a ping to the client every 15 seconds to keep the connection open. They're very common use cases and should be covered from the start, especially with async.

    Use case

    websocket.worker.every(.seconds(15)) {
      // keep the connection alive
      websocket.ping()
    }
    
    opened by Joannis 34
  • Push Notification for iOS and Web

    Push Notification for iOS and Web

    Notification Service Extension is an interesting feature that can send plaintext or rich push notifications to iOS.

    Is it available on Linux and macOS server side beside the pubsub method?

    opened by wildfiremedia 31
  • Server doesn't stop succesfully in CLI after opening WebSocket connections

    Server doesn't stop succesfully in CLI after opening WebSocket connections

    Steps to reproduce

    Create a simple Vapor app that opens WebSocket connections:

    import Vapor
    
    extension Application {
      func configure(
        onWebSocketOpen: @escaping (WebSocket) -> (),
        onWebSocketClose: @escaping (WebSocket) -> ()
      ) {
        webSocket("watcher") { _, ws in
          onWebSocketOpen(ws)
          ws.onClose.whenComplete { _ in onWebSocketClose(ws) }
        }
      }
    }
    
    final class Server {
      private struct Lifecycle: LifecycleHandler {
        weak var server: Server?
    
        /// Closes all active WebSocket connections
        func shutdown(_ app: Application) {
          try! EventLoopFuture<()>.andAllSucceed(
            server?.connections.map { $0.close() } ?? [],
            on: app.eventLoopGroup.next()
          ).wait()
          print("shutdown finished")
        }
      }
    
      private var connections = Set<WebSocket>()
      private let app: Application
    
      init() throws {
        var env = Environment(name: "development", arguments: ["vapor"])
        try LoggingSystem.bootstrap(from: &env)
        app = Application(env)
        app.configure(
          onWebSocketOpen: { [weak self] in
            self?.connections.insert($0)
          },
          onWebSocketClose: { [weak self] in
            self?.connections.remove($0)
          }
        )
      }
    
      /// Blocking function that starts the HTTP server
      func run() throws {
        defer { app.shutdown() }
        app.lifecycle.use(Lifecycle(server: self))
        try app.run()
      }
    }
    
    try Server().run()
    

    Expected behavior

    When this server app is started from command-line and any WebSocket connections are established, the server process can stopped quickly with Ctrl-C.

    Actual behavior

    The server process can't be stopped quickly with Ctrl-C. It hangs for about 5 seconds or so and then raises the following error:

    shutdown finished
    [ ERROR ] Could not stop HTTP server: Abort.500: Server stop took too long.
    ERROR: Cannot schedule tasks on an EventLoop that has already shut down. This will be upgraded to a forced crash in future SwiftNIO versions.
    

    Environment

    These are the details of revelant package dependencies from Package.resolved:

          {
            "package": "vapor",
            "repositoryURL": "https://github.com/vapor/vapor.git",
            "state": {
              "branch": null,
              "revision": "88293674e2ea017691c56af20d0938dfff7ece04",
              "version": "4.27.0"
            }
          },
          {
            "package": "websocket-kit",
            "repositoryURL": "https://github.com/vapor/websocket-kit.git",
            "state": {
              "branch": null,
              "revision": "b0736014be634475dac4c23843811257d86dcdc1",
              "version": "2.1.1"
            }
          }
    
    • Vapor Framework version: 4.27.0, but this was reproducible in versions as old as 4.5.0
    • Vapor Toolbox version: not installed
    • OS version: macOS 10.15.6
    bug 
    opened by MaxDesiatov 30
  • Make number of threads in app.threadPool configurable

    Make number of threads in app.threadPool configurable

    The Vapor 4.0 documentation of "Async" suggest to run any blocking work in the background, such that several clients won't block each other. I've set up a new project from scratch and registered two routes:

    app.get("hello-again") { req -> String in
        print("hello-again requested")
    
        return "Hello, world, again!"
    }
    
    app.get("hello") { req -> EventLoopFuture<String> in
        print("hello requested")
        let promise = req.eventLoop.makePromise(of: Void.self)
    
        req.application.threadPool.submit { _ in
            sleep(5)
            print("(did some work)")
    
            promise.succeed(Void())
        }
    
        return promise.futureResult.transform(to: "Hello, world!")
    }
    

    If I request GET/hello and immediately GET/hello-again, GET/hello-again is served directly as expected. But if I request GET/hello and than immediately a second GET/hello, the second request waits for the first one to complete. Thus, the output in my console is:

    [ INFO ] GET /hello
    hello requested
    (did some work)
    [ INFO ] GET /hello
    hello requested
    (did some work)
    

    But from the documentation I would have rather expected:

    [ INFO ] GET /hello
    hello requested
    [ INFO ] GET /hello
    hello requested
    (did some work)
    (did some work)
    

    What am I missing?

    enhancement 
    opened by devnikkin 30
  • Resource temporarily unavailable after 30 seconds

    Resource temporarily unavailable after 30 seconds

    Another socket issue that occurs after 30 seconds. It happens on Ubuntu 16.04 with Swift 3.0.1 and Vapor 1.1.10 on an EC2 instance running behind an Elastic Load Balancer:

    Server error: dispatch(Transport.StreamError.receive("There was a problem while receiving data.", Socket failed with code 11 ("Resource temporarily unavailable") [readFailed] "Unknown error"))

    Is it important or can we silence that error, too?

    opened by skreutzberger 29
  • Injecting dependencies into ResourceControllers

    Injecting dependencies into ResourceControllers

    How am I supposed to do this? Vapor requires me to register a type as a controller, not an object. The Controller protocol dictates this type should have a default initializer. Vapor then uses this initializer to create an object right before it calls one of the index, store, ... methods on it. This means I can't inject anything into the object because I can't initialize it.

    Wouldn't it be better to register an object as a controller, instead of a type? That's the way Blackfish does it, meaning I can inject my dependencies (model) via the initializer as follows (Blackfish code):

    app.use(path: "/api/students", controller: StudentsApiController(model: model))
    
    enhancement 
    opened by svanimpe 29
  • Couldn't install vapor with homebrew

    Couldn't install vapor with homebrew "libssl.1.0.0.dylib not loaded"

    Steps to reproduce

    Tried installing vapor with homebrew with the official docs. Running all commands in zsh:

    brew tap vapor/tap
    brew install vapor/tap/vapor
    vapor --help
    

    Expected behavior

    vapor --help running properly without errors

    Actual behavior

    Screenshot 2019-11-24 at 01 09 41

    Environment

    • Vapor Framework version:
    • Vapor Toolbox version: /usr/local/Cellar/vapor/3.1.10
    • OS version: MacOS 10.14.6
    bug 
    opened by lil5 28
  • Xcode - no such module Vapor

    Xcode - no such module Vapor

    Hello, when I was trying to set up a small a vapor project I ran into an issue with the xcode project that gets created. I ran vapor new dragons, cd dragons, vapor xcode. When the xcode project opened up, I added a new file "Battles.swift" to the models folder, and edited the main.swift file to code in the image below. When I tried to build the app via xcode, I got this error "No such module 'Vapor'. " However, I was still able to build and run the vapor project via the command line. Not quite sure what the problem is. It might just be my machine. Any help would be appreciated. Thanks

    opened by shayneptorres 25
  • tls server

    tls server

    • Vapor Version: 2.0
    • OS: macOS Sierra
    • OS Version: 10.12.6
    • Vapor Toolbox Version: 2.0.4

    the ServerSocket.swift in TLS module the function accept()->Self return self i changed it like this at the end of the function

        let c = InternetSocket(client, context)
        c.cSSL = ssl
        return c
    

    but then server always report error like this

    Server error: dispatch(Transport Layer Security Error: Undefined error: 0
    
    Identifier: TLS.TLSError.SSL_read (0))
    GET /info
    Server error: dispatch(Transport Layer Security Error: The operation did not complete; the same TLS/SSL I/O function should be called again later.
    
    Identifier: TLS.TLSError.SSL_read (-1))
    

    so it can't keep connect alive

    bug 
    opened by vcxk 21
  • localization

    localization

    I18n support

    Introduction

    I don't think this feature needs an introduction actually; all of us know what internationalization is and why it's important for Vapor to support it. Anyway, in case this paragraph is mandatory: Vapor needs to support internationalization in order to become the greatest Swift based web framework. 😄

    Motivation

    One of the key features of every professional web framework is the I18n support. Right now Vapor lacks this feature and I think adding this we can make Vapor to be one step closer to the level of others web frameworks around the web.

    Proposed solution

    I believe the best place for the dictionaries is under the Resources/Locales folder. The structure to be used to separate each language is quite simple: a folder with the language code (es, en, jp, etc.) and as much files as the developer wants to organise its dictionaries under those folders.

    As Vapor has JSON format config files already, I propose to use this file format to store the dictionaries, so we can read them natively.

    An I18n class is going to take care about locales and site language switching.

    Code snippets

    I propose the static func t(_: String) method to do the translation of the key passed as argument.

    I18n.t('foo.bar')
    

    As you can see it's quite straightforward. That method just search for the key in the corresponding locale folder.

    The static func changeLanguage(_: String) method will change the current selected locale to the language code passed as parameter in the call.

    I18n.changeLanguage('en')
    

    Impact

    As I18n class is quite atomic I don't expect any impact to Vapor applications.

    Alternatives considered

    I think about using yaml instead json due to the vast use of yaml format in web frameworks but json is natively supported by Swift so Vapor get less dependencies to third party libraries this way.

    Decision (For Moderator Use)

    On May 26th, 2016, the community decided to approve this proposal.

    enhancement 
    opened by kerunaru 21
  • improve git functionality for `vapor new`

    improve git functionality for `vapor new`

    Currently every project created with vapor new is based on the vapor example project. That results in boilerplate code which is more suitable for demonstration purposes than actual starting ground for new projects. In example the mustache example functionality and dependency likely is going to be removed every time.

    The solution would be to introduce the example argument for the vapor command line application, which creates an new project based on the example. In parallel the new argument will be modified to create a barebone project with just the minimal content necessary to get going (requires a new repository as a source). In addition there is an option (argument --template, other suggestions welcome) to specify the repository to use as the template for creating a new project.

    This enables new starting points when creating new projects.

    Example

    vapor new Vaporizer
    

    Creates a new project in the Vaporizer directory based on the minimal, default project template. Invokes dependency installation and build automatically (vapor build).

    vapor new Vaporizer --template=https://github.com/alomvar/vapor-blog-template
    

    Creates a new project in the Vaporizer directory based on the provided project template (a Git repository). Invokes dependency installation and build automatically (vapor build).

    vapor example
    

    Creates a new project in VaporExample based on the official Vapor example project. Invokes dependency installation and build automatically (vapor build) and runs it afterwards.

    enhancement 
    opened by peterthomashorn 21
  • Response.Body(stream:) is not async/await ready

    Response.Body(stream:) is not async/await ready

    Describe the bug

    Vapor has the option for chunk streaming of the body.

    Response(body: .init(stream: { writer in
    })
    

    People may want to use async functions to get the results which they will write in the response Because this function doesn't accept async code, it needs to be wrapped inside Task. But for writing buffer Vapor uses writeAndFlush function from NIO. This function calls eventLoop.assertInEventLoop and it asserts self.inEventLoop This returns true if the current NIOThread is the same as the NIOThread that is tied to this EventLoop. Although code builds successfully, at runtime it crashes through Task thread and event loop thread are different

    bug 
    opened by Higher08 0
  • Add mocked client + subclass of XCTestCase

    Add mocked client + subclass of XCTestCase

    I have created a spy client, in order to stub the response and also being able to verify the request.

    To reduce boilerplate code, I created a XCTVaporTestCase. You don't need to configure the application every time. And tearing it down. All is being handled by XCTVaporTestCase.

    You just simply do this:

    final class ExampleTests: XCTVaporTestCase {
         func test_example() throws {
               try client.stubResponse(httpStatus: .ok)
         }
    }
    
    opened by Kithin 0
  • WebSocket Connects Before Server Can Configure It?

    WebSocket Connects Before Server Can Configure It?

    Bug Description

    I'm probably holding it wrong: It seems a server can not configure a new WebSocket before Vapor establishes the WebSocket connection. This means the client requesting the connection receives it before the server even had a chance to configure the WebSocket, i.e. before the WebSocket even has its input handler set.

    This has led to a flaky and hard to find (infuriating) bug, where the client's first sent data sometimes got totally lost because it arrived 15 milliseconds too early. My preliminary workaround is to let the client wait 100 ms to increase the probability that the server has its stuff together when the client actually uses the WebSocket connection.

    To Reproduce

    Establish a WebSocket connection between client and server and let the client send data immediately after its successful WebSocket request.

    Server side:

    myRouteBuilder.webSocket("websocket") { request in
        HTTPHeaders()
    } onUpgrade: { request, newWebsocket in
        // 🐢 Too late. Vapor has already established the connection, so the client has already sent data.
        newWebsocket.onBinary { ws, bufferedBytesFromWebSocket in
            // do somethin with the incoming data
        }
    }
    

    The workaround in the client, where it waits before sending the first data:

    let server = try await createServer(forLanguageNamed: codebase.languageName)
    try await Task.sleep(nanoseconds: 100_000_000)
    _ = try await server.request(.initialize(folder: codebase.folder))
    

    Expected behavior

    I expect to be able to configure the WebSocket before it is offered to the client. Either onUpgrade would be called before the connection is finally established with the client, or there would be some other means to get a hold of the WebSocket – ideally even before deciding on whether to upgrade the connection, because that decision can depend on whether the WebSocket can even be configured. I honestly (naively) would expect that in shouldUpgrade I can create and return the WebSocket that is then passed to onUpgrade.

    Also, I would expect the documentation of Vapor WebSockets to reflect the current implementation – for example changes made in April 2021 and in October 2020.

    Environment

    • macOS 13.1
    • Xcode 14.2
    • Vapor 4.67.5
    • The client uses URLSessionWebSocketTask
    bug 
    opened by flowtoolz 3
  • HTTPServerErrorHandler: Improve Error Handling for HTTPParserError

    HTTPServerErrorHandler: Improve Error Handling for HTTPParserError

    Invalid HTTP previously just closed the connection, but did not actually handle the errors.

    This PR uses an adapted version of https://github.com/apple/swift-nio/blob/main/Sources/NIOHTTP1/HTTPServerProtocolErrorHandler.swift to properly catch exceptions if they occur.

    Closes: https://github.com/vapor/vapor/issues/2921

    opened by fred-sch 1
  • Special characters in URL lead to empty response from server

    Special characters in URL lead to empty response from server

    Describe the bug

    Invalid URLs containing not %-encoded special characters lead to an empty response and a hang when shutting down the application.

    To Reproduce

    1. run vapor new helloworld-test
    2. cd helloworld-test
    3. swift run --log trace
    4. in another terminal execute: curl "http://127.0.0.1:8080/°" --http1.1 --verbose

    This will give the following behavior in the curl query:

    ⚡55% ✖  curl "http://127.0.0.1:8080/°" --http1.1 --verbose
    *   Trying 127.0.0.1:8080...
    * Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
    > GET /° HTTP/1.1
    > Host: 127.0.0.1:8080
    > User-Agent: curl/7.78.0
    > Accept: */*
    >
    * Empty reply from server
    * Closing connection 0
    curl: (52) Empty reply from server
    

    and the following in the trace log

    ⚡53% ➜ .build/debug/Run --log trace
    [ codes.vapor.application ] [ DEBUG ] Could not load .env.development file: open(file:oFlag:mode:): No such file or directory (errno: 2) (Vapor/DotEnv.swift:120)
    [ codes.vapor.application ] [ TRACE ] Running on shared EventLoopGroup. Not shutting down EventLoopGroup. (Vapor/DotEnv.swift:106)
    [ codes.vapor.application ] [ DEBUG ] Could not load .env file: open(file:oFlag:mode:): No such file or directory (errno: 2) (Vapor/DotEnv.swift:120)
    [ codes.vapor.application ] [ TRACE ] Running on shared EventLoopGroup. Not shutting down EventLoopGroup. (Vapor/DotEnv.swift:106)
    [ codes.vapor.application ] [ TRACE ] Running on shared EventLoopGroup. Not shutting down EventLoopGroup. (Vapor/DotEnv.swift:59)
    [ codes.vapor.application ] [ NOTICE ] Server starting on http://127.0.0.1:8080 (Vapor/HTTPServer.swift:296)
    [ codes.vapor.application ] [ DEBUG ] Unhandled HTTP server error: invalid URL (Vapor/HTTPServer.swift:459)
    [ codes.vapor.application ] [ TRACE ] Unhandled user event: outputClosed (Vapor/HTTPServerRequestDecoder.swift:217)
    ^C
    [ codes.vapor.application ] [ DEBUG ] Application shutting down (Vapor/Application.swift:136)
    [ codes.vapor.application ] [ TRACE ] Shutting down providers (Vapor/Application.swift:138)
    [ codes.vapor.application ] [ TRACE ] Clearing Application storage (Vapor/Application.swift:142)
    [ codes.vapor.application ] [ DEBUG ] Requesting HTTP server shutdown (Vapor/HTTPServer.swift:314)
    [ codes.vapor.application ] [ ERROR ] Could not stop HTTP server: Abort.500: Server stop took too long. (Vapor/HTTPServer.swift:318)
    [ codes.vapor.application ] [ DEBUG ] HTTP server shutting down (Vapor/HTTPServer.swift:320)
    [ codes.vapor.application ] [ TRACE ] Shutting down EventLoopGroup (Vapor/Application.swift:150)
    [ codes.vapor.application ] [ TRACE ] Closing keep-alive HTTP connection since server is going away (Vapor/HTTPServerRequestDecoder.swift:210)
    ERROR: Cannot schedule tasks on an EventLoop that has already shut down. This will be upgraded to a forced crash in future SwiftNIO versions.
    [ codes.vapor.application ] [ TRACE ] Application shutdown complete (Vapor/Application.swift:159)
    

    Expected behavior

    • Return a valid response, possibly 400 for bad request because of invalid URL
    • No effect on server shutdown.

    Environment

    • Vapor Framework version: 4.67.4
    • Vapor Toolbox version: 18.6.0
    • OS version: Mac OS 13.0.1

    Additional context

    With TLS enabled, no crash is produced.

    ⚡53% ✖  curl "https://127.0.0.1:8090/°" --insecure --verbose
    *   Trying 127.0.0.1:8090...
    * Connected to 127.0.0.1 (127.0.0.1) port 8090 (#0)
    * ALPN, offering h2
    * ALPN, offering http/1.1
    * successfully set certificate verify locations:
    *  CAfile: /Users/fredericschwarz/opt/anaconda3/ssl/cacert.pem
    *  CApath: none
    * TLSv1.3 (OUT), TLS handshake, Client hello (1):
    * TLSv1.3 (IN), TLS handshake, Server hello (2):
    * TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
    * TLSv1.3 (IN), TLS handshake, Certificate (11):
    * TLSv1.3 (IN), TLS handshake, CERT verify (15):
    * TLSv1.3 (IN), TLS handshake, Finished (20):
    * TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
    * TLSv1.3 (OUT), TLS handshake, Finished (20):
    * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
    * ALPN, server accepted to use h2
    * Server certificate:
    *  subject: CN=localhost
    *  start date: Aug  9 18:44:22 2022 GMT
    *  expire date: Aug 10 18:44:22 2023 GMT
    *  issuer: CN=CustomCA
    *  SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
    * Using HTTP2, server supports multiplexing
    * Connection state changed (HTTP/2 confirmed)
    * Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
    * Using Stream ID: 1 (easy handle 0x7f896e013000)
    > GET /° HTTP/2
    > Host: 127.0.0.1:8090
    > user-agent: curl/7.78.0
    > accept: */*
    >
    * TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
    * TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
    * old SSL session ID is stale, removing
    < HTTP/2 404
    < content-length: 35
    < content-type: application/json; charset=utf-8
    < date: Fri, 25 Nov 2022 18:26:31 GMT
    <
    * Connection #0 to host 127.0.0.1 left intact
    {"error":true,"reason":"Not Found"}%
    
    [ codes.vapor.application ] [ DEBUG ] Could not load .env.development file: open(file:oFlag:mode:): No such file or directory (errno: 2) (Vapor/Utilities/DotEnv.swift:120)
    [ codes.vapor.application ] [ TRACE ] Running on shared EventLoopGroup. Not shutting down EventLoopGroup. (Vapor/Utilities/DotEnv.swift:106)
    [ codes.vapor.application ] [ DEBUG ] Could not load .env file: open(file:oFlag:mode:): No such file or directory (errno: 2) (Vapor/Utilities/DotEnv.swift:120)
    [ codes.vapor.application ] [ TRACE ] Running on shared EventLoopGroup. Not shutting down EventLoopGroup. (Vapor/Utilities/DotEnv.swift:106)
    [ codes.vapor.application ] [ TRACE ] Running on shared EventLoopGroup. Not shutting down EventLoopGroup. (Vapor/Utilities/DotEnv.swift:59)
    [ codes.vapor.http-server ] [ NOTICE ] Server starting on https://127.0.0.1:8090 (Vapor/HTTP/Server/HTTPServer.swift:277)
    [ codes.vapor.application ] [ TRACE ] Decoded HTTP part: head: HTTPRequestHead { method: GET, uri: "/°", version: HTTP/2.0, headers: [("host", "127.0.0.1:8090"), ("user-agent", "curl/7.78.0"), ("accept", "*/*")] } (Vapor/HTTP/Server/HTTPServerRequestDecoder.swift:34)
    [ codes.vapor.application ] [ TRACE ] Decoded HTTP part: end (Vapor/HTTP/Server/HTTPServerRequestDecoder.swift:34)
    [ codes.vapor.application ] [ INFO ] GET /° [request-id: 07C70599-FAEE-4480-9118-713F5F77F67B] (Vapor/Middleware/RouteLoggingMiddleware.swift:13)
    [ codes.vapor.application ] [ DEBUG ] RouteNotFound.404: Not Found
    Stack trace:
    0  Run Vapor.(NotFoundResponder in _76D94FECBE6158E9EC6FBADA9020BBE5).respond(to: Vapor.Request) -> NIOCore.EventLoopFuture<Vapor.Response>
    1  Run protocol witness for Vapor.Responder.respond(to: Vapor.Request) -> NIOCore.EventLoopFuture<Vapor.Response> in conformance Vapor.(NotFoundResponder in _76D94FECBE6158E9EC6FBADA9020BBE5) : Vapor.Responder in Vapor
    2  Run Vapor.ErrorMiddleware.respond(to: Vapor.Request, chainingTo: Vapor.Responder) -> NIOCore.EventLoopFuture<Vapor.Response>
    3  Run protocol witness for Vapor.Middleware.respond(to: Vapor.Request, chainingTo: Vapor.Responder) -> NIOCore.EventLoopFuture<Vapor.Response> in conformance Vapor.ErrorMiddleware : Vapor.Middleware in Vapor
    4  Run Vapor.(HTTPMiddlewareResponder in _488FE00728E24C900504670C08744C50).respond(to: Vapor.Request) -> NIOCore.EventLoopFuture<Vapor.Response>
    5  Run protocol witness for Vapor.Responder.respond(to: Vapor.Request) -> NIOCore.EventLoopFuture<Vapor.Response> in conformance Vapor.(HTTPMiddlewareResponder in _488FE00728E24C900504670C08744C50) : Vapor.Responder in Vapor
    6  Run Vapor.RouteLoggingMiddleware.respond(to: Vapor.Request, chainingTo: Vapor.Responder) -> NIOCore.EventLoopFuture<Vapor.Response>
    7  Run protocol witness for Vapor.Middleware.respond(to: Vapor.Request, chainingTo: Vapor.Responder) -> NIOCore.EventLoopFuture<Vapor.Response> in conformance Vapor.RouteLoggingMiddleware : Vapor.Middleware in Vapor
    8  Run Vapor.(HTTPMiddlewareResponder in _488FE00728E24C900504670C08744C50).respond(to: Vapor.Request) -> NIOCore.EventLoopFuture<Vapor.Response>
    9  Run protocol witness for Vapor.Responder.respond(to: Vapor.Request) -> NIOCore.EventLoopFuture<Vapor.Response> in conformance Vapor.(HTTPMiddlewareResponder in _488FE00728E24C900504670C08744C50) : Vapor.Responder in Vapor
    10 Run Vapor.DefaultResponder.respond(to: Vapor.Request) -> NIOCore.EventLoopFuture<Vapor.Response>
    11 Run protocol witness for Vapor.Responder.respond(to: Vapor.Request) -> NIOCore.EventLoopFuture<Vapor.Response> in conformance Vapor.DefaultResponder : Vapor.Responder in Vapor
    12 Run Vapor.HTTPServerHandler.channelRead(context: NIOCore.ChannelHandlerContext, data: NIOCore.NIOAny) -> ()
    13 Run protocol witness for NIOCore._ChannelInboundHandler.channelRead(context: NIOCore.ChannelHandlerContext, data: NIOCore.NIOAny) -> () in conformance Vapor.HTTPServerHandler : NIOCore._ChannelInboundHandler in Vapor
    14 Run NIOCore.ChannelHandlerContext.(invokeChannelRead in _F5AC316541457BD146E3694279514AA3)(NIOCore.NIOAny) -> ()
    15 Run NIOCore.ChannelHandlerContext.(invokeChannelRead in _F5AC316541457BD146E3694279514AA3)(NIOCore.NIOAny) -> ()
     [request-id: 07C70599-FAEE-4480-9118-713F5F77F67B] (Vapor/Middleware/ErrorMiddleware.swift:42)
    [ codes.vapor.application ] [ TRACE ] Unhandled user event: ResponseEndSentEvent() (Vapor/HTTP/Server/HTTPServerHandler.swift:66)
    ^C
    [ codes.vapor.application ] [ DEBUG ] Application shutting down (Vapor/Application.swift:135)
    [ codes.vapor.application ] [ TRACE ] Shutting down providers (Vapor/Application.swift:137)
    [ codes.vapor.application ] [ TRACE ] Clearing Application storage (Vapor/Application.swift:141)
    [ codes.vapor.http-server ] [ DEBUG ] Requesting HTTP server shutdown (Vapor/HTTP/Server/HTTPServer.swift:295)
    [ codes.vapor.http-server ] [ DEBUG ] HTTP server shutting down (Vapor/HTTP/Server/HTTPServer.swift:301)
    [ codes.vapor.application ] [ TRACE ] Shutting down EventLoopGroup (Vapor/Application.swift:149)
    [ codes.vapor.application ] [ TRACE ] Application shutdown complete (Vapor/Application.swift:158)
    
    bug 
    opened by fred-sch 2
Releases(4.67.5)
  • 4.67.5(Dec 16, 2022)

    This patch was authored by @Joannis and released by @0xTim.

    Fixes a bug where a streaming body from a request was not processing a chunk, causing the request to stall. This happened when a handler was being set at the exact moment a chunk was being processed, causing the chunk to be added to a now irrelevant array that was just processed while switching to a streaming callback.

    See #2906

    Source code(tar.gz)
    Source code(zip)
  • 4.67.4(Nov 17, 2022)

    This patch was authored and released by @Joannis.

    Fixes a bug where an Array of Dates wouldn't be encoded or decoded when using URL encoding.

    Source code(tar.gz)
    Source code(zip)
  • 4.67.3(Nov 15, 2022)

    This patch was authored and released by @0xTim.

    Fixes a bug where abandoned requests mean that the stream callback is never invoked. This happens when a client sends a request, the server starts processing it but hasn't constructed the Response and then the client closes early. The response is discarded, as is the stream callback so it never gets invoked. This fixes that issue

    Source code(tar.gz)
    Source code(zip)
  • 4.67.2(Nov 9, 2022)

    This patch was authored by @michal-tomlein and released by @0xTim.

    The generator parameter in Array.random(count:using:) was unused in what appears to be a copy-paste error. This change passes it down to FixedWidthInteger.random(using:), which was the original intention.

    Source code(tar.gz)
    Source code(zip)
  • 4.67.1(Nov 3, 2022)

    This patch was authored by @patrick-zippenfenig and released by @0xTim.

    Expire and Last-Modified header were encoding the hour part in 12h format (hh) instead of 24h format (HH). This results in timestamps being 12 hours off for all afternoon hours. This fixes the format used to follow the spec correctly and adds tests to ensure no regressions.

    Source code(tar.gz)
    Source code(zip)
  • 4.67.0(Nov 3, 2022)

    This patch was authored by @mcritz and released by @0xTim.

    This PR wraps Request.Body.drain() as a modern Swift AsyncSequence<ByteBuffer, Error>.

    This is useful to stream bytes from request rather than collecting them in memory. Example: A route could handle a multigigbyte file upload like this:

    do {
        let nioFileHandle = try NIOFileHandle(path: filePath, mode: .write)
        var offset: Int64 = 0
        
        for try await bytes in req.body {
            try await req.application.fileio.write(fileHandle: nioFileHandle,
                                                   toOffset: offset,
                                                   buffer: bytes,
                                                   eventLoop: req.eventLoop).get()
            offset += Int64(bytes.readableBytes)
            try nioFileHandle.close()
        }
    } catch {
       ...
    }
    
    Source code(tar.gz)
    Source code(zip)
  • 4.66.1(Oct 13, 2022)

    This patch was authored by @MahdiBM and released by @0xTim.

    Lock has recently been deprecated in favor of NIOLock.

    Changes

    All Locks have been renamed to NIOLock.

    Source code(tar.gz)
    Source code(zip)
  • 4.66.0(Oct 13, 2022)

    This patch was authored by @Joannis and released by @0xTim.

    If a third-party library or user defined code crashed a Vapor app, the Vapor Application deinit will crash the app before the real issue pops up. This leads to frustrating debug sessions

    Source code(tar.gz)
    Source code(zip)
  • 4.65.2(Sep 14, 2022)

    This patch was authored by @rkreutz and released by @gwynne.

    We can update application.storage from within HTTPServer, this way we can keep any changes that happen to the configuration internally up-to-date with the application storage.

    This possibly has the least changes and less surface of potential flaws, since we are only adding an extra param and working on top of it. However, now we are setting the application storage from within HTTPServer, there is no issue with that, is just that now we have 2 places changing the storage for the config.

    Resolves #2755

    Source code(tar.gz)
    Source code(zip)
  • 4.65.1(Aug 18, 2022)

    This patch was authored by @grahamburgsma and released by @0xTim.

    Add missing protocol ExpressibleByStringLiteral to HTTPHeaders.Name. The implementation init(stringLiteral:) was there, but the actual protocol was missing.

    Source code(tar.gz)
    Source code(zip)
  • 4.65.0(Aug 4, 2022)

    This patch was authored by @Lukasa and released by @0xTim.

    Motivation

    When using NIOSSL it is sometimes necessary to completely take over the certificate verification logic. NIOSSL exposes a callback for this, but it's currently hidden from Vapor users. We should let them get access to this callback.

    Modifications

    • Added the callback to the HTTPServer configuration struct.
    • Plumbed the callback through.
    • Added some invalid test certs to the resources for the tests.
    • Added a test to confirm the override functions correctly.

    Result

    Users can override client cert validation.

    Source code(tar.gz)
    Source code(zip)
  • 4.64.0(Jul 25, 2022)

    This patch was authored by @K1NXZ and released by @0xTim.

    Validate a regular expression pattern

    Example:

    struct TestContent: Codable, Validatable {
        static func validations(_ validations: inout Validations) {
            validations.add("numbersOnly", as: String.self, is: .pattern("^[0-9]*$"))
        }
        
        let numbersOnly: String
        
        init(numbersOnly: String) {
            self.numbersOnly = numbersOnly
        }
    }
    
    Source code(tar.gz)
    Source code(zip)
  • 4.63.0(Jul 21, 2022)

    This patch was authored and released by @gwynne.

    The new subscript simplifies "provider" implementations that extend Application and use its Storage instance without complex initialization requirements:

    extension Application {
        public struct Foo {
            final class Storage { /* content which needs no special initialization */ }
            struct Key: StorageKey { typealias Value = Storage }
            let application: Application
    
            // Before:
            var storage: Storage {
                if self.application.storage[Key.self] == nil { self.initialize() }
                return self.application.storage[Key.self]!
            }
    
            func initialize() { self.application.storage[Key.self] = .init() }
    
            // After:
            var storage: Storage { self.application.storage[Key.self, default: .init()] }
    
    Source code(tar.gz)
    Source code(zip)
  • 4.62.2(Jul 20, 2022)

    This patch was authored by @BasPeter and released by @0xTim.
    • Required parameter in add(each) now taken into account for validation
    • Add tests for required false test case
    Source code(tar.gz)
    Source code(zip)
  • 4.62.1(Jul 4, 2022)

    This patch was authored and released by @0xTim.

    This is a workaround for #2742. This ensures the request body is available in the middleware rather than it failing silently.

    Source code(tar.gz)
    Source code(zip)
  • 4.62.0(Jun 15, 2022)

    This patch was authored by @josercc and released by @0xTim.

    Conform Bool to Content to allow Bool types to be returned to the top level

    app.get("isOK") { req in
        return true
    }
    
    Source code(tar.gz)
    Source code(zip)
  • 4.61.1(Jun 6, 2022)

    ⚠️ Security Update ⚠️

    This release fixes an issue where the URLEncodedFormDecoder was vulnerable to a Denial of Service attack. Largely nested data would cause a stack overflow crash. We recommend upgrading to this release as soon as possible. For more details see the security advisory GHSA-qvxg-wjxc-r4gg.

    This has been designated as CVE-2022-31019

    Source code(tar.gz)
    Source code(zip)
  • 4.61.0(Jun 6, 2022)

    What's Changed

    • Fix issue with tail content-range headers by @BennyDeBock in https://github.com/vapor/vapor/pull/2840
    • Clean up use of C targets in Vapor by @gwynne in https://github.com/vapor/vapor/pull/2832
    • Update depencendy as it's now required by @0xTim in https://github.com/vapor/vapor/pull/2842
    • Expose Response's Upgrader field by @GNMoseke in https://github.com/vapor/vapor/pull/2817
    • Support for peerAddress on Request by @paunik in https://github.com/vapor/vapor/pull/2822

    New Contributors

    • @GNMoseke made their first contribution in https://github.com/vapor/vapor/pull/2817
    • @paunik made their first contribution in https://github.com/vapor/vapor/pull/2822

    Full Changelog: https://github.com/vapor/vapor/compare/4.60.3...4.61.0

    Source code(tar.gz)
    Source code(zip)
  • 4.60.3(May 31, 2022)

    ⚠️ Security Update ⚠️

    This release fixes an issue where the FileMiddleware was vulnerable to a Denial of Service attack. Invalid range headers could cause the application to crash. If using FileMiddleware we recommend upgrading to this release as soon as possible. For more details see the security advisory GHSA-vj2m-9f5j-mpr5.

    This has been designated as CVE-2022-31005

    Source code(tar.gz)
    Source code(zip)
  • 4.60.2(May 26, 2022)

    This patch was authored by @BennyDeBock and released by @0xTim.

    Fix the byte count calculation in FileIO when handling a range.

    Resolves #2834

    Source code(tar.gz)
    Source code(zip)
  • 4.60.1(May 23, 2022)

    This patch was authored and released by @BennyDeBock.

    Adds a reason to the Abort statement as well as adding a log statement for debugging

    closes #2720

    Source code(tar.gz)
    Source code(zip)
  • 4.60.0(May 20, 2022)

    What's Changed

    • Add international email validator by @BennyDeBock in https://github.com/vapor/vapor/pull/2829
    • Add flag to enable/disable metrics by @valerianb in https://github.com/vapor/vapor/pull/2827
    • Update AHC version which allows us to enable missing tests by @0xTim in https://github.com/vapor/vapor/pull/2814
    • Expose the Channel's ByteBufferAllocator in Request and Response by @0xTim in https://github.com/vapor/vapor/pull/2595

    New Contributors

    • @valerianb made their first contribution in https://github.com/vapor/vapor/pull/2827

    Full Changelog: https://github.com/vapor/vapor/compare/4.59.1...4.60.0

    Source code(tar.gz)
    Source code(zip)
  • 4.59.1(May 17, 2022)

    This patch was authored and released by @BennyDeBock.

    Correctly handle the filename* ContentDisposition header to be compliant with RFC-5987 as described in RFC 6266. This allows handling of unicode and other format characters in filenames.

    Resolves https://github.com/vapor/vapor/issues/2802

    Source code(tar.gz)
    Source code(zip)
  • 4.59.0(May 16, 2022)

  • 4.58.0(May 13, 2022)

    This patch was authored and released by @BennyDeBock.

    closes #2659

    Adds a function called delete to remove a cached value. I wasn't able to override the method with ExpressableByNilLiteral? instead of T? without making breaking changes

    Source code(tar.gz)
    Source code(zip)
  • 4.57.1(May 12, 2022)

    This patch was authored by @MaxDesiatov and released by @0xTim.

    According to MDN, .mjs is an extension that JavaScript files with support for EcmaScript Module system can use. The lack of this extension in the mapping causes some browsers to throw 'text/plain' is not a valid JavaScript MIME type. error.

    Source code(tar.gz)
    Source code(zip)
  • 4.57.0(Apr 17, 2022)

    This patch was authored by @AndreYonadam and released by @0xTim.

    This adds the ability to perform asynchronous work in the beforeRequest and afterResponse closures in XCTVapor.

    E.g.

    try await app.test(.GET, "/hello", beforeRequest: { req async throws in
      let currentUsersCount = try await User.query(on: app.db).count()
    }, afterResponse: { res async throws in
      let newUsersCount = try await User.query(on: app.db).count()
    })
    
    Source code(tar.gz)
    Source code(zip)
  • 4.56.0(Apr 15, 2022)

    This patch was authored by @AndreYonadam and released by @0xTim.

    This adds support for custom failure descriptions when running validations. This allows you to customise the any unreadable keys and allows you to provide localized failure descriptions for each Validation.

    Source code(tar.gz)
    Source code(zip)
  • 4.55.4(Apr 9, 2022)

    This patch was authored by @SusanDoggie and released by @0xTim.

    Fix a compilation error in the latest Swift nightlies due to using String.lazy.split(separator:), which is not needed

    Source code(tar.gz)
    Source code(zip)
  • 4.55.3(Mar 13, 2022)

    This patch was authored and released by @0xTim.

    This ensures that the Application's configuration is updated correctly when the server starts. This allows you to retrieve correct hostname and ports from the Application.

    Resolves #2755

    Source code(tar.gz)
    Source code(zip)
Owner
Vapor
Creating modular server side software with Swift.
Vapor
Meet Corvus, the first strongly declarative server-side framework.

Corvus Corvus is the first truly declarative server-side framework for Swift. It provides a declarative, composable syntax which makes it easy to get

null 42 Jun 29, 2022
Apple Push Notifications (APNs) Server-Side library.

Perfect-Notifications 简体中文 APNs remote Notifications for Perfect. This package adds push notification support to your server. Send notifications to iO

PerfectlySoft Inc. 113 Oct 28, 2022
A Swift web framework and HTTP server.

A Swift Web Framework and HTTP Server Summary Kitura is a web framework and web server that is created for web services written in Swift. For more inf

Kitura 7.6k Jan 6, 2023
libuv base Swift web HTTP server framework

Notice Trevi now open a Trevi Community. Yoseob/Trevi project split up into respective Trevi, lime, middlewares and sys packages at our community. If

leeyoseob 46 Jan 29, 2022
Swift Express is a simple, yet unopinionated web application server written in Swift

Documentation <h5 align="right"><a href="http://demo.swiftexpress.io/">Live ?? server running Demo <img src="https://cdn0.iconfinder.com/data/icons/

Crossroad Labs 850 Dec 2, 2022
Lightweight library for web server applications in Swift on macOS and Linux powered by coroutines.

Why Zewo? • Support • Community • Contributing Zewo Zewo is a lightweight library for web applications in Swift. What sets Zewo apart? Zewo is not a w

Zewo 1.9k Dec 22, 2022
High Performance (nearly)100% Swift Web server supporting dynamic content.

Dynamo - Dynamic Swift Web Server Starting this project the intention was to code the simplest possible Web Server entirely in Swift. Unfortunately I

John Holdsworth 68 Jul 25, 2022
📡 RealHTTP is a lightweight yet powerful client-side HTTP library.

RealHTTP RealHTTP is a lightweight yet powerful client-side HTTP library. Our goal is make an easy to use and effortless http client for Swift. Featur

Immobiliare Labs 233 Jan 7, 2023
Client side for goyotashi

B_2121_client サーバサイドはこちら Required Dependency Cocoa Pods — https://guides.cocoapods.org/using/getting-started.html Getting Started ⚠️ M1 Mac の場合、「Finde

JPHACKS 2 Oct 31, 2021
An iOS library to route API paths to objects on client side with request, mapping, routing and auth layers

WANetworkRouting Developed and Maintained by ipodishima Founder & CTO at Wasappli Inc. Sponsored by Wisembly A routing library to fetch objects from a

null 10 Nov 20, 2022
Swift backend / server framework (Pure Swift, Supports Linux)

NetworkObjects NetworkObjects is a #PureSwift backend. This framework compiles for OS X, iOS and Linux and serves as the foundation for building power

Alsey Coleman Miller 258 Oct 6, 2022
A Ruby on Rails inspired Web Framework for Swift that runs on Linux and OS X

IMPORTANT! We don't see any way how to make web development as great as Ruby on Rails or Django with a very static nature of current Swift. We hope th

Saulius Grigaitis 2k Dec 5, 2022
A minimal, fast and unopinionated web framework for Swift

![Fire Image] (http://i.imgur.com/1qR6Nl4.png) Blackfire An extremely fast Swift web framework ?? Getting Started If you're familiar with express.js t

Elliott Minns 908 Dec 2, 2022
A Swift Multiplatform Single-threaded Non-blocking Web and Networking Framework

Serverside non-blocking IO in Swift Ask questions in our Slack channel! Lightning (formerly Edge) Node Lightning is an HTTP Server and TCP Client/Serv

SkyLab 316 Oct 6, 2022
Super lightweight web framework in Swift based on SWSGI

Ambassador Super lightweight web framework in Swift based on SWSGI Features Super lightweight Easy to use, designed for UI automatic testing API mocki

Envoy 170 Nov 15, 2022
Minimal web framework and middleware for Swift

Kunugi Kunugi(椚) is minimal web framework and middleware systems for Swift. This is inpired by Node.js' Koa. Kunugi doesn't provide http server its se

Yusuke Ito 35 Apr 18, 2022
Lightweight, flexible HTTP server framework written in Swift

Hummingbird Lightweight, flexible server framework written in Swift. Hummingbird consists of three main components, the core HTTP server, a minimal we

Hummingbird 245 Dec 30, 2022
RestKit is a framework for consuming and modeling RESTful web resources on iOS and OS X

RestKit RestKit is a modern Objective-C framework for implementing RESTful web services clients on iOS and Mac OS X. It provides a powerful object map

The RestKit Project 10.2k Dec 29, 2022
Robust Swift networking for web APIs

Conduit Conduit is a session-based Swift HTTP networking and auth library. Within each session, requests are sent through a serial pipeline before bei

Mindbody 52 Oct 26, 2022