Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

service provider support #52

Merged
merged 23 commits into from
Feb 22, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
13b3344
Refactored to use an Application instance
shnhrrsn Feb 20, 2016
561c53f
Added service providers
shnhrrsn Feb 20, 2016
ab1ac16
Removed singelton from Application
shnhrrsn Feb 20, 2016
8e85b54
Renamed ServiceProvider to Provider
shnhrrsn Feb 20, 2016
f10f574
Updated Application init to include an optional providers parameter
shnhrrsn Feb 22, 2016
28f96a3
Added Application.start(port:) to start the server + auto-boot the app
shnhrrsn Feb 22, 2016
a42c608
Refactored providers to use a single boot(application:) class method …
shnhrrsn Feb 22, 2016
c663f5d
Added Application + Provider to the Xcode project
shnhrrsn Feb 22, 2016
4e296b2
Merged Server into Application
shnhrrsn Feb 22, 2016
f4827d7
move routes to application class
tanner0101 Feb 22, 2016
7ab8e22
simplify provider api
tanner0101 Feb 22, 2016
3e4fa21
`Request.Handler` cleanup + middleware
tanner0101 Feb 22, 2016
ff078c8
re-add boot providers
tanner0101 Feb 22, 2016
6a464a2
readme updates
tanner0101 Feb 22, 2016
46edf0a
Updated SessionMiddleware to conform to updated Middleware protocol
shnhrrsn Feb 22, 2016
cbdb713
updating application integration to use new router driver
tanner0101 Feb 22, 2016
b9c7012
request handler comes pre converted
loganwright Feb 22, 2016
81ad1ce
ensuring global Route set as driver by default
loganwright Feb 22, 2016
af8a3a2
removing unnecessary response()
loganwright Feb 22, 2016
eb165cb
convertiblehandler => Router.Handler
loganwright Feb 22, 2016
9cbfdbd
moving get / post etc. methods back to application and updating surro…
loganwright Feb 22, 2016
145ffc1
Router => Branch Router, Handler moved to application, errors propoga…
loganwright Feb 22, 2016
66c054b
adding host support for desired syntax
loganwright Feb 22, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 4 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,13 @@ A Laravel/Lumen Inspired Web Framework for Swift that works on iOS, OS X, and Ub
[![PRs Welcome](https://img.shields.io/badge/prs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
[![Slack Status](http://slack.tanner.xyz:8085/badge.svg?style=flat-square)](http://slack.tanner.xyz:8085)

## Getting Started
## Work in Progress

Clone the [Example](https://github.com/qutheory/vapor-example) project to start making your application or check out the [live demo](http://vapor.qutheory.io) running on Ubuntu. This repository is for the framework module.
This is a work in progress, so *do not* rely on this for anything important. And pull requests are welcome!

You can also download the alpha [Vapor Installer](https://github.com/mpclarkson/vapor-installer), which allows you to create a new project at the command line e.g. `vapor new MyProject`
## Documentation

You must have Swift 2.2 or later installed. You can learn more about Swift 2.2 at [Swift.org](http://swift.org)

Want to make a pull request? You can learn how from this *free* series [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github)

### Work in Progress

This is a work in progress, so don't rely on this for anything important. And pull requests are welcome!

## Wiki

Visit the [Vapor Wiki](https://github.com/qutheory/vapor/wiki) for extensive documentation on using and contributing to Vapor.
Visit the [Vapor Wiki](https://github.com/qutheory/vapor/wiki) for extensive documentation on getting setup, using, and contributing to Vapor.

## Server

Expand Down
178 changes: 178 additions & 0 deletions Sources/Application.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import Foundation

#if os(Linux)
import Glibc
#endif

public class Application {
public static let VERSION = "0.1.9"

/**
The router driver is responsible
for returning registered `Route` handlers
for a given request.
*/
public let router: RouterDriver

/**
The server driver is responsible
for handling connections on the desired port.
This property is constant since it cannot
be changed after the server has been booted.
*/
public var server: ServerDriver

/**
`Middleware` will be applied in the order
it is set in this array.

Make sure to append your custom `Middleware`
if you don't want to overwrite default behavior.
*/
public var middleware: [Middleware.Type]


/**
Provider classes that have been registered
with this application
*/
public var providers: [Provider.Type]

/**
The work directory of your application is
the directory in which your Resources, Public, etc
folders are stored. This is normally `./` if
you are running Vapor using `.build/xxx/App`
*/
public static var workDir = "./" {
didSet {
if !self.workDir.hasSuffix("/") {
self.workDir += "/"
}
}
}

internal var host: String = "*"

/**
Initialize the Application.
*/
public init(router: RouterDriver = BranchRouter(), server: ServerDriver = SocketServer()) {
self.server = server
self.router = router

self.middleware = []
self.providers = []

self.middleware.append(SessionMiddleware)
}


public func bootProviders() {
for provider in self.providers {
provider.boot(self)
}
}


/**
Boots the chosen server driver and
runs on the supplied port.
*/
public func start(port inPort: Int = 80) {
self.bootProviders()

self.server.delegate = self

var port = inPort

//grab process args
for argument in Process.arguments {
if argument.hasPrefix("--workDir=") {
let workDirString = argument.split("=")[1]
self.dynamicType.workDir = workDirString
print("Work dir override: \(workDirString)")
} else if argument.hasPrefix("--port=") {
let portString = argument.split("=")[1]
if let portInt = Int(portString) {
print("Port override: \(portInt)")
port = portInt
}
}
}

do {
try self.server.boot(port: port)

print("Server has started on port \(port)")

self.loop()
} catch {
print("Server start error: \(error)")
}
}

/**
Starts an infinite loop to keep the server alive while it
waits for inbound connections.
*/
func loop() {
#if os(Linux)
while true {
sleep(1)
}
#else
NSRunLoop.mainRunLoop().run()
#endif
}
}

extension Application: ServerDriverDelegate {

public func serverDriverDidReceiveRequest(request: Request) -> Response {
var handler: Request.Handler

// Check in routes
if let routerHandler = router.route(request) {
handler = routerHandler
} else {
// Check in file system
let filePath = self.dynamicType.workDir + "Public" + request.path

let fileManager = NSFileManager.defaultManager()
var isDir: ObjCBool = false

if fileManager.fileExistsAtPath(filePath, isDirectory: &isDir) {
// File exists
if let fileBody = NSData(contentsOfFile: filePath) {
var array = [UInt8](count: fileBody.length, repeatedValue: 0)
fileBody.getBytes(&array, length: fileBody.length)

return Response(status: .OK, data: array, contentType: .Text)
} else {
handler = { _ in
return Response(error: "Could not open file.")
}
}
} else {
// Default not found handler
handler = { _ in
return Response(status: .NotFound, text: "Page not found")
}
}
}

// Loop through middlewares in order
for middleware in self.middleware {
handler = middleware.handle(handler)
}

do {
return try handler(request: request)
} catch {
return Response(error: "Server Error: \(error)")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is the final catch all. In case no routes, middleware, or providers caught the error, it will be returned as a 500.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfect, this actually makes the other implementation easier and more concise. Updated based on comments

}

}

}
77 changes: 41 additions & 36 deletions Sources/Router.swift → Sources/BranchRouter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,64 +8,52 @@

public typealias Host = String

public let Route = Router()

extension Request {
extension Application {

public typealias Handler = Request throws -> ResponseConvertible
}

extension Router: RouterDriver {
public func route(request: Request) -> Request.Handler? {
return handle(request)
}

public func register(hostname hostname: String = "*", method: Request.Method, path: String, handler: Request.Handler) {
add(hostname, method: method, path: path, handler: handler)
}
}

extension Router {
public final func get(path: String, closure: Request.Handler) {
add(method: .Get, path: path, handler: closure)
public final func get(path: String, closure: Handler) {
self.add(.Get, path: path, closure: closure)
}

public final func post(path: String, closure: Request.Handler) {
self.add(method: .Post, path: path, handler: closure)
public final func post(path: String, closure: Handler) {
self.add(.Post, path: path, closure: closure)
}

public final func put(path: String, closure: Request.Handler) {
self.add(method: .Put, path: path, handler: closure)
public final func put(path: String, closure: Handler) {
self.add(.Put, path: path, closure: closure)
}

public final func patch(path: String, closure: Request.Handler) {
self.add(method: .Patch, path: path, handler: closure)
public final func patch(path: String, closure: Handler) {
self.add(.Patch, path: path, closure: closure)
}

public final func delete(path: String, closure: Request.Handler) {
self.add(method: .Delete, path: path, handler: closure)
public final func delete(path: String, closure: Handler) {
self.add(.Delete, path: path, closure: closure)
}

public final func options(path: String, closure: Request.Handler) {
self.add(method: .Options, path: path, handler: closure)
public final func options(path: String, closure: Handler) {
self.add(.Options, path: path, closure: closure)
}

public final func any(path: String, closure: Request.Handler) {
public final func any(path: String, closure: Handler) {
self.get(path, closure: closure)
self.post(path, closure: closure)
self.put(path, closure: closure)
self.patch(path, closure: closure)
self.delete(path, closure: closure)
}

public final func resource(path: String, controller: Controller) {

let last = "/:id"
let shortPath = path.componentsSeparatedByString(".")
.flatMap { component in
return [component, "/:\(component)_id/"]
}
.dropLast()
.joinWithSeparator("")

// ie: /users
self.get(shortPath, closure: controller.index)
self.post(shortPath, closure: controller.store)
Expand All @@ -76,15 +64,30 @@ extension Router {
self.put(fullPath, closure: controller.update)
self.delete(fullPath, closure: controller.destroy)
}

public final func add(method: Request.Method, path: String, closure: Handler) {
router.register(hostname: host, method: method, path: path) { request in
return try closure(request).response()
}
}

public final func host(host: String, closure: () -> Void) {
let original = self.host
self.host = host
closure()
self.host = original
}
}

public final class Router {
public final class BranchRouter: RouterDriver {

// MARK: Private Tree Representation

private final var tree: [Host : [Request.Method : Branch]] = [:]

internal init() {}
// MARK: Routing

internal final func handle(request: Request) -> Request.Handler? {
public final func route(request: Request) -> Request.Handler? {
let root = tree[request.hostname] ?? tree["*"]
guard
let branch = root?[request.method]
Expand All @@ -97,13 +100,15 @@ public final class Router {
return branch.handle(request, comps: generator)
}

public final func add(host: Host = "*", method: Request.Method, path: String, handler: Request.Handler) {
// MARK: Registration

public final func register(hostname hostname: String = "*", method: Request.Method, path: String, handler: Request.Handler) {
let generator = path.pathComponentGenerator()
var root = tree[host] ?? [:]
var root = tree[hostname] ?? [:]
let branch = root[method] ?? Branch(name: "")
branch.extendBranch(generator, handler: handler)
root[method] = branch
tree[host] = root
tree[hostname] = root
}
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/Middleware.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ public protocol Middleware {
Call `handler(request)` somewhere inside your custom
handler to get the `Response` object.
*/
func handle(handler: Request -> Response) -> (Request -> Response)
static func handle(handler: Request.Handler) -> Request.Handler

}
3 changes: 3 additions & 0 deletions Sources/Provider.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
public protocol Provider {
static func boot(application: Application)
}
2 changes: 2 additions & 0 deletions Sources/Request.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ public class Request {
case Options = "OPTIONS"
case Unknown = "x"
}

public typealias Handler = ((request: Request) throws -> Response)

///HTTP Method used for request.
public let method: Method
Expand Down
2 changes: 1 addition & 1 deletion Sources/Response.swift
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ public class Response {
}

var headers: [String: String] {
var headers = ["Server" : "Vapor \(Server.VERSION)"]
var headers = ["Server" : "Vapor \(Application.VERSION)"]

if self.cookies.count > 0 {
var cookieString = ""
Expand Down