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

Lw/branch router driver #47

Merged
merged 36 commits into from
Feb 22, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
f807e2d
branch implementation and basic performance test 30-40% faster
loganwright Feb 20, 2016
485036a
middle period in transitioning to a new router model
loganwright Feb 20, 2016
89febae
integrating router and branch w/ server
loganwright Feb 20, 2016
9feec43
router test cleanup
loganwright Feb 20, 2016
1fd10b5
removing unused files and marking controller todo
loganwright Feb 20, 2016
431578b
returning response convertible instead of response directly in reques…
loganwright Feb 20, 2016
edc32b8
RequestHandler => Request.Handler and AltRouter => Router
loganwright Feb 20, 2016
4d2605f
reconnecting controller
loganwright Feb 20, 2016
1f24240
supporting nested resources
loganwright Feb 21, 2016
c28d4c3
updating nested tests to new router
loganwright Feb 21, 2016
adc672d
removing performance test for now since data has been collected. fut…
loganwright Feb 21, 2016
f040fb3
removing excess logs
loganwright Feb 22, 2016
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
480cdff
Merge pull request #52 from qutheory/lw/service-provider-support
tanner0101 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)")
}

}

}
108 changes: 108 additions & 0 deletions Sources/Branch.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
//
// Branch.swift
// HelloServer
//
// Created by Logan Wright on 2/15/16.
// Copyright © 2016 LoganWright. All rights reserved.
//

/**
When routing requests, different branches will be established, in a linked list style stemming from their host and request method. It can be represented as

| host | request.method | branch -> branch -> branch
*/
internal final class Branch {

/**
The name of the branch, ie if we have a path hello/:name, the branch structure will be
Branch('hello') (connected to) Branch('name')

In cases where a slug is used, ie ':name' the slug will be used as the name and passed as a key in matching
*/
let name: String

/**
There are two types of branches, those that support a handler, and those that are a linker between branches, for example /users/messages/:id will have 3 connected branches, only one of which supports a handler

Branch('users') -> Branch('messages') -> *Branches('id')
*indicates a supported branch
*/
private var handler: Request.Handler?

/**
key or *

if it is a `key`, then it connects to an additional branch
if it is `*`, it is a slug point and the name represents a key for a dynamic value

*/
private(set) var subBranches: [String : Branch] = [:]

/**
Used to create a new branch

- parameter name: the name associated with the branch, or the key when dealing with a slug
- parameter handler: the handler to be called if its a valid endpoint, or `nil` if this is a bridging branch

- returns: an initialized request Branch
*/
init(name: String, handler: Request.Handler? = nil) {
self.name = name
self.handler = handler
}

/**
This function will recursively traverse the branch until the path is fulfilled or the branch ends

- parameter request: the request to use in matching
- parameter comps: ordered pathway components generator

- returns: a request handler or nil if not supported
*/
@warn_unused_result
func handle(request: Request, comps: AnyGenerator<String>) -> Request.Handler? {
guard let key = comps.next() else {
return handler
}

if let next = subBranches[key] {
return next.handle(request, comps: comps)
} else if let wildcard = subBranches["*"] {
request.parameters[wildcard.name] = key.stringByRemovingPercentEncoding
return wildcard.handle(request, comps: comps)
} else {
return nil
}
}

/**
If a branch exists that is linked as:

Branch('one') -> Branch('two')

This branch will be extended with the given value

- parameter generator: the generator that will be used to match the path components. /users/messages/:id will return a generator that is 'users' <- 'messages' <- '*id'
- parameter handler: the handler to assign to the end path component
*/
func extendBranch(generator: AnyGenerator<String>, handler: Request.Handler) {
guard let key = generator.next() else {
self.handler = handler
return
}

if key.hasPrefix(":") {
let chars = key.characters
let indexOne = chars.startIndex.advancedBy(1)
let sub = key.characters.suffixFrom(indexOne)
let substring = String(sub)
let next = subBranches["*"] ?? Branch(name: substring)
next.extendBranch(generator, handler: handler)
subBranches["*"] = next
} else {
let next = subBranches[key] ?? Branch(name: key)
next.extendBranch(generator, handler: handler)
subBranches[key] = next
}
}
}