├── .gitignore ├── HelloTrevi ├── AppDelegate.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ └── Main.storyboard ├── Info.plist ├── Root.swift ├── ViewController.swift ├── main.swift ├── public │ ├── favicon.ico │ ├── sky.jpeg │ └── style.css └── views │ ├── helloworld.ssp │ └── index.ssp ├── LICENSE.txt ├── Lime ├── Info.plist ├── Lime.h └── Lime │ ├── Layer.swift │ ├── Lime.swift │ ├── LimePrepare.swift │ ├── Middleware │ ├── BodyParser.swift │ ├── Favicon.swift │ ├── Logger.swift │ ├── Middleware.swift │ ├── MultiParty.swift │ ├── Query.swift │ ├── Router.swift │ ├── ServeStatic.swift │ └── SwiftServerPage.swift │ ├── Regexp.swift │ ├── Render.swift │ ├── Request.swift │ ├── Require.swift │ ├── Response.swift │ ├── RoutAble.swift │ └── Route.swift ├── Makefile ├── README.md ├── Trevi.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── leeyoseob.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── leeyoseob.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── Trevi.xcscheme │ ├── Trevi_ver_lime.xcscheme │ └── xcschememanagement.plist └── Trevi ├── Info.plist ├── Library ├── Buffer.swift ├── EventEmitter.swift ├── FileSystem.swift ├── Http.swift ├── HttpParser.swift ├── HttpProtocol.swift ├── HttpServer.swift ├── IncomingMessage.swift ├── Net.swift ├── OutgoingMessage.swift ├── ServerResponse.swift ├── StreamReadable.swift └── StreamWritable.swift ├── Makefile ├── Source ├── .DS_Store ├── Async.swift ├── FSBase.swift ├── Handle.swift ├── Libuv │ ├── libuv.a │ ├── module.modulemap │ ├── uv-darwin.h │ ├── uv-errno.h │ ├── uv-threadpool.h │ ├── uv-unix.h │ ├── uv-version.h │ └── uv.h ├── LibuvError.swift ├── LibuvUtility.swift ├── Loop.swift ├── Pipe.swift ├── Poll.swift ├── Stream.swift ├── Tcp.swift ├── Timer.swift └── Work.swift ├── Test ├── FileServer.swift └── NetEchoServer.swift ├── Trevi.h └── Utility ├── File.swift ├── Json.swift └── Utility.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## OSX generated 6 | .DS_Store 7 | 8 | ## Jetbrain AppCode generated 9 | .idea/ 10 | 11 | ## Build generated 12 | build/ 13 | DerivedData 14 | 15 | ## Various settings 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | xcuserdata 25 | 26 | ## Other 27 | *.xccheckout 28 | *.moved-aside 29 | *.xcuserstate 30 | *.xcscmblueprint 31 | 32 | ## Obj-C/Swift specific 33 | *.hmap 34 | *.ipa 35 | 36 | # CocoaPods 37 | # 38 | # We recommend against adding the Pods directory to your .gitignore. However 39 | # you should judge for yourself, the pros and cons are mentioned at: 40 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 41 | Pods/ 42 | Podfile.lock 43 | 44 | # Carthage 45 | # 46 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 47 | Carthage/Checkouts 48 | Carthage/Build 49 | 50 | # fastlane 51 | # 52 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 53 | # screenshots whenever they are needed. 54 | # For more information about the recommended setup visit: 55 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md 56 | fastlane/report.xml 57 | fastlane/screenshots 58 | 59 | -------------------------------------------------------------------------------- /HelloTrevi/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // HelloTrevi 4 | // 5 | // Created by LeeYoseob on 2015. 11. 30.. 6 | // Copyright © 2015년 LeeYoseob. All rights reserved. 7 | // 8 | // # Notice 9 | // Trevi now open a [Trevi Community](https://github.com/Trevi-Swift). 10 | // Yoseob/Trevi project split up into respective Trevi, lime, middlewares and sys packages at our community. 11 | // 12 | // If you want to build or test all projects at Xcode, please check out [Trevi-Dev](https://github.com/Trevi-Swift/Trevi-Dev). 13 | // Otherwise, you can build Trevi, lime and other packages by using Swift Package manager. 14 | // [Here](https://github.com/Trevi-Swift/example-trevi-lime) are an example and it now runs on Linux. 15 | // 16 | // Hope Trevi interests you. 17 | 18 | 19 | import Cocoa 20 | import Trevi 21 | import Lime 22 | 23 | 24 | @NSApplicationMain 25 | class AppDelegate: NSObject, NSApplicationDelegate { 26 | 27 | func applicationDidFinishLaunching ( aNotification: NSNotification ) { 28 | 29 | let server = Http () 30 | 31 | let lime = Lime() 32 | 33 | lime.set("views", "\(__dirname)/views") 34 | 35 | lime.set("view engine", SwiftServerPage()) 36 | 37 | lime.use(Logger(format: "default")) 38 | 39 | lime.use(Favicon()) 40 | 41 | lime.use(BodyParser.json()) 42 | 43 | lime.use(BodyParser.urlencoded()) 44 | 45 | lime.use("/", Root()) 46 | 47 | lime.use { (req, res, next) in 48 | res.statusCode = 404 49 | res.send("404 error") 50 | } 51 | 52 | server.createServer(lime).listen(8080) 53 | 54 | /* 55 | server.createServer({ (req, res, next) in 56 | 57 | var chuck = "" 58 | func ondata(c: String){ 59 | chuck += c 60 | } 61 | func onend(){ 62 | print("end") 63 | res.write(chuck) 64 | res.end() 65 | 66 | } 67 | req.on("data", ondata) 68 | req.on("end", onend) 69 | 70 | }).listen(8080) 71 | */ 72 | } 73 | 74 | func applicationWillTerminate ( aNotification: NSNotification ) { 75 | // Insert code here to tear down your application 76 | } 77 | } 78 | 79 | 80 | -------------------------------------------------------------------------------- /HelloTrevi/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /HelloTrevi/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | NSHumanReadableCopyright 28 | Copyright © 2015년 LeeYoseob. All rights reserved. 29 | NSMainStoryboardFile 30 | Main 31 | NSPrincipalClass 32 | NSApplication 33 | 34 | 35 | -------------------------------------------------------------------------------- /HelloTrevi/Root.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Lime.swift 3 | // Trevi 4 | // 5 | // Created by LeeYoseob on 2015. 12. 1.. 6 | // Copyright © 2015년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Lime 11 | 12 | /* 13 | Cookie parser 14 | Chunk encoding 15 | response refactoring (etag) 16 | mime type 17 | 18 | 19 | all(get,post,put) 20 | 21 | */ 22 | 23 | import Trevi 24 | 25 | public class Root{ 26 | 27 | private let lime = Lime() 28 | private var router: Router! 29 | 30 | public init(){ 31 | 32 | router = lime.router 33 | 34 | router.get("/") { req , res , next in 35 | res.render("index.ssp", args: ["title":"Trevi"]) 36 | } 37 | 38 | router.get("/index") { req , res , next in 39 | res.write("index get") 40 | res.end() 41 | } 42 | 43 | router.post("double", MultiParty()) { (req, res, next) -> Void in 44 | 45 | for file in req.files.values { 46 | 47 | print("\(file.name) , \(file.path)") 48 | } 49 | 50 | for (k,v) in req.body { 51 | print("\(k) , \(v)") 52 | } 53 | 54 | res.send("multipart parser middleware") 55 | } 56 | 57 | 58 | router.post("/index") { req , res , next in 59 | func ondata(data: String) { 60 | print(data) 61 | } 62 | func onend() { 63 | res.send("index post") 64 | } 65 | req.on("data", ondata) 66 | req.on("end", onend) 67 | } 68 | 69 | router.get("/redir") { req , res , next in 70 | res.redirect("http://127.0.0.1:8080/") 71 | } 72 | 73 | router.get("/trevi/:param1") { req , res , next in 74 | print("[GET] /trevi/:praram") 75 | res.send(req.params["param1"]!) 76 | } 77 | } 78 | } 79 | 80 | // To leave implementation order to use a module in lime to use. 81 | extension Root: Require{ 82 | public func export() -> Router { 83 | return self.router 84 | } 85 | } 86 | 87 | -------------------------------------------------------------------------------- /HelloTrevi/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // HelloTrevi 4 | // 5 | // Created by LeeYoseob on 2015. 11. 30.. 6 | // Copyright © 2015년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class ViewController: NSViewController { 12 | 13 | override func viewDidLoad () { 14 | super.viewDidLoad () 15 | } 16 | 17 | override var representedObject: AnyObject? { 18 | didSet { 19 | // Update the view, if already loaded. 20 | } 21 | } 22 | 23 | 24 | } 25 | 26 | -------------------------------------------------------------------------------- /HelloTrevi/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // Trevi_ver_lime 4 | // 5 | // Created by SeungHyun Lee on 2016. 03. 02.. 6 | // Copyright © 2016년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Trevi 10 | import Lime 11 | 12 | let server = Http () 13 | 14 | let lime = Lime() 15 | 16 | lime.set("views", "\(__dirname)/views") 17 | 18 | lime.set("view engine", SwiftServerPage()) 19 | 20 | lime.use(Logger(format: "default")) 21 | 22 | lime.use(Favicon()) 23 | 24 | lime.use(ServeStatic(path: "\(__dirname)/public")) 25 | 26 | lime.use(BodyParser.json()) 27 | 28 | lime.use(BodyParser.urlencoded()) 29 | 30 | lime.use("/", Root()) 31 | 32 | lime.use { (req, res, next) in 33 | res.statusCode = 404 34 | res.send("404 error") 35 | } 36 | 37 | server.createServer(lime).listen(8080) 38 | 39 | // server.createServer({ (req, res, next) in 40 | // res.write("hello Trevi") 41 | // res.end() 42 | // }).listen(8080) -------------------------------------------------------------------------------- /HelloTrevi/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yoseob/Trevi/d1a8c2edecfd5cac51a355395744e4f938cd1bea/HelloTrevi/public/favicon.ico -------------------------------------------------------------------------------- /HelloTrevi/public/sky.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yoseob/Trevi/d1a8c2edecfd5cac51a355395744e4f938cd1bea/HelloTrevi/public/sky.jpeg -------------------------------------------------------------------------------- /HelloTrevi/public/style.css: -------------------------------------------------------------------------------- 1 | .btn,h1,h2,h3,h4,h5,h6{letter-spacing:.09em;text-transform:uppercase}.btn,.copyright p,h1,h2,h3,h4,h5,h6{text-transform:uppercase}@font-face{font-family:"San Francisco-UL";font-weight:100;src:url(https://applesocial.s3.amazonaws.com/assets/styles/fonts/sanfrancisco/sanfranciscodisplay-ultralight-webfont.woff)}@font-face{font-family:"San Francisco-T";font-weight:200;src:url(https://applesocial.s3.amazonaws.com/assets/styles/fonts/sanfrancisco/sanfranciscodisplay-thin-webfont.woff)}@font-face{font-family:"San Francisco-R";font-weight:400;src:url(https://applesocial.s3.amazonaws.com/assets/styles/fonts/sanfrancisco/sanfranciscodisplay-regular-webfont.woff)}@font-face{font-family:"San Francisco-M";font-weight:500;src:url(https://applesocial.s3.amazonaws.com/assets/styles/fonts/sanfrancisco/sanfranciscodisplay-medium-webfont.woff)}@font-face{font-family:"San Francisco-SB";font-weight:600;src:url(https://applesocial.s3.amazonaws.com/assets/styles/fonts/sanfrancisco/sanfranciscodisplay-semibold-webfont.woff)}@font-face{font-family:"San Francisco-B";font-weight:700;src:url(https://applesocial.s3.amazonaws.com/assets/styles/fonts/sanfrancisco/sanfranciscodisplay-bold-webfont.woff)}*{font-family:"San Francisco-UL"}.bg-box{height:100%;width:100%;left:0;position:fixed;top:0;background:linear-gradient(to bottom,#40ed56 0,#06a985 100%)}h1,h2,h3,h4,h5,h6{font-family:"San Francisco",sans-serif;font-weight:400;margin:0 0 17px;opacity:1;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";filter:alpha(opacity=100)}a,body{font-family:Lato,sans-serif}h2,h5{margin-bottom:33px}h5,h6{letter-spacing:.11em}h1{font-size:7em;margin-bottom:17px}h2{font-size:2.667em}h3{font-size:1.556em;margin-bottom:30px}h4{font-size:1.222em;margin-bottom:36px}h5{font-size:.889em}.btn,h6{font-size:.778em}p{line-height:1.667em;letter-spacing:.046em;margin:0 0 30px}p.lead{font-size:1em;line-height:1.69em;letter-spacing:.05em;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=80)";filter:alpha(opacity=80)}a{opacity:.8;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=80)";filter:alpha(opacity=80);color:#373737;-webkit-transition:all .3s;-moz-transition:all .3s;-ms-transition:all .3s;-o-transition:all .3s;transition:all .3s}.btn:active,a:focus,a:hover{-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"}a:focus,a:hover{opacity:1;filter:alpha(opacity=100);text-decoration:none;color:inherit;outline:0;-webkit-transition:opacity .3s;-moz-transition:opacity .3s;-ms-transition:opacity .3s;-o-transition:opacity .3s;transition:opacity .3s}a:active{color:#a0a0a0;-webkit-transition:opacity .3s;-moz-transition:opacity .3s;-ms-transition:opacity .3s;-o-transition:opacity .3s;transition:opacity .3s}.btn{margin-bottom:20px;background:0 0;opacity:.8;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=80)";filter:alpha(opacity=80);-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;border-radius:0;font-weight:700;border:0;border-left:2px solid;border-right:2px solid;padding:9px 27px;position:relative;text-align:center;-webkit-transition:opacity .3s,padding .3s;-moz-transition:opacity .3s,padding .3s;-ms-transition:opacity .3s,padding .3s;-o-transition:opacity .3s,padding .3s;transition:opacity .3s,padding .3s}.btn:after,.btn:before{border-top:2px solid;border-bottom:2px solid}.btn:active{opacity:1;filter:alpha(opacity=100);-webkit-box-shadow:none;box-shadow:none;-webkit-transition:padding .3s;-moz-transition:padding .3s;-ms-transition:padding .3s;-o-transition:padding .3s;transition:padding .3s}.btn:active:hover,.btn:hover{color:inherit;padding:9px 37px;opacity:1;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";filter:alpha(opacity=100);-webkit-transition:opacity .3s,padding .3s;-moz-transition:opacity .3s,padding .3s;-ms-transition:opacity .3s,padding .3s;-o-transition:opacity .3s,padding .3s;transition:opacity .3s,padding .3s}.btn:after,.btn:before{content:'';position:absolute;width:5px;top:0;height:100%}body,body main.fullpage section .section-content-wrapper{position:relative}.btn:before{left:0}.btn:after{right:0}.btn[aria-label=Close]{padding-top:14px;padding-bottom:14px}.btn[aria-label=Close]:hover{padding:14px 37px}.btn[aria-label=Close] img{display:block}.btn-reset{background-color:transparent;-webkit-box-shadow:none;box-shadow:none;border:0;padding:0}.btn-reset:active:focus,.btn-reset:focus,.btn:active:focus,.btn:focus{outline-offset:0;outline:0;color:inherit}body{font-weight:300;color:#fff;font-size:18px;text-align:center;padding-right:0!important}body main.fullpage section{opacity:0;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";filter:alpha(opacity=0);visibility:hidden}body main.fullpage section .section-content-wrapper .section-content{display:table-cell;vertical-align:middle}body main section{padding-top:120px}body main section .section-title{font-size:2.778em;margin-bottom:60px}body main section .section-content-title{letter-spacing:.05em;margin-bottom:25px}body main section p.lead{margin-bottom:62px}.copyright p{opacity:.6;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=60)";filter:alpha(opacity=60);padding-bottom:48px;padding-top:48px;font-size:.778em;margin:0} -------------------------------------------------------------------------------- /HelloTrevi/views/helloworld.ssp: -------------------------------------------------------------------------------- 1 | 2 | 3 | <%=title%> 4 | 5 | 6 |

Hello World!

7 |

Current time : <%=time%>

8 |

Number : <%=number%>

9 | 10 | -------------------------------------------------------------------------------- /HelloTrevi/views/index.ssp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | <%=title%> 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 20 | 21 |
22 |
23 |
24 | 25 |
27 |
28 |
30 |
31 |

Trevi

32 | 33 |

Lime version

34 | 35 |

36 | Trevi is a minimal and flexible Swift web application server
37 | that provides Swift server page and MVC module.
38 | Trevi uses and event-driven, non-blocking I/O model based on GCD.
39 | Swift 2.0 and xCode 7.1.1 required or latest release.
40 |

41 | 42 | 43 |
44 |
45 | 46 | 49 |
50 | 51 |
52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /Lime/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSHumanReadableCopyright 24 | Copyright © 2016년 LeeYoseob. All rights reserved. 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Lime/Lime.h: -------------------------------------------------------------------------------- 1 | // 2 | // Lime.h 3 | // Lime 4 | // 5 | // Created by LeeYoseob on 2016. 2. 3.. 6 | // Copyright © 2016년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for Lime. 12 | FOUNDATION_EXPORT double LimeVersionNumber; 13 | 14 | //! Project version string for Lime. 15 | FOUNDATION_EXPORT const unsigned char LimeVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Lime/Lime/Layer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Layer.swift 3 | // Trevi 4 | // 5 | // Created by LeeYoseob on 2016. 3. 2.. 6 | // Copyright © 2016년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Trevi 11 | 12 | /* 13 | Reuse for becoming more useful results Middleware and all the routes that object is wrapped with this class. 14 | 15 | */ 16 | public class Layer { 17 | 18 | private var handle: HttpCallback? 19 | public var path: String! = "" 20 | public var regexp: RegExp! 21 | public var name: String! 22 | public var route: Route? 23 | public var method: HTTPMethodType! = .UNDEFINED 24 | 25 | public var keys: [String]? // params key ex path/:name , name is key 26 | public var params: [String: String]? 27 | 28 | public init(path: String ,name: String? = "function", options: Option? = nil, fn: HttpCallback){ 29 | setupAfterInit(path, opt: options, name: name, fn: fn) 30 | 31 | } 32 | public init(path: String, options: Option? = nil, module: Middleware){ 33 | setupAfterInit(path, opt: options, name: module.name.rawValue, fn: module.handle) 34 | 35 | } 36 | 37 | private func setupAfterInit(p: String, opt: Option? = nil, name: String?, fn: HttpCallback){ 38 | self.handle = fn 39 | self.path = p 40 | self.name = name 41 | 42 | //create regexp 43 | regexp = self.pathRegexp(path, option: opt) 44 | 45 | if path == "/" && opt?.end == false { 46 | regexp.fastSlash = true 47 | } 48 | } 49 | 50 | private func pathRegexp(path: String, option: Option!) -> RegExp{ 51 | // create key, and append key when create regexp 52 | keys = [String]() 53 | 54 | if path.length() > 1 { 55 | for param in searchWithRegularExpression(path, pattern: ":([^\\/]*)") { 56 | keys!.append(param["$1"]!.text) 57 | } 58 | } 59 | 60 | return RegExp(path: path) 61 | } 62 | 63 | //handle mathed route module 64 | public func handleRequest(req: IncomingMessage , res: ServerResponse, next: NextCallback){ 65 | let function = self.handle 66 | function!(req,res,next) 67 | } 68 | 69 | //Request url meching. 70 | public func match(path: String?) -> Bool{ 71 | 72 | guard path != nil else { 73 | self.params = nil 74 | self.path = nil 75 | return false 76 | } 77 | 78 | guard (self.regexp.fastSlash) == false else { 79 | self.path = "" 80 | self.params = [String: String]() 81 | return true 82 | } 83 | 84 | var ret: [String]! = self.regexp.exec(path!) 85 | 86 | guard ret != nil else{ 87 | self.params = nil 88 | self.path = nil 89 | return false 90 | } 91 | 92 | self.path = ret[0] 93 | self.params = [String: String]() 94 | ret.removeFirst() 95 | 96 | var idx = 0 97 | var key: String! = "" 98 | for value in ret { 99 | key = keys![idx] 100 | idx += 1 101 | if key == nil { 102 | break 103 | } 104 | params![key] = value 105 | key = nil 106 | } 107 | 108 | return true 109 | } 110 | 111 | } 112 | 113 | -------------------------------------------------------------------------------- /Lime/Lime/Lime.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Trevi.swift 3 | // Trevi 4 | // 5 | // Created by LeeYoseob on 2015. 12. 7.. 6 | // Copyright © 2015년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Trevi 11 | 12 | /* 13 | For Trevi users, allow routing and to apply middlewares without difficulty. 14 | */ 15 | 16 | public class Lime : Routable { 17 | 18 | public var setting: [String: AnyObject]! 19 | 20 | public var router: Router{ 21 | let r = self._router 22 | if let r = r { 23 | return r 24 | } 25 | return Router() 26 | } 27 | 28 | public override init () { 29 | super.init() 30 | lazyRouter() 31 | } 32 | 33 | private func lazyRouter(){ 34 | guard _router == nil else { 35 | return 36 | } 37 | _router = Router() 38 | _router.use(md: Query()) 39 | } 40 | 41 | public func use(middleware: Middleware) { 42 | _router.use(md: middleware) 43 | } 44 | 45 | #if os(Linux) 46 | public func set(name: String, _ val: String){ 47 | if setting == nil { 48 | setting = [String: AnyObject]() 49 | } 50 | setting[name] = StringWrapper(string: val) 51 | } 52 | #endif 53 | 54 | public func set(name: String, _ val: AnyObject){ 55 | if setting == nil { 56 | setting = [String: AnyObject]() 57 | } 58 | setting[name] = val 59 | } 60 | 61 | public func handle(req: IncomingMessage, res: ServerResponse, next: NextCallback?){ 62 | 63 | var done: NextCallback? = next 64 | 65 | if next == nil{ 66 | func finalHandler() { 67 | res.statusCode = 404 68 | let msg = "Not Found 404" 69 | res.write(msg) 70 | res.end() 71 | } 72 | done = finalHandler 73 | 74 | req.app = self 75 | } 76 | 77 | return self._router.handle(req,res: res,next: done!) 78 | } 79 | } 80 | 81 | // Needed to activate lime in the Trevi Fountain. 82 | extension Lime: ApplicationProtocol { 83 | public func createApplication() -> Any { 84 | return self.handle 85 | } 86 | } 87 | 88 | 89 | 90 | // For Lime extension ServerResponse 91 | extension ServerResponse { 92 | 93 | // Lime recommend using that send rather than using write 94 | public func send(data: String, encoding: String! = nil, type: String! = ""){ 95 | write(data, encoding: encoding, type: type) 96 | endReuqstAndClean() 97 | } 98 | 99 | public func send(data: NSData, encoding: String! = nil, type: String! = ""){ 100 | write(data, encoding: encoding, type: type) 101 | endReuqstAndClean() 102 | } 103 | 104 | public func send(data: [String : String], encoding: String! = nil, type: String! = ""){ 105 | write(data, encoding: encoding, type: type) 106 | endReuqstAndClean() 107 | } 108 | 109 | private func endReuqstAndClean(){ 110 | end() 111 | if req.files != nil { 112 | for file in self.req.files.values{ 113 | FSBase.unlink(path: file.path) 114 | } 115 | } 116 | } 117 | 118 | public func render(path: String, args: [String:String]? = nil) { 119 | if let app = req.app as? Lime, let render = app.setting["view engine"] as? Render { 120 | var entirePath = path 121 | #if os(Linux) 122 | if let abpath = app.setting["views"] as? StringWrapper { 123 | entirePath = "\(abpath.string)/\(entirePath)" 124 | } 125 | #else 126 | if let bundlePath = NSBundle.mainBundle().pathForResource(NSURL(fileURLWithPath: path).lastPathComponent!, ofType: nil) { 127 | entirePath = bundlePath 128 | } 129 | #endif 130 | 131 | if args != nil { 132 | render.render(entirePath, args: args!) { data in 133 | self.write(data) 134 | } 135 | } else { 136 | render.render(entirePath) { data in 137 | self.write(data) 138 | } 139 | } 140 | } 141 | end() 142 | } 143 | 144 | public func redirect(url: String){ 145 | self.writeHead(302, headers: [Location:url]) 146 | self.end() 147 | } 148 | } 149 | 150 | 151 | //extention incomingMessage for lime 152 | extension IncomingMessage { 153 | 154 | } 155 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /Lime/Lime/LimePrepare.swift: -------------------------------------------------------------------------------- 1 | //// 2 | //// LimePrepare.swift 3 | //// Trevi 4 | //// 5 | //// Created by LeeYoseob on 2016. 2. 17.. 6 | //// Copyright © 2016년 LeeYoseob. All rights reserved. 7 | //// 8 | // 9 | // 10 | //import Foundation 11 | //import Trevi 12 | // 13 | //public class LimePrepare { 14 | // let prepare = PreparedHttp() 15 | // var eventListener: EventListener? 16 | // var totalLength = 0 17 | // 18 | // public init(){} 19 | // public convenience init(elistener: EventListener){ 20 | // self.init() 21 | // eventListener = elistener 22 | // } 23 | // 24 | // public func appendData(info: EventInfo) ->Int { 25 | // 26 | // if let readData = info.params { 27 | // self.totalLength += readData.length 28 | // if readData.length > 0 { 29 | // let (contentLength, headerLength) = prepare.appendReadData(readData) 30 | // if contentLength > headerLength{ 31 | // self.totalLength -= headerLength 32 | // } 33 | // if self.totalLength >= contentLength || contentLength == 0{ 34 | // shootRequest(info.stream!) 35 | // } 36 | // } 37 | // return readData.length; 38 | // } 39 | // return 0 40 | // } 41 | // 42 | // private func reset(){ 43 | // self.prepare.dInit() 44 | // self.totalLength = 0 45 | // } 46 | // 47 | // private func shootRequest(stream: SocketStream){ 48 | // let httpClient = ClientSocket ( socket: stream ) 49 | // //@Danger 50 | // MiddlewareManager.sharedInstance ().handleRequest(prepare.handleRequest(httpClient)) 51 | // reset() 52 | // } 53 | //} 54 | -------------------------------------------------------------------------------- /Lime/Lime/Middleware/BodyParser.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BodyParser.swift 3 | // Trevi 4 | // 5 | // Created by LeeYoseob on 2015. 11. 30.. 6 | // Copyright © 2015년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Trevi 11 | 12 | private protocol parseAble{ 13 | func parse() -> [String:AnyObject!]! 14 | } 15 | 16 | struct ParserdData { 17 | var name : String? 18 | var value : String? 19 | var type : String? 20 | var data : NSData? 21 | } 22 | 23 | 24 | /* 25 | This class is the middleware as one of the most important 26 | Consisting of many ways is easily allows us to write the user body. 27 | Now Support Json, Text, urlencoded parser 28 | */ 29 | 30 | 31 | public class BodyParser: Middleware{ 32 | 33 | public var name = MiddlewareName.BodyParser 34 | 35 | public init(){ 36 | 37 | } 38 | 39 | public func handle(req: IncomingMessage, res: ServerResponse, next: NextCallback?) { 40 | 41 | } 42 | 43 | public static func getBody(req: IncomingMessage, _ cb: (body: String)->()){ 44 | 45 | var body = "" 46 | func ondata(dt : String){ 47 | body += dt 48 | } 49 | 50 | func onend(){ 51 | cb(body: body) 52 | } 53 | req.on("data", ondata) 54 | req.on("end", onend) 55 | } 56 | 57 | public static func read(req: IncomingMessage, _ next: NextCallback?, parse: ((req: IncomingMessage, next: NextCallback , body: String!)->())){ 58 | getBody(req) { body in 59 | parse(req: req, next: next!, body: body) 60 | 61 | } 62 | } 63 | 64 | public static func urlencoded() -> HttpCallback{ 65 | func parse(req: IncomingMessage, _ next: NextCallback? , _ bodyData: String!){ 66 | var body = bodyData 67 | if body != nil { 68 | 69 | if body.containsString(CRLF){ 70 | body.removeAtIndex(body.endIndex.predecessor()) 71 | } 72 | var resultBody = [String:String]() 73 | for component in body.componentsSeparatedByString("&") { 74 | let trim = component.componentsSeparatedByString("=") 75 | resultBody[trim.first!] = trim.last! 76 | } 77 | req.body = resultBody 78 | 79 | next!() 80 | }else { 81 | 82 | } 83 | } 84 | 85 | func urlencoded(req: IncomingMessage, res: ServerResponse, next: NextCallback?) { 86 | guard req.header[Content_Type] == "application/x-www-form-urlencoded" else { 87 | return next!() 88 | } 89 | 90 | guard req.hasBody == true else{ 91 | return next!() 92 | } 93 | 94 | guard req.method == .POST || req.method == .PUT else{ 95 | return next!() 96 | } 97 | 98 | read(req, next!,parse: parse) 99 | } 100 | return urlencoded 101 | 102 | } 103 | 104 | 105 | public static func json() -> HttpCallback { 106 | 107 | func parse(req: IncomingMessage, _ next: NextCallback? , _ body: String!){ 108 | do { 109 | 110 | let data = body.dataUsingEncoding(NSUTF8StringEncoding) 111 | let result = try NSJSONSerialization.JSONObjectWithData (data! , options: .AllowFragments ) as? [String:String] 112 | if let ret = result { 113 | req.json = ret 114 | return next!() 115 | }else { 116 | // error handle 117 | } 118 | } catch { 119 | 120 | } 121 | } 122 | 123 | func jsonParser(req: IncomingMessage, res: ServerResponse, next: NextCallback?) { 124 | guard req.header[Content_Type] == "application/json" else { 125 | return next!() 126 | } 127 | 128 | guard req.hasBody == true else{ 129 | return next!() 130 | } 131 | 132 | guard req.method == .POST || req.method == .PUT else{ 133 | return next!() 134 | } 135 | 136 | read(req, next!,parse: parse) 137 | } 138 | return jsonParser 139 | } 140 | 141 | 142 | public static func text() -> HttpCallback{ 143 | 144 | func parse(req: IncomingMessage, _ next: NextCallback? , _ body: String!){ 145 | 146 | if let ret = body { 147 | req.bodyText = ret 148 | return next!() 149 | }else { 150 | // error handle 151 | } 152 | } 153 | 154 | func textParser(req: IncomingMessage, res: ServerResponse, next: NextCallback?) { 155 | guard req.header[Content_Type] == "text/plain" else { 156 | return next!() 157 | } 158 | 159 | guard req.hasBody == true else{ 160 | return next!() 161 | } 162 | 163 | guard req.method == .POST || req.method == .PUT else{ 164 | return next!() 165 | } 166 | 167 | read(req, next!,parse: parse) 168 | } 169 | 170 | return textParser 171 | } 172 | 173 | } 174 | 175 | 176 | 177 | 178 | 179 | -------------------------------------------------------------------------------- /Lime/Lime/Middleware/Favicon.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Favicon.swift 3 | // Trevi 4 | // 5 | // Created by LeeYoseob on 2015. 12. 5.. 6 | // Copyright © 2015년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Trevi 11 | 12 | // use for favicon.io 13 | public class Favicon: Middleware { 14 | 15 | public var name: MiddlewareName = .Favicon; 16 | 17 | public init () { 18 | 19 | } 20 | 21 | public func handle(req: IncomingMessage, res: ServerResponse, next: NextCallback?) { 22 | 23 | if req.url == "/favicon.ico" { 24 | 25 | #if os(Linux) 26 | 27 | #else 28 | guard let bundlePath = NSBundle.mainBundle().pathForResource(NSURL(fileURLWithPath: req.url).lastPathComponent!, ofType: nil) else{ 29 | return next!() 30 | } 31 | #endif 32 | 33 | let file = FileSystem.ReadStream(path: bundlePath) 34 | 35 | let faviconData :NSMutableData! = NSMutableData() 36 | file?.onClose() { handle in 37 | res.send(faviconData,type: "image/x-icon") 38 | } 39 | 40 | file?.readStart() { error, data in 41 | if error == 0{ 42 | faviconData.appendData(data) 43 | }else{ 44 | next!() 45 | } 46 | } 47 | 48 | }else{ 49 | next!() 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /Lime/Lime/Middleware/Logger.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Logger.swift 3 | // Trevi 4 | // 5 | // Created by SeungHyun Lee on 2016. 1. 5.. 6 | // Copyright © 2016년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Trevi 11 | 12 | public typealias LoggerProccessor = (IncomingMessage, ServerResponse) -> String 13 | public var funcTbl: [String : LoggerProccessor] = [ 14 | "http-version" : log_http_version, 15 | "response-time" : log_response_time, 16 | "remote-addr" : log_remote_addr, 17 | "date" : log_date, 18 | "method" : log_method, 19 | "url" : log_url, 20 | "referrer" : log_referrer, 21 | "user-agent" : log_user_agent, 22 | "status" : log_status 23 | ] 24 | 25 | /** 26 | * 27 | * A Middleware for logging client connection. 28 | * 29 | */ 30 | public class Logger: Middleware { 31 | 32 | public var name: MiddlewareName 33 | public let format: String 34 | private let logFile: FileSystem.WriteStream? 35 | 36 | public init (format: String) { 37 | name = .Logger 38 | 39 | switch (format) { 40 | case "default": 41 | self.format = ":remote-addr - - [ :date ] \":method :url HTTP/:http-version\" :status :res[content-length] \":referrer\" \":user-agent\"" 42 | case "short": 43 | self.format = ":remote-addr - :method :url HTTP/:http-version :status :res[content-length] - :response-time ms" 44 | case "tiny": 45 | self.format = ":method :url :status :res[content-length] - :response-time ms" 46 | default: 47 | self.format = format 48 | } 49 | 50 | let filename = "\(__dirname)/log_\(getCurrentDatetime("yyyyMMdd_hhmmss")).log" 51 | logFile = FileSystem.WriteStream(path: filename) 52 | } 53 | 54 | deinit { 55 | logFile?.close() 56 | } 57 | 58 | public func handle(req: IncomingMessage, res: ServerResponse, next: NextCallback?) -> () { 59 | res.onFinished = requestLog 60 | next!() 61 | } 62 | 63 | /** 64 | * 65 | * Make log with the format. 66 | * 67 | * - Parameter response: Can be a source to make a log and also be a 68 | * destination. 69 | * 70 | */ 71 | private func requestLog(response res: ServerResponse) { 72 | let log = compileLog(self, req: res.req, res: res) 73 | logFile?.writeData(("\(log)\n".dataUsingEncoding(NSUTF8StringEncoding)!)) 74 | print(log) 75 | } 76 | } 77 | 78 | private func compileLog(logger: Logger, req: IncomingMessage, res: ServerResponse) -> String { 79 | var isCompiled = false 80 | var compiled = String(logger.format) 81 | 82 | for tokens in searchWithRegularExpression(logger.format, pattern: ":res\\[(.*?)\\]", options: [ .CaseInsensitive ]) { 83 | for type in HttpHeaderType.allValues where type.rawValue.lowercaseString == tokens["$1"]!.text.lowercaseString { 84 | guard let logPiece : String = res.header[ type.rawValue ] else { 85 | compiled = compiled.stringByReplacingOccurrencesOfString( ":res[\(tokens["$1"]!.text)]", withString: "" ) 86 | continue 87 | } 88 | 89 | compiled = compiled.stringByReplacingOccurrencesOfString( ":res[\(tokens["$1"]!.text)]", withString: logPiece ) 90 | isCompiled = true 91 | } 92 | } 93 | 94 | for tokens in searchWithRegularExpression(logger.format, pattern: ":([A-z0-9\\-]*)", options: [ .CaseInsensitive ]) { 95 | // get function by token 96 | guard let tokenFunc = funcTbl[tokens["$1"]!.text.lowercaseString] else { 97 | compiled = compiled.stringByReplacingOccurrencesOfString( ":\(tokens["$1"]!.text)", withString: "" ) 98 | continue 99 | } 100 | 101 | compiled = compiled.stringByReplacingOccurrencesOfString( ":\(tokens["$1"]!.text)", withString: tokenFunc(req, res) ) 102 | isCompiled = true 103 | } 104 | 105 | return isCompiled ? compiled : "" 106 | } 107 | 108 | private func log_http_version ( req: IncomingMessage, res: ServerResponse ) -> String { 109 | return req.version 110 | } 111 | 112 | private func log_response_time ( req: IncomingMessage, res: ServerResponse ) -> String { 113 | let elapsedTime = Double( res.startTime.timeIntervalSinceDate( req.startTime ) ) 114 | return "\(elapsedTime * 1000)" 115 | } 116 | 117 | private func log_remote_addr ( req: IncomingMessage, res: ServerResponse ) -> String { 118 | guard let addr = getEndpointFromSocketAddress(Tcp.getPeerName(uv_tcp_ptr(req.socket.handle))) else { 119 | return "" 120 | } 121 | return addr.host 122 | } 123 | 124 | private func log_date ( req: IncomingMessage, res: ServerResponse ) -> String { 125 | return getCurrentDatetime() 126 | } 127 | 128 | private func log_method ( req: IncomingMessage, res: ServerResponse ) -> String { 129 | return req.method.rawValue 130 | } 131 | 132 | private func log_url ( req: IncomingMessage, res: ServerResponse ) -> String { 133 | return req.url 134 | } 135 | 136 | private func log_referrer ( req: IncomingMessage, res: ServerResponse ) -> String { 137 | if let referer = req.header["referer"] { 138 | return referer 139 | } else if let referrer = req.header["referrer"] { 140 | return referrer 141 | } else { 142 | return "" 143 | } 144 | } 145 | 146 | private func log_user_agent ( req: IncomingMessage, res: ServerResponse ) -> String { 147 | if let agent = req.header["user-agent"] { 148 | return agent 149 | } else { 150 | return "" 151 | } 152 | } 153 | 154 | private func log_status ( req: IncomingMessage, res: ServerResponse ) -> String { 155 | return "\(res.statusCode)" 156 | } -------------------------------------------------------------------------------- /Lime/Lime/Middleware/Middleware.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Middleware.swift 3 | // Trevi 4 | // 5 | // Created by LeeYoseob on 2015. 11. 30.. 6 | // Copyright © 2015년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Trevi 11 | 12 | public enum MiddlewareName: String { 13 | case Query = "query" 14 | case Err = "error" 15 | case Undefined = "undefined" 16 | case Favicon = "favicon" 17 | case BodyParser = "bodyParser" 18 | case Logger = "logger" 19 | case Json = "json" 20 | case CookieParser = "cookieParser" 21 | case Session = "session" 22 | case SwiftServerPage = "swiftServerPage" 23 | case Trevi = "trevi" 24 | case Router = "router" 25 | case ServeStatic = "serveStatic" 26 | // else... 27 | } 28 | 29 | 30 | 31 | /* 32 | 33 | Middleware is an easy and fast to using the server can offer the many functions. 34 | Should be implemented handle in order to use as middleware. 35 | */ 36 | public protocol Middleware{ 37 | var name: MiddlewareName { get set } 38 | func handle(req: IncomingMessage, res: ServerResponse, next: NextCallback?) -> () 39 | } 40 | -------------------------------------------------------------------------------- /Lime/Lime/Middleware/Query.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Query.swift 3 | // Trevi 4 | // 5 | // Created by LeeYoseob on 2016. 3. 2.. 6 | // Copyright © 2016년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Trevi 11 | 12 | // defualt middleware, parse Query 13 | class Query: Middleware { 14 | var name: MiddlewareName = .Query 15 | init(){ 16 | } 17 | 18 | func handle(req: IncomingMessage, res: ServerResponse, next: NextCallback?) { 19 | // Parsing url query by using regular expression. 20 | queryParse(req.url) { query in 21 | req.query = query 22 | req.url = (req.url.componentsSeparatedByString( "?" ) as [String])[0] 23 | next!() 24 | } 25 | } 26 | } 27 | public func queryParse(src: String , cb: ([ String: String ])->()) { 28 | 29 | var result = [String: String]() 30 | if let regex: NSRegularExpression = try? NSRegularExpression ( pattern: "[&\\?](.+?)=([\(unreserved)\(gen_delims)\\!\\$\\'\\(\\)\\*\\+\\,\\;]*)", options: [ .CaseInsensitive ] ) { 31 | 32 | for match in regex.matchesInString ( src, options: [], range: NSMakeRange( 0, src.length() ) ) { 33 | let keyRange = match.rangeAtIndex( 1 ) 34 | let valueRange = match.rangeAtIndex( 2 ) 35 | let key = src.substring ( keyRange.location, length: keyRange.length ) 36 | let value = src.substring ( valueRange.location, length: valueRange.length ) 37 | result.updateValue ( value.stringByRemovingPercentEncoding!, forKey: key.stringByRemovingPercentEncoding! ) 38 | } 39 | } 40 | 41 | return cb(result) 42 | 43 | } 44 | -------------------------------------------------------------------------------- /Lime/Lime/Middleware/Router.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Router.swift 3 | // Trevi 4 | // 5 | // Created by LeeYoseob on 2015. 11. 30.. 6 | // Copyright © 2015년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Trevi 11 | 12 | /* 13 | One of the Middleware class to the path to ensure that able to handle the user defined url 14 | However, it's not find the path to the running is trevi all save the path at this class when the server is starting on the go. 15 | so it is like lazyRouter 16 | 17 | */ 18 | 19 | public class Router: Middleware{ 20 | public var methods = [HTTPMethodType]() 21 | public var name: MiddlewareName = .Router 22 | private var stack = [Layer]() 23 | 24 | public init(){} 25 | public func handle(req: IncomingMessage, res: ServerResponse, next: NextCallback? ) { 26 | 27 | var idx = 0 28 | var options = [HTTPMethodType:Int]() 29 | var removed = "" 30 | var slashAdd = false 31 | 32 | var parantParams = req.params 33 | var parantUrl = req.baseUrl 34 | var done = next 35 | 36 | req.baseUrl = parantUrl 37 | req.originUrl = req.originUrl.length() == 0 ? req.url : req.originUrl 38 | 39 | func trimPrefix(layer: Layer , layerPath: String, path: String){ 40 | 41 | let nextPrefix: String! = path == layerPath ? "/" : path.substring(layerPath.length(), length: 1) 42 | 43 | if nextPrefix != nil && nextPrefix != "/" { 44 | done!() 45 | return 46 | } 47 | 48 | if layerPath.length() > 0 { 49 | removed = layerPath 50 | req.baseUrl = parantUrl 51 | let removedPathLen = removed.length() 52 | 53 | req.url = req.url == layerPath ? "/": path.substring(removedPathLen, length: path.length() - removedPathLen) 54 | 55 | if req.url.substring(0, length: 1) != "/" { 56 | req.url = ("/"+req.url) 57 | slashAdd = true 58 | } 59 | req.baseUrl = removed 60 | } 61 | 62 | layer.handleRequest(req, res: res, next: nextHandle) 63 | } 64 | 65 | func nextHandle(){ 66 | 67 | if removed.length() != 0 { 68 | req.baseUrl = parantUrl 69 | removed = "" 70 | } 71 | 72 | if idx > self.stack.count{ 73 | return 74 | } 75 | 76 | let path = getPathname(req) 77 | var layer: Layer! 78 | var match: Bool! 79 | var route: Route! 80 | 81 | while match != true && idx < stack.count{ 82 | layer = stack[idx] 83 | idx += 1 84 | match = matchLayer(layer, path: path) 85 | route = layer.route 86 | 87 | if (match != true) || (route == nil ) { 88 | continue 89 | } 90 | 91 | let method = req.method 92 | let hasMethod = route.handlesMethod(method) 93 | 94 | if hasMethod && method == .OPTIONS { 95 | for method in route.options() { 96 | options[method] = 1 97 | } 98 | } 99 | 100 | } 101 | 102 | if match == nil || match == false { 103 | return done!() 104 | } 105 | 106 | if route != nil { 107 | req.route = route 108 | } 109 | 110 | if layer.params != nil{ 111 | var params = layer.params 112 | if parantParams != nil { 113 | params = mergeParams(layer.params, src: parantParams) 114 | } 115 | req.params = params 116 | } 117 | 118 | let layerPath = layer.path 119 | 120 | #if os(Linux) 121 | poccessParams(layer, paramsCalled: StringWrapper(string: ""), req: req, res: res) { err in 122 | if err != nil { 123 | return nextHandle() 124 | } 125 | 126 | if route != nil { 127 | return layer.handleRequest(req, res: res, next: nextHandle) 128 | } 129 | 130 | trimPrefix(layer, layerPath: layerPath, path: path) 131 | } 132 | #else 133 | poccessParams(layer, paramsCalled: "", req: req, res: res) { err in 134 | if err != nil { 135 | return nextHandle() 136 | } 137 | 138 | if route != nil { 139 | return layer.handleRequest(req, res: res, next: nextHandle) 140 | } 141 | 142 | trimPrefix(layer, layerPath: layerPath, path: path) 143 | } 144 | #endif 145 | } 146 | nextHandle() 147 | } 148 | 149 | private func mergeParams(dest: [String: String]? , src: [String: String]?) -> [String: String]?{ 150 | var _dest = dest 151 | for (k,v) in src! { 152 | _dest![k] = v 153 | } 154 | return _dest 155 | } 156 | 157 | private func poccessParams(layer: Layer, paramsCalled: AnyObject, req: IncomingMessage, res: ServerResponse, cb:((String?)->())){ 158 | cb(nil) 159 | } 160 | 161 | private func matchLayer(layer: Layer , path: String) -> Bool{ 162 | return layer.match(path) 163 | } 164 | 165 | private func getPathname(req: IncomingMessage) -> String{ 166 | //should parsing req.url 167 | return req.url! 168 | } 169 | 170 | func use(path: String? = "/", md: Middleware){ 171 | stack.append(Layer(path: path!, options: Option(end: false), module: md)) 172 | } 173 | 174 | func use(fns: HttpCallback...){ 175 | for fn in fns { 176 | stack.append(Layer(path: "/", name: "function", options: Option(end: false), fn: fn)) 177 | } 178 | } 179 | 180 | 181 | public func all ( path: String, _ callback: HttpCallback... ) { 182 | 183 | } 184 | /** 185 | * Support http ver 1.1/1.0 186 | */ 187 | public func get (path: String, _ callback: HttpCallback) { 188 | boundDispatch(path, callback , .GET) 189 | } 190 | /** 191 | * Support http ver 1.1/1.0 192 | */ 193 | 194 | public func post ( path: String, _ middleWare: Middleware? = nil, _ callback: HttpCallback ) { 195 | boundDispatch(path, callback, .POST, middleWare) 196 | } 197 | /** 198 | * Support http ver 1.1/1.0 199 | */ 200 | public func put ( path: String, _ callback: HttpCallback ) { 201 | boundDispatch(path, callback , .PUT) 202 | } 203 | /** 204 | * Support http ver 1.1/1.0 205 | */ 206 | public func head ( path: String, _ callback: HttpCallback... ) { 207 | 208 | } 209 | /** 210 | * Support http ver 1.1/1.0 211 | */ 212 | public func delete ( path: String, _ callback: HttpCallback... ) { 213 | 214 | } 215 | 216 | 217 | private func boundDispatch(path: String, _ callback: HttpCallback, _ method: HTTPMethodType , _ md: Middleware? = nil ){ 218 | let route = Route(method: method, path) 219 | if let middleware = md { 220 | 221 | let newMiddleWare = Layer(path: "/", options: Option(end: false), module: middleware) 222 | newMiddleWare.method = method 223 | route.stack.append(newMiddleWare) 224 | } 225 | 226 | methods.append(method) 227 | route.method = method 228 | route.dispatch = callback 229 | let layer = Layer(path: path, name: "bound dispatch", options: Option(end: true), fn: route.dispatchs) 230 | layer.route = route 231 | stack.append(layer) 232 | } 233 | 234 | } 235 | 236 | -------------------------------------------------------------------------------- /Lime/Lime/Middleware/ServeStatic.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ServeStatic.swift 3 | // Trevi 4 | // 5 | // Created by SeungHyun Lee on 2015. 12. 27.. 6 | // Copyright © 2015년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Trevi 11 | 12 | /** 13 | A Middleware for serving static files in server like .css, .js, .html, etc. 14 | */ 15 | public class ServeStatic: Middleware { 16 | 17 | public var name: MiddlewareName 18 | private let basePath: String 19 | 20 | public init (path: String) { 21 | name = .ServeStatic 22 | 23 | if let last = path.characters.last where last == "/" { 24 | basePath = path[path.startIndex ..< path.endIndex.advancedBy(-1)] 25 | } else { 26 | basePath = path 27 | } 28 | } 29 | 30 | public func handle(req: IncomingMessage, res: ServerResponse, next: NextCallback?) { 31 | var entirePath = req.url 32 | #if os(Linux) 33 | entirePath = "\(basePath)/\(req.url)" 34 | #else 35 | if let bundlePath = NSBundle.mainBundle().pathForResource(NSURL(fileURLWithPath: req.url).lastPathComponent!, ofType: nil) { 36 | entirePath = bundlePath 37 | } 38 | #endif 39 | 40 | let file = FileSystem.ReadStream(path: entirePath) 41 | let buf = NSMutableData() 42 | 43 | file?.onClose() { handle in 44 | return res.send(buf) 45 | } 46 | 47 | file?.readStart() { error, data in 48 | buf.appendData(data) 49 | } 50 | next!() 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Lime/Lime/Middleware/SwiftServerPage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftServerPage.swift 3 | // Trevi 4 | // 5 | // Created by SeungHyun Lee on 2015. 12. 5.. 6 | // Copyright © 2015년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Trevi 11 | 12 | /** 13 | * 14 | * A Middleware to compile a specific SSP(Swift Server Page) file and send the 15 | * data to client. 16 | * 17 | */ 18 | public class SwiftServerPage: Render { 19 | 20 | public init() { 21 | } 22 | 23 | /** 24 | * 25 | * Get a compiled result of a SSP(Swift Server Page) file from the specific 26 | * path with the argument. 27 | * 28 | * - Parameter path: A path where file that is read is located 29 | * 30 | * - Returns: A string initialized by compiled swift server page data from 31 | * the file specified by path. 32 | * 33 | */ 34 | public func render(path: String, writer: ((String) -> Void)) { 35 | return render(path, args: [:], writer: writer) 36 | } 37 | 38 | /** 39 | * 40 | * Get a compiled result of a SSP(Swift Server Page) file from the specific 41 | * path with the argument. 42 | * 43 | * - Parameter path: A path where file that is read is located 44 | * - Parameter args: Arguments that will be using to compile SSP file. 45 | * - Parameter writer: Callback to send data to user. 46 | * 47 | * - Returns: A string initialized by compiled swift server page data from 48 | * the file specified by path. 49 | * 50 | */ 51 | public func render(path: String, args: [String:String], writer: ((String) -> Void)) { 52 | let file = FileSystem.ReadStream(path: path) 53 | let buf = NSMutableData() 54 | 55 | file?.onClose() { handle in 56 | let swiftCodes = convertToSwift(from: String(data: buf, encoding: NSUTF8StringEncoding)!, with: args) 57 | compileSwift(path, code: swiftCodes, callback: writer) 58 | } 59 | 60 | file?.readStart() { error, data in 61 | buf.appendData(data) 62 | } 63 | } 64 | } 65 | 66 | /** 67 | * 68 | * Get the Swift source codes from the specific SSP(Swift Server Page) file. 69 | * In this process, the SSP codes is divided into HTML codes and Swift codes. 70 | * After that, the HTML codes is wrapped by `print` function. An wrapped HTML 71 | * codes are combined with Swift code again. 72 | * 73 | * - Parameter ssp: The original data of SSP file which will be converted to a 74 | * Swift source code file. 75 | * - Parameter args: The list of arguments which is used at compiling. 76 | * 77 | * - Returns: The Swift source codes which are converted from SSP file with 78 | * arguments. 79 | * 80 | */ 81 | private func convertToSwift(from ssp: String, with args: [String:String]) -> String { 82 | var swiftCode: String = "" 83 | for key in args.keys { 84 | swiftCode += "var \(key) = \"\(args[key]!)\"\n" 85 | } 86 | 87 | var startIdx = ssp.startIndex 88 | 89 | let searched = searchWithRegularExpression( ssp, pattern: "(<%=?)[ \\t\\n]*([\\w\\W]+?)[ \\t\\n]*%>", options: [.CaseInsensitive] ) 90 | for dict in searched { 91 | let swiftTag, htmlTag: String 92 | 93 | if dict["$1"]!.text == "<%=" { 94 | swiftTag = "print(\(dict["$2"]!.text), terminator:\"\")" 95 | } else { 96 | swiftTag = dict["$2"]!.text 97 | } 98 | 99 | htmlTag = ssp[startIdx ..< ssp.startIndex.advancedBy ( dict["$0"]!.range.location )] 100 | .stringByReplacingOccurrencesOfString ( "\"", withString: "\\\"" ) 101 | .stringByReplacingOccurrencesOfString ( "\t", withString: "{@t}" ) 102 | .stringByReplacingOccurrencesOfString ( "\n", withString: "{@n}" ) 103 | 104 | swiftCode += "print(\"\(htmlTag)\", terminator:\"\")\n\(swiftTag)\n" 105 | 106 | startIdx = ssp.startIndex.advancedBy ( dict["$0"]!.range.location + dict["$0"]!.range.length ) 107 | } 108 | 109 | let htmlTag = ssp[startIdx ..< ssp.endIndex] 110 | .stringByReplacingOccurrencesOfString ( "\"", withString: "\\\"" ) 111 | .stringByReplacingOccurrencesOfString ( "\t", withString: "{@t}" ) 112 | .stringByReplacingOccurrencesOfString ( "\n", withString: "{@n}" ) 113 | 114 | return (swiftCode + "print(\"\(htmlTag)\")\n") 115 | } 116 | 117 | /** 118 | * 119 | * Run a callback as an argument to the results of compiling input code. 120 | * 121 | * - Parameter path: The path where compiled Swift codes will be located. 122 | * - Parameter code: Source codes which will be compiled. 123 | * - Parameter callback: Callback to send data to user. 124 | * 125 | */ 126 | private func compileSwift(path: String, code: String, callback: ((String) -> Void)) { 127 | let timestamp = Int(NSDate().timeIntervalSince1970 * 1000) 128 | let compileFile = "/tmp/\(NSURL(fileURLWithPath: path).lastPathComponent!)\(timestamp).swift" 129 | 130 | let file = FileSystem.WriteStream(path: compileFile) 131 | file?.writeData(code.dataUsingEncoding(NSUTF8StringEncoding)!) 132 | file?.close() 133 | 134 | #if os(Linux) 135 | if Glibc.system("bash -c \"source ~/.profile && swiftc \(compileFile) -o /tmp/ssp\(timestamp) && chmod +x /tmp/ssp\(timestamp)\"") == 0 { 136 | if Glibc.system("bash -c \"/tmp/ssp\(timestamp) > /tmp/ssp\(timestamp)_print\"") == 0 { 137 | let file = FileSystem.ReadStream(path: "/tmp/ssp\(timestamp)_print") 138 | let buf = NSMutableData() 139 | 140 | file?.onClose() { handle in 141 | if let result = String(data: buf, encoding: NSUTF8StringEncoding)? 142 | .stringByReplacingOccurrencesOfString ( "{@t}", withString: "\t" ) 143 | .stringByReplacingOccurrencesOfString ( "{@n}", withString: "\n" ) { 144 | callback(result) 145 | Glibc.remove("/tmp/ssp\(timestamp)_print") 146 | } 147 | } 148 | 149 | file?.readStart() { error, data in 150 | buf.appendData(data) 151 | } 152 | } 153 | } 154 | #else 155 | if Darwin.system("swiftc \(compileFile) -o /tmp/ssp\(timestamp)") == 0 { 156 | if Darwin.system("bash -c \"/tmp/ssp\(timestamp) > /tmp/ssp\(timestamp)_print\"") == 0 { 157 | let file = FileSystem.ReadStream(path: "/tmp/ssp\(timestamp)_print") 158 | let buf = NSMutableData() 159 | 160 | file?.onClose() { handle in 161 | if let result = String(data: buf, encoding: NSUTF8StringEncoding)? 162 | .stringByReplacingOccurrencesOfString ( "{@t}", withString: "\t" ) 163 | .stringByReplacingOccurrencesOfString ( "{@n}", withString: "\n" ) { 164 | callback(result) 165 | Darwin.remove("/tmp/ssp\(timestamp)_print") 166 | } 167 | } 168 | 169 | file?.readStart() { error, data in 170 | buf.appendData(data) 171 | } 172 | } 173 | } 174 | #endif 175 | } 176 | -------------------------------------------------------------------------------- /Lime/Lime/Regexp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Regexp.swift 3 | // Trevi 4 | // 5 | // Created by LeeYoseob on 2016. 3. 2.. 6 | // Copyright © 2016년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Trevi 11 | 12 | // Through a layer of regular expression in order to match. 13 | 14 | public class RegExp { 15 | public var fastSlash: Bool! // middleware only true 16 | public var source: String! // Regular expression for path 17 | public var originPath: String! 18 | 19 | public init() { 20 | self.fastSlash = false 21 | self.source = "" 22 | } 23 | 24 | public init(path: String) { 25 | fastSlash = false 26 | originPath = path 27 | 28 | if path.length() > 1 { 29 | // remove if the first of url is slash 30 | if path.characters.first == "/" { 31 | source = "^\\/*\(path[path.startIndex.successor() ..< path.endIndex])/?.*" 32 | } else { 33 | source = "^\\/*\(path)/?.*" 34 | } 35 | 36 | for param in searchWithRegularExpression(source, pattern: "(:[^\\/]+)") { 37 | source = source.stringByReplacingOccurrencesOfString(param["$1"]!.text, withString: "([^\\/]+)") 38 | } 39 | 40 | for param in searchWithRegularExpression(originPath, pattern: "(:[^\\/]+)") { 41 | originPath = originPath.stringByReplacingOccurrencesOfString(param["$1"]!.text, withString: ".*") 42 | } 43 | } 44 | } 45 | 46 | public func exec(path: String) -> [String]? { 47 | var result: [String]? = nil 48 | 49 | if (path == originPath) && path == "/" && source == nil { 50 | result = [path] 51 | return result 52 | } 53 | if source == nil { 54 | return result 55 | } 56 | 57 | for param in searchWithRegularExpression(path, pattern: "(\(originPath))(?:.*)") { 58 | if result == nil { 59 | result = [String]() 60 | result!.append(param["$1"]!.text) 61 | } 62 | 63 | for params in searchWithRegularExpression(path, pattern: source) { 64 | for idx in 1 ..< params.count { 65 | result!.append(params["$\(idx)"]!.text) 66 | } 67 | } 68 | } 69 | 70 | return result 71 | } 72 | } 73 | 74 | public struct Option{ 75 | public var end: Bool = false 76 | public init(end: Bool){ 77 | self.end = end 78 | } 79 | } 80 | 81 | 82 | -------------------------------------------------------------------------------- /Lime/Lime/Render.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Render.swift 3 | // Trevi 4 | // 5 | // Created by SeungHyunLee on 3/9/16. 6 | // Copyright © 2016 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol Render { 12 | func render(path: String, writer: ((String) -> Void)) 13 | func render(path: String, args: [String:String], writer: ((String) -> Void)) 14 | } -------------------------------------------------------------------------------- /Lime/Lime/Request.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Request.swift 3 | // Trevi 4 | // 5 | // Created by LeeYoseob on 2015. 11. 23.. 6 | // Copyright © 2015년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Trevi 11 | 12 | 13 | // Currently, you don't use this class, but will use the next. 14 | public class Request { 15 | 16 | // HTTP method like GET, POST. 17 | public var method: HTTPMethodType = HTTPMethodType.UNDEFINED 18 | 19 | public var httpVersionMajor : String? = "1" 20 | 21 | public var httpVersionMinor : String? = "1" 22 | 23 | public var version : String { 24 | return "\(httpVersionMajor).\(httpVersionMinor)" 25 | } 26 | 27 | // Original HTTP data include header & body 28 | public var headerString: String! { 29 | didSet { 30 | parse() 31 | } 32 | } 33 | 34 | // HTTP header 35 | public var header = [ String: String ] () 36 | 37 | // HTTP body 38 | public var body = [String : AnyObject]() 39 | 40 | public var bodyFragments = [String]() 41 | 42 | // Body parsed to JSON 43 | public var json: [String:AnyObject!]! 44 | 45 | // Parameter in url for semantic URL 46 | // ex) /url/:name 47 | public var params = [ String: String ] () 48 | 49 | // Qeury string from requested url 50 | // ex) /url?id="123" 51 | public var query = [ String: String ] () 52 | 53 | // Seperated path by component from the requested url 54 | public var pathComponent: [String] = [ String ] () 55 | 56 | // Requested url 57 | public var path: String { 58 | didSet { 59 | let segment = self.path.componentsSeparatedByString ( "/" ) 60 | for idx in 0 ..< segment.count where idx != 0 { 61 | pathComponent.append ( segment[idx] ) 62 | } 63 | } 64 | } 65 | 66 | public let startTime: NSDate 67 | 68 | // A variable to contain something needs by user. 69 | public var attribute = [ String : String ] () 70 | 71 | public init () { 72 | self.path = String () 73 | self.startTime = NSDate () 74 | } 75 | 76 | public init ( _ headerStr: String ) { 77 | self.path = String () 78 | self.startTime = NSDate () 79 | self.headerString = headerStr 80 | parse() 81 | } 82 | 83 | private final func parse () { 84 | 85 | // TODO : error when file uploaded.. 86 | guard let converted = headerString else { 87 | return 88 | } 89 | let requestHeader: [String] = converted.componentsSeparatedByString ( CRLF ) 90 | let requestLineElements: [String] = requestHeader.first!.componentsSeparatedByString ( SP ) 91 | 92 | // This is only for HTTP/1.x 93 | if requestLineElements.count == 3 { 94 | if let method = HTTPMethodType ( rawValue: requestLineElements[0] ) { 95 | self.method = method 96 | } 97 | 98 | let httpProtocolString = requestLineElements.last! 99 | let versionComponents: [String] = httpProtocolString.componentsSeparatedByString( "/" ) 100 | let version: [String] = versionComponents.last!.componentsSeparatedByString( "." ) 101 | 102 | httpVersionMajor = version.first! 103 | httpVersionMinor = version.last! 104 | 105 | parseHeader( requestHeader ) 106 | } 107 | } 108 | 109 | private final func parseHeader ( fields: [String] ) { 110 | for _idx in 1 ..< fields.count { 111 | if let fieldSet: [String] = fields[_idx].componentsSeparatedByString ( ":" ) where fieldSet.count > 1 { 112 | self.header[fieldSet[0].trim()] = fieldSet[1].trim(); 113 | self.header[fieldSet[0].trim().lowercaseString] = fieldSet[1].trim(); 114 | } 115 | } 116 | } 117 | } 118 | 119 | 120 | -------------------------------------------------------------------------------- /Lime/Lime/Require.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Require.swift 3 | // Trevi 4 | // 5 | // Created by LeeYoseob on 2015. 11. 23.. 6 | // Copyright © 2015년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | /* 13 | User-defined certainly should be implemented in order to use a router. 14 | */ 15 | public protocol Require{ 16 | func export() -> Router 17 | } 18 | -------------------------------------------------------------------------------- /Lime/Lime/Response.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Response.swift 3 | // Trevi 4 | // 5 | // Created by LeeYoseob on 2015. 11. 23.. 6 | // Copyright © 2015년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Trevi 11 | 12 | 13 | // Currently, you don't use this class, but will use the next. 14 | public protocol Sender{ 15 | func send(data: AnyObject?) -> Bool 16 | func end () -> Bool 17 | func template() -> Bool 18 | func render ( filename: String, args: AnyObject? ) -> Bool 19 | } 20 | 21 | public class Response :Sender{ 22 | 23 | public var header = [ String: String ] () 24 | 25 | 26 | public var statusCode: Int { 27 | return internalStatus.rawValue 28 | } 29 | public var status: Int { 30 | set { 31 | internalStatus = StatusCode ( rawValue: newValue )! 32 | } 33 | get { 34 | return statusCode 35 | } 36 | } 37 | 38 | //for binary 39 | private var data : NSData?{ 40 | didSet{ 41 | //set response content-type of header 42 | //image.. Any 43 | } 44 | } 45 | 46 | //for dictionary 47 | private var body : [ String: AnyObject ]?{ 48 | didSet{ 49 | header[Content_Type] = "application/json;charset=utf-8" 50 | } 51 | } 52 | 53 | //for text 54 | private var bodyString: String? { 55 | didSet { 56 | header[Content_Type] = "text/plain;charset=utf-8" 57 | } 58 | } 59 | 60 | /** 61 | * Make body. Surport all kind of Class. This value only used getter 62 | * 63 | * 64 | * @param { String|number|AnyObject} data 65 | * @return {NSData} bodyData 66 | * @private 67 | */ 68 | 69 | private var bodyData : NSData? { 70 | if let dt = data{ 71 | return dt 72 | }else if let bodyString = bodyString { 73 | return bodyString.dataUsingEncoding(NSUTF8StringEncoding)! 74 | }else if (body != nil) { 75 | let jsonData = try? NSJSONSerialization.dataWithJSONObject(body!, options:NSJSONWritingOptions(rawValue:0)) 76 | // if need jsonString, use it 77 | return jsonData 78 | } 79 | return nil 80 | } 81 | 82 | public var method : HTTPMethodType = .UNDEFINED 83 | 84 | private var internalStatus : StatusCode = .OK 85 | 86 | 87 | 88 | /** 89 | * Send a response data 90 | * 91 | * 92 | * Examples: 93 | * 94 | * res.send([:]) 95 | * res.send('some String') 96 | * 97 | * @param { String|number|AnyObject} data 98 | * @public 99 | */ 100 | 101 | public func send (data: AnyObject?) -> Bool { 102 | //need control flow that can divide AnyObject type 103 | switch data { 104 | case let str as String : 105 | bodyString = str 106 | case let dt as NSData: 107 | self.data = dt 108 | case let dic as [String:AnyObject]: 109 | body = dic 110 | default: 111 | break 112 | } 113 | return end() 114 | } 115 | 116 | /** 117 | * Send with html,etc, this function is help MVC 118 | * 119 | * 120 | * Examples: 121 | * 122 | * res.render('some html') 123 | * res.render('some html',[:]) 124 | * 125 | * @param { String } filename 126 | * @param { AnyObject } args 127 | * @public 128 | */ 129 | public func render ( filename: String, args: AnyObject? ) -> Bool { 130 | 131 | // var _args = [ String : String ]() 132 | // if args != nil { 133 | // _args = args as! [String:String] 134 | // } 135 | 136 | 137 | 138 | //this function called when rand html. forced change content-type = text/html 139 | // should add control flow filename.css or filename.js filename.html etc 140 | let temp : [String] = filename.componentsSeparatedByString(".") 141 | var filetype = temp.last! as String 142 | if filetype.length() > 0 && filetype == "ssp" { 143 | filetype = "html" 144 | } 145 | let content_type = "text/\(filetype);charset=utf-8" 146 | 147 | header[Content_Type] = content_type 148 | return end() 149 | } 150 | 151 | //not yet impliment 152 | public func template() -> Bool{ 153 | return end() 154 | } 155 | 156 | /** 157 | * Prepare header and body to send, Impliment send 158 | * 159 | * 160 | * @private 161 | * return {Bool} isSend 162 | */ 163 | public func end () ->Bool{ 164 | // let headerData = prepareHeader () 165 | // let sendData: NSData = makeResponse ( headerData, body: self.bodyData ) 166 | return true 167 | } 168 | 169 | /** 170 | * Redirect Page redering with destination url 171 | * 172 | * @param { String} url 173 | * @public 174 | * return {Bool} isSend 175 | */ 176 | public func redirect ( url u: String )->Bool{ 177 | self.status = 302 178 | self.header[Location] = u 179 | return end() 180 | } 181 | 182 | 183 | /** 184 | * Factory method make to response and make complate send message 185 | * 186 | * @param { NSData} header 187 | * @param { NSData} body 188 | * @private 189 | * return {NSData} bodyData 190 | */ 191 | private func makeResponse ( header: NSData, body: NSData?) -> ( NSData ) { 192 | if method != .HEAD{ 193 | if let b = body { 194 | let result = NSMutableData ( data: header ) 195 | result.appendData (b) 196 | return result 197 | } 198 | } 199 | return header 200 | } 201 | 202 | /** 203 | * Factory method fill header data 204 | * 205 | * @private 206 | * return {NSData} headerdata 207 | */ 208 | 209 | private func prepareHeader () -> NSData { 210 | // header[Date] = NSDate.GtmString() 211 | header[Server] = "Trevi-lime" 212 | header[Accept_Ranges] = "bytes" 213 | 214 | if let bodyData = bodyData { 215 | header[Content_Length] = "\(bodyData.length)" // replace bodyString length 216 | } 217 | 218 | // var headerString = "\(HttpProtocol) \(statusCode) \(statusString)" + CRLF 219 | // headerString += dictionaryToString ( header ) 220 | // return headerString.dataUsingEncoding ( NSUTF8StringEncoding )! 221 | return NSData() 222 | } 223 | 224 | private func dictionaryToString ( dic: NSDictionary ) -> String! { 225 | var resultString = "" 226 | for (key, value) in dic { 227 | if value.lengthOfBytesUsingEncoding ( NSUTF8StringEncoding ) == 0 { 228 | resultString += "\(key)\r\n" 229 | } else { 230 | resultString += "\(key):\(value)\r\n" 231 | } 232 | } 233 | resultString += CRLF 234 | return resultString; 235 | } 236 | } 237 | 238 | -------------------------------------------------------------------------------- /Lime/Lime/RoutAble.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RoutAble.swift 3 | // Trevi 4 | // 5 | // Created by LeeYoseob on 2015. 11. 30.. 6 | // Copyright © 2015년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | 10 | /* 11 | RoutAble is interface to make module like need to start server and matched for path 12 | */ 13 | 14 | import Foundation 15 | import Trevi 16 | 17 | // External module's top class that has a router with class. like lime. 18 | // I commend it to the router using inheritance. 19 | 20 | public class Routable{ 21 | internal var _router: Router! 22 | 23 | public func use(path: String = "/", _ middleware: Require){ 24 | let r = middleware.export() 25 | _router.use(path, md: r) 26 | } 27 | 28 | //just function 29 | public func use(fn: HttpCallback){ 30 | _router.use(fn) 31 | } 32 | } -------------------------------------------------------------------------------- /Lime/Lime/Route.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Route.swift 3 | // Trevi 4 | // 5 | // Created by LeeYoseob on 2015. 11. 30.. 6 | // Copyright © 2015년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Trevi 11 | 12 | // Terminating the callback functions with a module used in order to find you. 13 | 14 | public class Route{ 15 | public var stack = [Layer!]() 16 | public var path: String? 17 | public var methods = [HTTPMethodType]() 18 | public var method: HTTPMethodType! 19 | public var dispatch: HttpCallback? { 20 | 21 | didSet{ 22 | let layer = Layer(path: "", name: "anonymous", options: Option(end: true), fn: self.dispatch!) 23 | if method != nil{ 24 | layer.method = method 25 | method = nil 26 | }else{ 27 | layer.method = .UNDEFINED 28 | } 29 | 30 | self.stack.append(layer) 31 | } 32 | } 33 | 34 | public init(method: HTTPMethodType, _ path: String){ 35 | self.path = path 36 | self.methods.append(method) 37 | } 38 | 39 | //To connect and use the ihamsu with a layer. 40 | public func dispatchs(req: IncomingMessage,res: ServerResponse,next: NextCallback?){ 41 | 42 | var idx = 0 43 | let stack = self.stack 44 | 45 | guard stack.count > 0 else { 46 | return next!() 47 | } 48 | 49 | req.route = self 50 | let method = req.method 51 | 52 | func nextHandle(){ 53 | guard stack.count > idx else { 54 | return next!() 55 | } 56 | 57 | let layer: Layer! = stack[idx] 58 | idx += 1 59 | 60 | guard layer != nil else{ 61 | return next!() 62 | } 63 | 64 | if (layer.method != nil) && (layer.method != method){ 65 | return nextHandle() 66 | } 67 | 68 | layer.handleRequest(req, res: res, next: nextHandle) 69 | } 70 | nextHandle() 71 | } 72 | 73 | public func handlesMethod(method: HTTPMethodType) -> Bool{ 74 | for _mathod in methods { 75 | if method == _mathod { 76 | return true 77 | } 78 | } 79 | 80 | return false 81 | } 82 | 83 | public func options() -> [HTTPMethodType] { 84 | return self.methods 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ## USER CONFIGURABLE SETTINGS ## 2 | PROJECT_NAME = HelloTrevi 3 | COMPILE_MODE = Debug 4 | PLATFORM = $(shell uname -s) 5 | ARCH = $(shell uname -m) 6 | SOURCE_DIR = HelloTrevi 7 | 8 | ## LOCATIONS ## 9 | ROOT_DIR = $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) 10 | TREVI_DIR = $(ROOT_DIR)/Trevi 11 | LIME_DIR = $(ROOT_DIR)/Lime 12 | SRC_DIR = $(ROOT_DIR)/$(SOURCE_DIR) 13 | BUILD_DIR = $(ROOT_DIR)/build 14 | PLATFORM_DIR = $(BUILD_DIR)/$(PROJECT_NAME)/$(COMPILE_MODE)/$(PLATFORM)/$(ARCH) 15 | PLATFORM_BUILD_DIR = $(PLATFORM_DIR)/bin 16 | PLATFORM_LIB_DIR = $(PLATFORM_DIR)/lib 17 | PLATFORM_OBJ_DIR = $(PLATFORM_DIR)/obj 18 | PLATFORM_TEMP_DIR = $(PLATFORM_DIR)/tmp 19 | 20 | ## LIBUV SETTING ## 21 | UV_PATH = $(BUILD_DIR)/libuv 22 | UV_LIB = $(UV_PATH)/out/Debug/libuv.a 23 | define UV_MMAP_STR 24 | module Libuv [system] { 25 | header "uv.h" 26 | link "uv" 27 | export * 28 | } 29 | endef 30 | export UV_MMAP_STR 31 | 32 | ## COMPILER SETTINGS ## 33 | SWIFT = swift -frontend -c -color-diagnostics 34 | SWIFTC = swiftc 35 | ifeq ($(COMPILE_MODE), Debug) 36 | CFLAGS = -Onone -g 37 | else 38 | CFLAGS = -O3 39 | endif 40 | 41 | ## LINKER SETTINGS ## 42 | LD = $(shell xcrun -f ld) 43 | OBJ_EXT = 44 | OBJ_PRE = 45 | ifeq (mh_dylib, $(MACH_O_TYPE)) 46 | OBJ_EXT = .dylib 47 | OBJ_PRE = lib 48 | LDFLAGS += -dylib 49 | endif 50 | 51 | TARGET = Trevi Lime 52 | SOURCE_FILES = $(shell find $(SRC_DIR) \( -name "*.swift" ! -name "AppDelegate.swift" ! -name "ViewController.swift" \)) 53 | 54 | ## BUILD TARGETS ## 55 | all: clean setup $(TARGET) build 56 | 57 | setup: 58 | $(shell mkdir -p $(BUILD_DIR)) 59 | $(shell mkdir -p $(PLATFORM_BUILD_DIR)) 60 | $(shell mkdir -p $(PLATFORM_LIB_DIR)) 61 | $(shell mkdir -p $(PLATFORM_OBJ_DIR)) 62 | $(shell mkdir -p $(PLATFORM_TEMP_DIR)) 63 | 64 | $(UV_LIB): 65 | @echo "\n\033[1;33m>>> Download Libuv & Make\033[0m" 66 | git clone "https://github.com/libuv/libuv.git" $(UV_PATH) && \ 67 | test -d $(UV_PATH)/build/gyp || \ 68 | (mkdir -p ./build && git clone https://chromium.googlesource.com/external/gyp.git $(UV_PATH)/build/gyp) && \ 69 | cd $(UV_PATH) && \ 70 | ./gyp_uv.py -f make && \ 71 | $(MAKE) -C ./out && \ 72 | cp "$(UV_LIB)" $(PLATFORM_LIB_DIR) && \ 73 | cp $(UV_PATH)/include/uv*.h $(PLATFORM_LIB_DIR) && \ 74 | echo "$$UV_MMAP_STR" > $(PLATFORM_LIB_DIR)/module.modulemap 75 | @echo "\n\033[1;33m<<<\033[0m\n" 76 | 77 | $(TARGET): .PHONY $(UV_LIB) 78 | @echo "\n\033[1;33m>>> Framework : $@ \033[0m" 79 | $(SWIFTC) $(CFLAGS) \ 80 | -emit-library \ 81 | -o $(PLATFORM_LIB_DIR)/lib$@.dylib \ 82 | -emit-module \ 83 | -emit-module-path $(PLATFORM_LIB_DIR)/$@.swiftmodule \ 84 | -module-name $@ \ 85 | -module-link-name $@ \ 86 | -I$(PLATFORM_LIB_DIR) \ 87 | -L$(PLATFORM_LIB_DIR) \ 88 | -v \ 89 | $(shell find '$(ROOT_DIR)/$@' \( -name "*.swift" ! -name "file.swift" \) ) 90 | @echo "\n\033[1;33m<<<\033[0m\n" 91 | 92 | link: 93 | $(LD) $(LDFLAGS) $(wildcard $(PLATFORM_OBJ_DIR)/*.o) \ 94 | -o $(PLATFORM_BUILD_DIR)/$(OBJ_PRE)$(MODULE_NAME)$(OBJ_EXT) 95 | 96 | build: .PHONY 97 | @echo "\n\033[1;33m>>> Build user source codes\033[0m" 98 | $(SWIFTC) $(CFLAGS) $(SOURCE_FILES) \ 99 | -o $(PLATFORM_BUILD_DIR)/$(PROJECT_NAME) \ 100 | -Xlinker -rpath \ 101 | -Xlinker @executable_path/../lib \ 102 | -I$(PLATFORM_LIB_DIR) \ 103 | -L$(PLATFORM_LIB_DIR) \ 104 | -v 105 | @echo "\n\033[1;33m<<<\033[0m\n" 106 | @echo "\033[1;33mBuild complete!\033[0m" 107 | @echo "\033[1;33mAn executable is created on \"\033[1;36m$(PLATFORM_BUILD_DIR)/$(PROJECT_NAME)\033[1;33m\"!\033[0m\n" 108 | 109 | clean: 110 | @echo "\n\033[1;33m>>> Clean\033[0m" 111 | rm -rf $(BUILD_DIR) 112 | @echo "\n\033[1;33m<<<\033[0m\n" 113 | 114 | .PHONY: -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Notice 2 | Trevi now open a [Trevi Community](https://github.com/Trevi-Swift). 3 | Yoseob/Trevi project split up into respective Trevi, lime, middlewares and sys packages at our community. 4 | 5 | If you want to build or test all projects at Xcode, please check out [Trevi-Dev](https://github.com/Trevi-Swift/Trevi-Dev). 6 | Otherwise, you can build Trevi, lime and other packages by using Swift Package manager. 7 | [Here](https://github.com/Trevi-Swift/example-trevi-lime) are an example and it now runs on Linux. 8 | 9 | Hope Trevi interests you. -------------------------------------------------------------------------------- /Trevi.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Trevi.xcodeproj/project.xcworkspace/xcuserdata/leeyoseob.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yoseob/Trevi/d1a8c2edecfd5cac51a355395744e4f938cd1bea/Trevi.xcodeproj/project.xcworkspace/xcuserdata/leeyoseob.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Trevi.xcodeproj/xcuserdata/leeyoseob.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Trevi.xcodeproj/xcuserdata/leeyoseob.xcuserdatad/xcschemes/Trevi.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Trevi.xcodeproj/xcuserdata/leeyoseob.xcuserdatad/xcschemes/Trevi_ver_lime.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Trevi.xcodeproj/xcuserdata/leeyoseob.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Example.xcscheme 8 | 9 | orderHint 10 | 2 11 | 12 | Lime.xcscheme 13 | 14 | isShown 15 | 16 | orderHint 17 | 2 18 | 19 | Trevi.xcscheme 20 | 21 | isShown 22 | 23 | orderHint 24 | 0 25 | 26 | Trevi_ver_lime.xcscheme 27 | 28 | isShown 29 | 30 | orderHint 31 | 1 32 | 33 | 34 | SuppressBuildableAutocreation 35 | 36 | 232299901C61E00D003E44AB 37 | 38 | primary 39 | 40 | 41 | 239411D21C0C2DA3001B9B07 42 | 43 | primary 44 | 45 | 46 | 239411E11C0C2E19001B9B07 47 | 48 | primary 49 | 50 | 51 | 23973D841C25AE7400FBB8E7 52 | 53 | primary 54 | 55 | 56 | DF0900D51C6ECFBD008CA6F0 57 | 58 | primary 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /Trevi/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 0.2.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSHumanReadableCopyright 24 | Copyright © 2015년 LeeYoseob. All rights reserved. 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Trevi/Library/Buffer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Buffer.swift 3 | // Trevi 4 | // 5 | // Created by SeungHyunLee on 2/22/16. 6 | // Copyright © 2016 LeeYoseob. All rights reserved. 7 | // 8 | 9 | public class Buffer { 10 | var data: [Int8] 11 | var length: Int { 12 | get { 13 | return data.count 14 | } 15 | } 16 | 17 | init() { 18 | self.data = [Int8]() 19 | } 20 | 21 | init(capacity: Int) { 22 | self.data = [Int8](count: capacity, repeatedValue: 0) 23 | } 24 | 25 | init(data: [Int8]) { 26 | self.data = data 27 | } 28 | init(data: String) { 29 | self.data = Array(UnsafeBufferPointer(start: data, count: data.characters.count)) 30 | } 31 | 32 | init(data: UnsafePointer, length: Int) { 33 | self.data = Array(UnsafeBufferPointer(start: data, count: length)) 34 | } 35 | 36 | func push(data: Int8) { 37 | self.data.append(data) 38 | } 39 | 40 | func push(data: Buffer) { 41 | self.data.appendContentsOf(data.data) 42 | } 43 | 44 | func push(data: [Int8]) { 45 | self.data.appendContentsOf(data) 46 | } 47 | 48 | func push(data: UnsafePointer, length: Int) { 49 | self.data.appendContentsOf(Array(UnsafeBufferPointer(start: data, count: length))) 50 | } 51 | 52 | func unshift(data: Int8) { 53 | self.data.append(data) 54 | } 55 | 56 | func unshift(data: Buffer) { 57 | self.data.insertContentsOf(data.data, at: 0) 58 | } 59 | 60 | func unshift(data: [Int8]) { 61 | self.data.insertContentsOf(data, at: 0) 62 | } 63 | 64 | func unshift(data: UnsafePointer, length: Int) { 65 | self.data.insertContentsOf(Array(UnsafeBufferPointer(start: data, count: length)), at: 0) 66 | } 67 | 68 | func truncate() { 69 | data.removeAll() 70 | } 71 | } -------------------------------------------------------------------------------- /Trevi/Library/EventEmitter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StreamListener.swift 3 | // Trevi 4 | // 5 | // Created by LeeYoseob on 2016. 2. 1.. 6 | // Copyright © 2016년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public typealias emitable = (AnyObject) -> Void 12 | 13 | public typealias noParamsEvent = (Void) -> Void 14 | 15 | public typealias oneStringeEvent = (String) -> Void 16 | 17 | public typealias oneDataEvent = (NSData) -> Void 18 | 19 | typealias EmiiterType = ((AnyObject) -> Void)? 20 | 21 | 22 | 23 | /* 24 | This class is an asynchronous data it uses to communicate, and passed to register and invokes the event. 25 | But now, because there is a limit of the type of all can't transmit the data. Will quickly change to have it 26 | */ 27 | 28 | public class EventEmitter{ 29 | 30 | var events = [String:Any]() 31 | 32 | init(){ 33 | 34 | } 35 | 36 | deinit{ 37 | 38 | } 39 | 40 | //register event function with name 41 | func on(name: String, _ emitter: Any){ 42 | 43 | guard events[name] == nil else{ 44 | print("already contain event") 45 | return 46 | } 47 | 48 | events[name] = emitter 49 | } 50 | 51 | func removeEvent(name: String){ 52 | events.removeValueForKey(name) 53 | } 54 | 55 | //invoke registed event with Parameters 56 | func emit(name: String, _ arg : AnyObject...){ 57 | 58 | 59 | guard let emitter = events[name] else{ 60 | print("called emitter") 61 | return 62 | } 63 | 64 | switch emitter { 65 | case let cb as HttpCallback: 66 | 67 | if arg.count == 2{ 68 | let req = arg[0] as! IncomingMessage 69 | let res = arg[1] as! ServerResponse 70 | cb(req,res, nil) 71 | } 72 | break 73 | 74 | case let cb as emitable: 75 | if arg.count == 1 { 76 | cb(arg.first!) 77 | }else { 78 | #if os(Linux) 79 | cb(arg as! AnyObject) 80 | #else 81 | cb(arg) 82 | #endif 83 | } 84 | break 85 | case let cb as oneStringeEvent: 86 | if arg.count == 1 { 87 | #if os(Linux) 88 | let str = arg.first as! StringWrapper 89 | cb(str.string) 90 | #else 91 | cb(arg.first as! String) 92 | #endif 93 | } 94 | break 95 | case let cb as oneDataEvent: 96 | if arg.count == 1 { 97 | cb(arg.first as! NSData) 98 | } 99 | break 100 | 101 | case let cb as noParamsEvent: 102 | cb() 103 | break 104 | 105 | default: 106 | break 107 | } 108 | } 109 | } 110 | 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /Trevi/Library/FileSystem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FileStream.swift 3 | // Trevi 4 | // 5 | // Created by JangTaehwan on 2016. 3. 6.. 6 | // Copyright © 2016년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Libuv 10 | import Foundation 11 | 12 | /** 13 | Filesystem library for Trevi and Trevi developers. 14 | Only provides File readable, writable stream module yet. 15 | */ 16 | public class FileSystem { 17 | 18 | public struct Options { 19 | public var fd : Int32! = nil 20 | public var flags : Int32! = nil 21 | public var mode : Int32! = nil 22 | } 23 | 24 | 25 | // Close the File descriptor on system and all libuv handle events. 26 | // Also, dealloc all memory associated with the handle. 27 | public static func close(handle : uv_handle_ptr) { 28 | 29 | let info = UnsafeMutablePointer(handle.memory.data) 30 | let request = info.memory.request 31 | let loop = info.memory.loop 32 | 33 | Handle.close(handle) 34 | FSBase.close(loop, request: request) 35 | info.dealloc(1) 36 | } 37 | 38 | 39 | // Should be inherited from StreamReadable 40 | public class ReadStream { 41 | 42 | public let loop : Loop 43 | public let pipe : Pipe 44 | public var options : Options = Options() 45 | 46 | public init?(path : String, options : Options? = nil) { 47 | 48 | self.loop = Loop() 49 | self.pipe = Pipe(loop: loop.loopHandle) 50 | self.options.flags = O_RDONLY 51 | self.options.mode = 0o666 52 | 53 | if let options = options { self.setOptions(options) } 54 | 55 | 56 | if self.options.fd == nil { 57 | self.options.fd = FSBase.open(self.loop.loopHandle, handle: self.pipe.pipeHandle, path : path, 58 | flags: self.options.flags, mode: self.options.mode) 59 | } 60 | 61 | if self.options.fd <= 0 { 62 | LibuvError.printState("FileSystem.ReadStream init", error : self.options.fd) 63 | return nil 64 | } 65 | else{ 66 | Pipe.open(self.pipe.pipeHandle, fd: self.options.fd) 67 | 68 | self.pipe.event.onClose = { (handle) in 69 | 70 | FileSystem.close(handle) 71 | } 72 | } 73 | } 74 | 75 | deinit{ 76 | Handle.close(self.pipe.handle) 77 | Loop.close(self.loop.loopHandle) 78 | } 79 | 80 | 81 | func setOptions(options : Options) { 82 | self.options.fd = options.fd 83 | self.options.flags = options.flags == nil ? O_RDONLY : options.flags 84 | self.options.mode = options.mode == nil ? 0o666 : options.mode 85 | } 86 | 87 | 88 | // Set ReadStream pipe onClose event. 89 | // It should be called before readstart. 90 | public func onClose(callback : ((handle : uv_handle_ptr)->Void)) { 91 | 92 | self.pipe.event.onClose = { (handle) in 93 | 94 | callback(handle: handle) 95 | FileSystem.close(handle) 96 | } 97 | } 98 | 99 | // Set ReadStream pipe onRead event and start loop. 100 | // Other events associated with this pipe handle should be set before call this function. 101 | public func readStart(callback : ((error : Int32, data : NSData)->Void)) { 102 | 103 | self.pipe.event.onRead = { (handle, data) in 104 | 105 | let info = UnsafeMutablePointer(handle.memory.data) 106 | info.memory.toRead = info.memory.toRead - UInt64(data.length) 107 | 108 | callback(error : 0, data : data) 109 | 110 | // Close readStream when there are no more data to read. 111 | if info.memory.toRead <= 0 { 112 | Handle.close(uv_handle_ptr(handle)) 113 | } 114 | } 115 | 116 | Stream.readStart(self.pipe.streamHandle) 117 | Loop.run(self.loop.loopHandle, mode: UV_RUN_DEFAULT) 118 | } 119 | 120 | 121 | // Pipe data directly to writeStream. 122 | // Close readStream and writeStream after finish read data. 123 | public func pipeStream(writeStream : WriteStream) { 124 | 125 | self.onClose() { (handle) in 126 | 127 | Handle.close(writeStream.pipe.handle) 128 | } 129 | 130 | self.readStart() { (error, data) in 131 | 132 | writeStream.writeData(data) 133 | } 134 | 135 | Loop.run(writeStream.loop.loopHandle, mode: UV_RUN_DEFAULT) 136 | } 137 | 138 | } 139 | 140 | 141 | // Should be inherited from StreamReadable 142 | 143 | public class WriteStream { 144 | 145 | public let loop : Loop 146 | public let pipe : Pipe 147 | public var options : Options = Options() 148 | 149 | public init?(path : String, options : Options? = nil) { 150 | 151 | self.loop = Loop() 152 | self.pipe = Pipe(loop: loop.loopHandle) 153 | self.options.flags = O_CREAT | O_WRONLY 154 | self.options.mode = 0o666 155 | 156 | if let options = options { self.setOptions(options) } 157 | 158 | if self.options.fd == nil { 159 | self.options.fd = FSBase.open(self.loop.loopHandle, handle: self.pipe.pipeHandle, path : path, 160 | flags: self.options.flags, mode: self.options.mode) 161 | } 162 | 163 | if self.options.fd <= 0 { 164 | LibuvError.printState("FileSystem.WriteStream init", error : self.options.fd) 165 | return nil 166 | } 167 | else{ 168 | 169 | Pipe.open(self.pipe.pipeHandle, fd: self.options.fd) 170 | 171 | self.pipe.event.onClose = { (handle) in 172 | 173 | FileSystem.close(handle) 174 | } 175 | } 176 | } 177 | 178 | deinit { 179 | Handle.close(self.pipe.handle) 180 | Loop.close(self.loop.loopHandle) 181 | } 182 | 183 | 184 | func setOptions(options : Options) { 185 | self.options.fd = options.fd 186 | self.options.flags = options.flags == nil ? O_CREAT | O_WRONLY : options.flags 187 | self.options.mode = options.mode == nil ? 0o666 : options.mode 188 | } 189 | 190 | 191 | public func close() { 192 | 193 | FileSystem.close(self.pipe.handle) 194 | } 195 | 196 | 197 | public func writeData(data : NSData) { 198 | 199 | Stream.doWrite(data, handle: self.pipe.streamHandle) 200 | } 201 | 202 | } 203 | 204 | } 205 | -------------------------------------------------------------------------------- /Trevi/Library/Http.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Http.swift 3 | // Trevi 4 | // 5 | // Created by LeeYoseob on 2015. 11. 20.. 6 | // Copyright © 2015년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Libuv 10 | import Foundation 11 | 12 | 13 | public typealias HttpCallback = ( ( IncomingMessage, ServerResponse, NextCallback?) -> Void ) 14 | 15 | public typealias NextCallback = ()->() 16 | 17 | public typealias ReceivedParams = (buffer: UnsafeMutablePointer, length: Int) 18 | 19 | 20 | /* 21 | This protocol is to other external module on the server should be implemented. 22 | Currently, the Trevi is it used to be implemented this. 23 | 24 | */ 25 | public protocol ApplicationProtocol { 26 | func createApplication() -> Any 27 | } 28 | 29 | public class Http { 30 | 31 | public init () { 32 | 33 | } 34 | 35 | /** 36 | * Create Server base on RouteAble Model, maybe it able to use many Middleware 37 | * end return self 38 | * 39 | * Examples: 40 | * http.createServer(RouteAble).listen(Port) 41 | * 42 | * 43 | * 44 | * @param {RouteAble} requireModule 45 | * @return {Http} self 46 | * @public 47 | */ 48 | 49 | //Only one function of registration is available. 50 | public func createServer( requestListener: ( IncomingMessage, ServerResponse, NextCallback? )->()) -> Net{ 51 | let server = HttpServer(requestListener: requestListener) 52 | return server 53 | } 54 | 55 | //Can be saved using external module. 56 | public func createServer( requestListener: Any) -> Net{ 57 | let server = HttpServer(requestListener: requestListener) 58 | return server 59 | } 60 | 61 | } 62 | 63 | -------------------------------------------------------------------------------- /Trevi/Library/HttpParser.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HttpParser.swift 3 | // Trevi 4 | // 5 | // Created by LeeYoseob on 2016. 2. 2.. 6 | // Copyright © 2016년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | public struct HeaderInfo{ 13 | public var header = [ String: String ]() 14 | public var versionMajor: String! 15 | public var versionMinor: String! 16 | public var url: String! 17 | public var method: String! 18 | public var hasbody: Bool! 19 | public init(){ 20 | } 21 | 22 | } 23 | 24 | public class HttpParser{ 25 | 26 | public var incoming: IncomingMessage! 27 | public var socket: Socket! 28 | 29 | public var onHeader: ((Void) -> (Void))? 30 | public var onHeaderComplete: ((HeaderInfo) -> Void)? 31 | public var onBody: ((NSData) -> Void)? 32 | public var onBodyComplete: ((Void) -> Void)? 33 | public var onIncoming: ((IncomingMessage) -> Bool)? 34 | 35 | public var date: NSDate = NSDate() 36 | 37 | //only header 38 | public var headerInfo: HeaderInfo! = nil 39 | 40 | //only body 41 | private var contentLength: Int = 0 42 | private var totalLength: Int = 0 43 | private var hasbody = false 44 | 45 | 46 | public init (){ 47 | } 48 | 49 | deinit{ 50 | } 51 | 52 | 53 | func headerParser(p:UnsafePointer , length: Int ,onHeaderInfo: (String,Bool)->() , onBodyData: (NSData)->()) { 54 | 55 | readLine(p, length: length) { (pointer, data, readTotalSize, readlineSize) -> (Bool) in 56 | 57 | if data == "" { 58 | onHeaderInfo(data , true) 59 | self.totalLength = length - readTotalSize 60 | 61 | if self.totalLength != 0 { 62 | let body = NSData(bytes: pointer+2, length: self.totalLength) 63 | onBodyData(body) 64 | } 65 | return false 66 | } 67 | 68 | onHeaderInfo(data , false) 69 | return true 70 | } 71 | } 72 | 73 | public func execute(data: NSData, length: Int){ 74 | 75 | if self.headerInfo == nil{ 76 | var headerCount = 0 77 | self.headerInfo = HeaderInfo() 78 | onHeader!() 79 | 80 | headerParser(UnsafePointer(data.bytes), length: length, onHeaderInfo: { headerLine , isFinish in 81 | 82 | if isFinish == true { 83 | self.onHeaderComplete!(self.headerInfo) 84 | } 85 | 86 | //first Line parse 87 | if headerCount == 0 { 88 | let requestLineElements: [String] = headerLine.componentsSeparatedByString ( SP ) 89 | 90 | // This is only for HTTP/1.x 91 | if requestLineElements.count == 3 { 92 | self.headerInfo.method = requestLineElements[0] 93 | self.headerInfo.url = requestLineElements[1] 94 | let httpProtocolString = requestLineElements.last! 95 | let versionComponents: [String] = httpProtocolString.componentsSeparatedByString( "/" ) 96 | let version: [String] = versionComponents.last!.componentsSeparatedByString( "." ) 97 | self.headerInfo.versionMajor = version.first! 98 | self.headerInfo.versionMinor = version.last! 99 | } 100 | }else{ 101 | if let fieldSet: [String] = headerLine.componentsSeparatedByString ( ":" ) where fieldSet.count > 1 { 102 | self.headerInfo.header[fieldSet[0].trim()] = fieldSet[1].trim(); 103 | if let contentLength = self.headerInfo.header[Content_Length]{ 104 | self.contentLength = Int(contentLength)! 105 | } 106 | } 107 | } 108 | 109 | headerCount += 1 110 | } , onBodyData: { body in 111 | 112 | self.onBody!(body) 113 | 114 | self.headerInfo.hasbody = true 115 | if self.contentLength == body.length { 116 | self.onBodyComplete!() 117 | self.reset() 118 | } 119 | }) 120 | 121 | }else{ 122 | 123 | if self.contentLength > 0 { 124 | self.totalLength += length 125 | onBody!(data) 126 | 127 | if self.totalLength >= self.contentLength{ 128 | self.onBodyComplete!() 129 | reset() 130 | } 131 | } 132 | } 133 | } 134 | 135 | private func reset(){ 136 | self.totalLength = 0 137 | self.contentLength = 0 138 | self.headerInfo = nil 139 | } 140 | } 141 | 142 | 143 | public func readLine(p:UnsafePointer , length: Int, line: (UnsafePointer, String!, Int, Int)->(Bool)){ 144 | var itr = p 145 | var startByte = itr 146 | 147 | let CR: Int8 = 13 148 | let LF: Int8 = 10 149 | 150 | var pre: Int8 = 0 151 | var crt: Int8 = 0 152 | var index = 0 153 | var lineStr: String! = nil 154 | var readLength = 0 155 | 156 | var isContinue: Bool = false 157 | 158 | for _ in 0.. HttpParser{ 49 | return parsers[socket.handle]! 50 | } 51 | 52 | private func connectionListener(sock: AnyObject){ 53 | 54 | let socket = sock as! Socket 55 | 56 | func parserSetup(){ 57 | 58 | parser(socket).onHeader = { 59 | } 60 | 61 | parser(socket).onHeaderComplete = { info in 62 | let incoming = IncomingMessage(socket: self.parser(socket).socket) 63 | 64 | incoming.header = info.header 65 | incoming.httpVersionMajor = info.versionMajor 66 | incoming.httpVersionMinor = info.versionMinor 67 | incoming.url = info.url 68 | incoming.method = HTTPMethodType(rawValue: info.method) 69 | incoming.hasBody = info.hasbody 70 | 71 | self.parser(socket).incoming = incoming 72 | self.parser(socket).onIncoming!(incoming) 73 | } 74 | 75 | parser(socket).onBody = { body in 76 | let incoming = self.parser(socket).incoming 77 | if body.length > 0 { 78 | incoming.push(body) 79 | } 80 | } 81 | 82 | parser(socket).onBodyComplete = { 83 | let incoming = self.parser(socket).incoming 84 | incoming.emit("end") 85 | } 86 | } 87 | 88 | parsers[socket.handle] = HttpParser() 89 | let _parser = parser(socket) 90 | _parser.socket = socket 91 | parserSetup() 92 | 93 | socket.ondata = { data, nread in 94 | if let _parser = self.parsers[socket.handle] { 95 | _parser.execute(data,length: nread) 96 | }else{ 97 | print("no parser") 98 | } 99 | } 100 | 101 | socket.onend = { 102 | 103 | var _parser = self.parsers[socket.handle] 104 | _parser!.onBody = nil 105 | _parser!.onBodyComplete = nil 106 | _parser!.onHeader = nil 107 | _parser!.onIncoming = nil 108 | _parser!.onHeaderComplete = nil 109 | _parser!.socket = nil 110 | _parser!.incoming = nil 111 | _parser = nil 112 | self.parsers.removeValueForKey(socket.handle) 113 | 114 | } 115 | 116 | parser(socket).onIncoming = { req in 117 | 118 | let res = ServerResponse(socket: req.socket) 119 | res.socket = req.socket 120 | res.connection = req.socket 121 | 122 | res.httpVersion = "HTTP/"+req.version 123 | if let connection = req.header[Connection] where connection == "keep-alive" { 124 | res.header[Connection] = connection 125 | res.shouldKeepAlive = true 126 | }else{ 127 | res.header[Connection] = "close" 128 | res.shouldKeepAlive = false 129 | } 130 | res.req = req 131 | 132 | self.emit("request", req ,res) 133 | 134 | return false 135 | } 136 | } 137 | } 138 | 139 | -------------------------------------------------------------------------------- /Trevi/Library/IncomingMessage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IncomingMessage.swift 3 | // Trevi 4 | // 5 | // Created by LeeYoseob on 2016. 3. 3.. 6 | // Copyright © 2016년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | public class IncomingMessage: StreamReadable{ 13 | 14 | public var socket: Socket! 15 | 16 | public var connection: Socket! 17 | 18 | // HTTP header 19 | public var header: [ String: String ]! 20 | 21 | public var httpVersionMajor: String = "1" 22 | 23 | public var httpVersionMinor: String = "1" 24 | 25 | public var version : String{ 26 | return "\(httpVersionMajor).\(httpVersionMinor)" 27 | } 28 | 29 | public var method: HTTPMethodType! 30 | 31 | // Seperated path by component from the requested url 32 | public var pathComponent: [String] = [ String ] () 33 | 34 | // Qeury string from requested url 35 | // ex) /url?id="123" 36 | public var query = [ String: String ] () 37 | 38 | public var path = "" 39 | 40 | public var hasBody: Bool! 41 | 42 | // for lime (not fixed) 43 | public var baseUrl: String! = "" 44 | public var route: AnyObject! 45 | public var originUrl: String! = "" 46 | public var params: [String: String]! 47 | public var json: [String: String]! 48 | public var body: [String: String]! 49 | public var bodyText: String! 50 | 51 | public var files: [String: File]! 52 | 53 | public var app: AnyObject! 54 | 55 | public let startTime: NSDate 56 | 57 | //server only 58 | public var url: String!{ 59 | didSet{ 60 | originUrl = url 61 | } 62 | } 63 | 64 | 65 | //response only 66 | public var statusCode: String! 67 | public var client: AnyObject! 68 | 69 | init(socket: Socket){ 70 | startTime = NSDate () 71 | super.init() 72 | self.socket = socket 73 | self.connection = socket 74 | self.client = socket 75 | } 76 | 77 | deinit{ 78 | socket = nil 79 | connection = nil 80 | client = nil 81 | } 82 | 83 | public override func _read(n: Int) { 84 | 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /Trevi/Library/Net.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Net.swift 3 | // Trevi 4 | // 5 | // Created by JangTaehwan on 2016. 2. 20.. 6 | // Copyright © 2016년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Libuv 10 | import Foundation 11 | 12 | 13 | /** 14 | Socket interface for Net or Net users. 15 | Should be inherited StreamReadable. 16 | */ 17 | public class Socket: EventEmitter { 18 | 19 | public let timer : Timer = Timer() 20 | 21 | public static var dictionary = [uv_stream_ptr : Socket]() 22 | 23 | public var handle: uv_stream_ptr! 24 | public var ondata: (( NSData, Int )->Void)? 25 | public var onend: ((Void)->(Void))? 26 | 27 | public init(handle: uv_stream_ptr) { 28 | 29 | self.handle = handle 30 | super.init() 31 | 32 | // Set dictionary to get the object by stream pointer 33 | Socket.dictionary[handle] = self 34 | } 35 | 36 | 37 | public func write(data: NSData, handle : uv_stream_ptr) { 38 | 39 | Stream.doWrite(data, handle: handle) 40 | } 41 | 42 | // Shutdown handle first to block close the Socket while writting. 43 | // After that, close Socket and onClose will be called. 44 | public func close() { 45 | 46 | Stream.doShutDown(handle) 47 | } 48 | 49 | 50 | // Block to close the Socket in delay msecs. 51 | public func setKeepAlive(msecs: UInt32) { 52 | 53 | Tcp.setKeepAlive(uv_tcp_ptr(self.handle), enable: 1, delay: msecs) 54 | } 55 | 56 | } 57 | 58 | 59 | // Socket static callbacks. These support closure event. 60 | extension Socket { 61 | 62 | public static func onConnection(handle : uv_stream_ptr , _ EE: EventEmitter) { 63 | 64 | let socket = Socket(handle: handle) 65 | EE.emit("connection", socket) 66 | } 67 | 68 | public static func onRead(handle : uv_stream_ptr, data: NSData) -> Void { 69 | 70 | if let wrap = Socket.dictionary[handle] { 71 | wrap.ondata!(data, data.length) 72 | } 73 | } 74 | 75 | 76 | // Set timeout to close the Socket after msecs from last write call. 77 | public static func onAfterWrite(handle: uv_stream_ptr) -> Void { 78 | 79 | if let wrap = Socket.dictionary[uv_stream_ptr(handle)] { 80 | Socket.onTimeout(wrap.timer.timerhandle, msecs: 40) { 81 | _ in 82 | 83 | Stream.doShutDown(handle) 84 | } 85 | } 86 | } 87 | 88 | public static func onClose(handle : uv_handle_ptr) { 89 | 90 | if let wrap = Socket.dictionary[uv_stream_ptr(handle)] { 91 | wrap.onend!() 92 | wrap.events.removeAll() 93 | wrap.ondata = nil 94 | wrap.onend = nil 95 | wrap.timer.close() 96 | Socket.dictionary.removeValueForKey(uv_stream_ptr(handle)) 97 | } 98 | } 99 | 100 | 101 | // Set timer event. This will remove previous event and start new event on Socket. 102 | public static func onTimeout( handle : uv_timer_ptr, msecs : UInt64, callback : ((uv_timer_ptr)->()) ) { 103 | 104 | if let wrap = Handle.dictionary[uv_handle_ptr(handle)]{ 105 | wrap.event.onTimeout = callback 106 | Timer.stop(handle) 107 | Timer.start(handle, timeout: msecs, count: 0) 108 | } 109 | } 110 | 111 | } 112 | 113 | 114 | /** 115 | Network module with system and Trevi. 116 | 117 | Target : 118 | 119 | public class EchoServer : Net { 120 | 121 | public init(){ 122 | super.init() 123 | self.on("connection", connectionListener) 124 | } 125 | 126 | func connectionListener(sock: AnyObject){ 127 | 128 | let socket = sock as! Socket 129 | 130 | // Set event when get a data. 131 | socket.ondata = { data, nread in 132 | socket.write(data, handle: socket.handle) 133 | } 134 | 135 | // Set end event. 136 | socket.onend = { } 137 | } 138 | } 139 | */ 140 | public class Net: EventEmitter { 141 | 142 | public let ip : String 143 | public var port : Int32 144 | 145 | public let server : Tcp 146 | 147 | public init(ip : String = "0.0.0.0") { 148 | self.ip = ip 149 | self.port = 8080 150 | self.server = Tcp() 151 | } 152 | 153 | 154 | public func listen(port: Int32) -> Int32? { 155 | self.port = port 156 | 157 | // Set listening event to call user function when start server. 158 | self.emit("listening") 159 | 160 | self.server.event.onConnection = { 161 | client in 162 | 163 | // Set user callback events. 164 | Socket.onConnection(client, self) 165 | 166 | if let wrap = Handle.dictionary[uv_handle_ptr(client)] { 167 | 168 | wrap.event.onRead = Socket.onRead 169 | wrap.event.onAfterWrite = Socket.onAfterWrite 170 | wrap.event.onClose = Socket.onClose 171 | } 172 | } 173 | 174 | guard let _ = Tcp.bind(self.server.tcpHandle, address : self.ip, port: self.port) else { 175 | return nil 176 | } 177 | guard let _ = Tcp.listen(self.server.tcpHandle) else { 178 | return nil 179 | } 180 | 181 | return 0 182 | } 183 | 184 | } -------------------------------------------------------------------------------- /Trevi/Library/OutgoingMessage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HttpStream.swift 3 | // Trevi 4 | // 5 | // Created by LeeYoseob on 2016. 3. 3.. 6 | // Copyright © 2016년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | //temp class 13 | 14 | 15 | public class OutgoingMessage { 16 | 17 | var socket: Socket! 18 | public var connection: Socket! 19 | 20 | public var header: [String: String]! 21 | public var shouldKeepAlive = false 22 | public var chunkEncoding = false 23 | 24 | public init(socket: AnyObject){ 25 | header = [String: String]() 26 | } 27 | deinit{ 28 | socket = nil 29 | connection = nil 30 | } 31 | public func _end(data: NSData, encoding: Any! = nil){ 32 | self.socket.write(data, handle: self.socket.handle) 33 | if shouldKeepAlive == false { 34 | self.socket.close() 35 | } 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /Trevi/Library/ServerResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ServerResponse.swift 3 | // Trevi 4 | // 5 | // Created by LeeYoseob on 2016. 3. 3.. 6 | // Copyright © 2016년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // Make a response of user requests. 12 | public class ServerResponse: OutgoingMessage{ 13 | //for Lime 14 | public var req: IncomingMessage! 15 | public let startTime: NSDate 16 | public var onFinished : ((ServerResponse) -> Void)? 17 | 18 | public var httpVersion: String = "" 19 | public var url: String! 20 | public var method: String! 21 | public var statusCode: Int!{ 22 | didSet{ 23 | self.status = StatusCode(rawValue: statusCode)!.statusString() 24 | } 25 | } 26 | 27 | private var _hasbody = false 28 | 29 | private var _body: String?{ 30 | didSet { 31 | self._hasbody = true 32 | var type = "text/plain; charset=utf-8" 33 | if (_body?.containsString("!DOCTYPE")) != nil || 34 | (_body?.containsString("")) != nil{ 35 | type = "text/html; charset=utf-8" 36 | } 37 | header[Content_Type] = type 38 | } 39 | } 40 | 41 | private var _bodyData: NSData! { 42 | didSet{ 43 | self._hasbody = true 44 | } 45 | } 46 | 47 | //for dictionary 48 | private var bodys: [ String: String ]?{ 49 | didSet{ 50 | self._hasbody = true 51 | header[Content_Type] = "application/json" 52 | } 53 | } 54 | 55 | private var bodyData : NSData? { 56 | if let dt = _bodyData{ 57 | return dt 58 | }else if let bodyString = _body { 59 | return bodyString.dataUsingEncoding(NSUTF8StringEncoding)! 60 | }else if (bodys != nil) { 61 | #if os(Linux) 62 | let jsonData = try? NSJSONSerialization.dataWithJSONObject(bodys as! AnyObject, options:NSJSONWritingOptions(rawValue:0)) 63 | #else 64 | let jsonData = try? NSJSONSerialization.dataWithJSONObject(bodys!, options:NSJSONWritingOptions(rawValue:0)) 65 | #endif 66 | // if need jsonString, use it 67 | return jsonData 68 | } 69 | return nil 70 | } 71 | 72 | private var status: String! 73 | 74 | private var firstLine: String! 75 | 76 | public init(socket: Socket) { 77 | startTime = NSDate () 78 | onFinished = nil 79 | super.init(socket: socket) 80 | self._body = "" 81 | } 82 | 83 | public func end(){ 84 | let hData: NSData = self.prepareHeader() 85 | let result: NSMutableData = NSMutableData(data: hData) 86 | if self._hasbody { 87 | result.appendData(self.bodyData!) 88 | } 89 | 90 | self._end(result) 91 | 92 | onFinished?(self) 93 | } 94 | 95 | public func writeHead(statusCode: Int, headers: [String:String]! = nil){ 96 | self.statusCode = statusCode 97 | mergeHeader(headers) 98 | firstLine = "\(httpVersion) \(statusCode) \(status)" + CRLF 99 | } 100 | 101 | 102 | public func write(data: String, encoding: String! = nil, type: String! = ""){ 103 | _body = data 104 | _hasbody = true 105 | } 106 | 107 | public func write(data: NSData, encoding: String! = nil, type: String! = ""){ 108 | 109 | _bodyData = data 110 | if let t = type{ 111 | header[Content_Type] = t 112 | } 113 | _hasbody = true 114 | } 115 | 116 | public func write(data: [String : String], encoding: String! = nil, type: String! = ""){ 117 | bodys = data 118 | _hasbody = true 119 | } 120 | 121 | /** 122 | * Factory method fill header data 123 | * 124 | * @private 125 | * return {NSData} headerdata 126 | */ 127 | private func prepareHeader () -> NSData { 128 | 129 | header[Date] = getCurrentDatetime("E,dd LLL yyyy HH:mm:ss 'GMT'") 130 | header[Server] = "Trevi-lime" 131 | header[Accept_Ranges] = "bytes" 132 | 133 | if self._hasbody { 134 | header[Content_Length] = "\(bodyData!.length)" // replace bodyString length 135 | } 136 | 137 | if firstLine == nil{ 138 | statusCode = 200 139 | firstLine = "\(httpVersion) \(statusCode) \(status)" + CRLF 140 | } 141 | var headerString = firstLine 142 | headerString! += dictionaryToString ( header ) 143 | return headerString!.dataUsingEncoding ( NSUTF8StringEncoding )! 144 | } 145 | 146 | private func mergeHeader(headers: [String : String]){ 147 | for (k,v) in headers { 148 | self.header[k] = v 149 | } 150 | } 151 | 152 | private func dictionaryToString ( dic: [String : String] ) -> String! { 153 | var resultString = "" 154 | for (key, value) in dic { 155 | if value.lengthOfBytesUsingEncoding ( NSUTF8StringEncoding ) == 0 { 156 | resultString += "\(key)\r\n" 157 | } else { 158 | resultString += "\(key): \(value)\r\n" 159 | } 160 | } 161 | resultString += CRLF 162 | return resultString; 163 | } 164 | } 165 | 166 | -------------------------------------------------------------------------------- /Trevi/Library/StreamReadable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StreamReadable.swift 3 | // Trevi 4 | // 5 | // Created by SeungHyunLee on 2/22/16. 6 | // Copyright © 2016 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Libuv 11 | 12 | public class StreamReadableState { 13 | var highWaterMark: Int 14 | var buffer: Buffer 15 | var length: Int 16 | var sync: Bool 17 | var flowing: Bool 18 | var reading: Bool 19 | var ended: Bool 20 | 21 | var bookEmitReadable: Bool 22 | var isEndEmitted: Bool 23 | var isReadableEmitted: Bool 24 | 25 | init() { 26 | highWaterMark = 16 * 1024 27 | buffer = Buffer() 28 | length = 0 29 | sync = false 30 | flowing = false 31 | ended = false 32 | reading = false 33 | 34 | bookEmitReadable = false 35 | isEndEmitted = false 36 | isReadableEmitted = false 37 | } 38 | } 39 | 40 | public class StreamReadable: EventEmitter { 41 | var _state: StreamReadableState 42 | 43 | public override init() { 44 | _state = StreamReadableState() 45 | } 46 | 47 | func _read(n: Int) { 48 | print("Not implemented") 49 | } 50 | 51 | // func push(chunk: uv_buf_const_ptr, encoding: NSStringEncoding = 0) -> Bool { 52 | // return addChunk(self, chunk: chunk, addToFront: false) 53 | // } 54 | 55 | func push(chunk: NSData?, encoding: NSStringEncoding = 0) -> Bool { 56 | return addChunk(self, chunk: chunk, addToFront: false) 57 | } 58 | 59 | // func unshift(chunk: uv_buf_const_ptr) -> Bool { 60 | // return addChunk(self, chunk: chunk, addToFront: true) 61 | // } 62 | 63 | func unshift(chunk: NSData?, encoding: NSStringEncoding = 0) -> Bool { 64 | return addChunk(self, chunk: chunk, addToFront: true) 65 | } 66 | 67 | func read(n: Int = -1) -> [Int8]? { 68 | 69 | if n > 0 { 70 | _state.isReadableEmitted = false 71 | } 72 | 73 | if n == 0 && _state.bookEmitReadable && (_state.length >= _state.highWaterMark || _state.ended) { 74 | if (_state.length == 0 && _state.ended) { 75 | endReadable(self) 76 | } else { 77 | emitReadable(self) 78 | } 79 | return nil 80 | } 81 | 82 | var readn = lengthToRead(n, state: _state) 83 | if readn == 0 && _state.ended { 84 | if _state.length == 0 { 85 | endReadable(self) 86 | } 87 | return nil 88 | } 89 | 90 | var doRead: Bool = _state.bookEmitReadable 91 | 92 | if _state.length == 0 || _state.length - n < _state.highWaterMark { 93 | doRead = true 94 | } 95 | 96 | if _state.ended || _state.reading { 97 | doRead = false 98 | } 99 | 100 | if doRead { 101 | _state.reading = true 102 | _state.sync = true 103 | if _state.length == 0 { 104 | _state.bookEmitReadable = true 105 | } 106 | self._read(_state.highWaterMark) 107 | _state.sync = false 108 | } 109 | 110 | var ret: [Int8]? 111 | if readn > 0 { 112 | ret = _state.buffer.data 113 | _state.buffer.truncate() 114 | } else { 115 | ret = nil 116 | } 117 | 118 | if ret == nil { 119 | _state.bookEmitReadable = true 120 | readn = 0 121 | } 122 | _state.length -= readn 123 | 124 | if _state.length == 0 && !_state.ended { 125 | _state.bookEmitReadable = true 126 | } 127 | 128 | // after EOF 129 | if n != readn && _state.ended && _state.length == 0 { 130 | endReadable(self) 131 | } 132 | 133 | if ret != nil { 134 | emit("data", Buffer(data: ret!)) 135 | } 136 | 137 | return ret 138 | } 139 | 140 | func isPaused() -> Bool { 141 | return _state.flowing == false 142 | } 143 | 144 | func pause() -> StreamReadable { 145 | if _state.flowing != false { 146 | _state.flowing = false 147 | self.emit("pause") 148 | } 149 | return self 150 | } 151 | 152 | func resume() -> StreamReadable { 153 | if !_state.flowing { 154 | _state.flowing = true 155 | self.emit("resume") 156 | 157 | flow(self) 158 | if (_state.flowing && !_state.reading) { 159 | read(0) 160 | } 161 | } 162 | return self 163 | } 164 | 165 | func pipe(destination dest: StreamWritable, end: Bool = true) -> StreamWritable { 166 | print("Not implemented") 167 | return dest 168 | } 169 | 170 | func unpipe(destination dest: StreamWritable? = nil) -> StreamReadable { 171 | print("Not implemented") 172 | return self 173 | } 174 | 175 | 176 | public override func on(name: String, _ emitter: Any) { 177 | super.on(name, emitter) 178 | 179 | if name == "data" && _state.flowing { 180 | resume() 181 | } 182 | 183 | if name == "readable" && !_state.isEndEmitted { 184 | _state.isReadableEmitted = false 185 | _state.bookEmitReadable = true 186 | if (!_state.reading) { 187 | self.read(0) 188 | } else if _state.length > 0 { 189 | emitReadable(self) 190 | } 191 | } 192 | } 193 | } 194 | 195 | func lengthToRead(n: Int = -1, state: StreamReadableState) -> Int { 196 | if (state.length == 0 && state.ended) { 197 | return 0 198 | } 199 | 200 | if n == -1 { 201 | return state.length 202 | } else if n < -1 || n == 0 { 203 | return 0 204 | } else { 205 | if (n > state.highWaterMark) { 206 | // max highWaterMark : 0x800000 207 | if (n >= 0x800000) { 208 | state.highWaterMark = 0x800000 209 | } else { 210 | state.highWaterMark = Int(pow(Double(n), 2.0)) 211 | } 212 | } 213 | 214 | if (n > state.length) { 215 | if (!state.ended) { 216 | state.bookEmitReadable = true 217 | return 0 218 | } else { 219 | return state.length 220 | } 221 | } 222 | } 223 | 224 | return n 225 | } 226 | 227 | private func addChunk(stream: StreamReadable, chunk: NSData?, addToFront: Bool) -> Bool { 228 | let state = stream._state 229 | 230 | if chunk == nil { 231 | state.reading = false 232 | onEofChunk(stream) 233 | } else if chunk!.length > 0 { 234 | let chunkBuf = Buffer(data: UnsafePointer(chunk!.bytes), length: chunk!.length) 235 | 236 | if !addToFront { 237 | state.reading = false 238 | } 239 | state.flowing = true 240 | if (state.flowing && state.length == 0 && !state.sync) { 241 | #if os(Linux) 242 | stream.emit("data", StringWrapper(string: chunk!)) 243 | #else 244 | stream.emit("data", chunk!) 245 | #endif 246 | stream.read(0) 247 | } else { 248 | state.length += chunkBuf.length 249 | if addToFront { 250 | state.buffer.push(chunkBuf) 251 | } else { 252 | state.buffer.unshift(chunkBuf) 253 | } 254 | emitReadable(stream) 255 | } 256 | } else if (!addToFront) { 257 | state.reading = false 258 | } 259 | 260 | return !state.ended && 261 | (state.bookEmitReadable || 262 | state.length < state.highWaterMark || 263 | state.length == 0) 264 | } 265 | 266 | private func onEofChunk(stream: StreamReadable) { 267 | let state = stream._state 268 | 269 | if state.ended { 270 | return 271 | } 272 | state.ended = true 273 | emitReadable(stream) 274 | } 275 | 276 | private func flow(stream: StreamReadable) { 277 | let state = stream._state 278 | 279 | if state.flowing { 280 | // read buffer continuously in flowing mode. 281 | while let _ = stream.read() where state.flowing { 282 | } 283 | } 284 | } 285 | 286 | private func emitReadable(stream: StreamReadable) { 287 | let state = stream._state 288 | 289 | if !state.isReadableEmitted { 290 | state.isReadableEmitted = true 291 | stream.emit("readable") 292 | flow(stream) 293 | } 294 | } 295 | 296 | private func endReadable(stream: StreamReadable) { 297 | let state = stream._state 298 | 299 | if (!state.isEndEmitted) { 300 | state.ended = true 301 | if (!state.isEndEmitted && state.length == 0) { 302 | state.isEndEmitted = true 303 | stream.emit("end") 304 | } 305 | } 306 | } -------------------------------------------------------------------------------- /Trevi/Library/StreamWritable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Writable.swift 3 | // Trevi 4 | // 5 | // Created by SeungHyunLee on 2/22/16. 6 | // Copyright © 2016 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public typealias writableCallback = (AnyObject?) -> Void 12 | public struct WorkNode { 13 | let chunk: Buffer 14 | let callback: writableCallback? 15 | 16 | init(chunk: Buffer, callback: writableCallback?) { 17 | self.chunk = chunk 18 | self.callback = callback 19 | } 20 | } 21 | 22 | public class WritableState { 23 | var highWaterMark: Int 24 | var buffer: Buffer 25 | var length: Int 26 | var workQueue: Array 27 | 28 | // current status 29 | var sync: Bool 30 | var writing: Bool 31 | var queueClearing: Bool 32 | var ending: Bool 33 | var ended: Bool 34 | var finished: Bool 35 | 36 | var bookEmitFinish: Bool 37 | var isFinishEmitted: Bool 38 | var bookEmitDrain: Bool 39 | var isDrainEmitted: Bool 40 | var isErrorEmitted: Bool 41 | 42 | var writeLength: Int 43 | var writeCallback: writableCallback? 44 | 45 | init() { 46 | highWaterMark = 16 * 1024 47 | buffer = Buffer() 48 | length = 0 49 | workQueue = Array() 50 | 51 | sync = false 52 | writing = false 53 | queueClearing = false 54 | ending = false 55 | ended = false 56 | finished = false 57 | 58 | bookEmitFinish = false 59 | isFinishEmitted = false 60 | bookEmitDrain = false 61 | isDrainEmitted = false 62 | isErrorEmitted = false 63 | 64 | writeLength = 0 65 | writeCallback = nil 66 | } 67 | } 68 | 69 | public class StreamWritable: EventEmitter { 70 | private var _state: WritableState 71 | public var writable: Bool = false 72 | 73 | public override init() { 74 | _state = WritableState() 75 | } 76 | 77 | // User implement 78 | func _write(chunk: Buffer, encoding: NSStringEncoding?, callback: writableCallback) { 79 | print("Not implemented") 80 | } 81 | 82 | // User implement 83 | func _writev(chunk: Buffer, encoding: NSStringEncoding?, callback: writableCallback) { 84 | } 85 | 86 | func cork() { 87 | print("Not implemented") 88 | } 89 | 90 | func end(chunk: String? = nil, encoding: NSStringEncoding? = nil, callback: Any? = nil) { 91 | if (chunk != nil) { 92 | write(chunk!) 93 | } 94 | 95 | if !_state.ending && !_state.finished { 96 | _state.ending = true 97 | 98 | // check finished 99 | if !_state.writing && _state.length == 0 && _state.ending && !_state.finished { 100 | _state.finished = true 101 | self.emit("finish") 102 | 103 | // execute callback 104 | // need to modify: self.once("finish", callback) 105 | if callback != nil { 106 | // execute callback 107 | _state.writeCallback!(nil) 108 | } 109 | } 110 | 111 | _state.ended = true 112 | } 113 | } 114 | 115 | func setDefaultEncoding() { 116 | print("Not implemented") 117 | } 118 | 119 | func uncork() { 120 | print("Not implemented") 121 | } 122 | 123 | func write(chunk: String, encoding: NSStringEncoding? = nil, callback: Any? = nil) -> Bool { 124 | return write(Buffer(data: chunk), encoding: encoding, callback: callback) 125 | } 126 | 127 | func write(chunk: Buffer, encoding: NSStringEncoding? = nil, callback: Any? = nil) -> Bool { 128 | var canInputMore = false 129 | 130 | if _state.ended { 131 | // error handling : writing after end 132 | } else { 133 | // put into buffer 134 | _state.length += chunk.length 135 | 136 | // check buffer space 137 | canInputMore = _state.length < _state.highWaterMark 138 | if !canInputMore { 139 | _state.bookEmitDrain = true 140 | } 141 | 142 | // check while writing 143 | if _state.writing { 144 | // put work into work queue 145 | _state.workQueue.append(WorkNode(chunk: chunk, callback: callback as? writableCallback)) 146 | } else { 147 | // call _write 148 | _state.writeCallback = callback as? writableCallback 149 | _state.writeLength = chunk.length 150 | _state.writing = true 151 | _state.sync = true 152 | self._write(chunk, encoding: encoding, callback: writeCallback) 153 | _state.sync = false 154 | } 155 | } 156 | 157 | return canInputMore 158 | } 159 | 160 | func writeCallback(error: AnyObject? = nil) { 161 | 162 | // update state of Writable. 163 | _state.writing = false 164 | _state.length -= _state.writeLength 165 | _state.writeLength = 0 166 | 167 | if error != nil { 168 | // error handling 169 | _state.writeCallback!(error!) 170 | _state.isErrorEmitted = true 171 | self.emit("error", error!) 172 | } else { 173 | // check finish 174 | let isFinished = !_state.writing && _state.length == 0 && _state.ending && !_state.finished 175 | 176 | // clear work queue when finish 177 | if isFinished && !_state.workQueue.isEmpty && !_state.queueClearing { 178 | // clear work queue 179 | clearWorkQueue() 180 | } 181 | 182 | // process after writing 183 | if !(isFinished) { 184 | if _state.length == 0 && _state.bookEmitDrain { 185 | self.emit("drain") 186 | _state.bookEmitDrain = false 187 | } 188 | } 189 | 190 | // execute callback 191 | if let callback = _state.writeCallback { 192 | callback(nil) 193 | } 194 | 195 | // check finished 196 | if !_state.writing && _state.length == 0 && _state.ending && !_state.finished { 197 | _state.finished = true 198 | self.emit("finish") 199 | } 200 | } 201 | 202 | _state.writeCallback = nil 203 | } 204 | 205 | func clearWorkQueue() { 206 | // set clearing flag 207 | _state.queueClearing = true 208 | 209 | // clearing queue 210 | // for work in _state.workQueue { 211 | while let work = _state.workQueue.first { 212 | // dequeue 213 | _state.workQueue.removeFirst() 214 | 215 | // call _write 216 | _state.writeCallback = work.callback 217 | _state.writeLength = work.chunk.length 218 | _state.writing = true 219 | _state.sync = true 220 | self._write(work.chunk, encoding: nil, callback: writeCallback) 221 | _state.sync = false 222 | 223 | // check writing 224 | if (_state.writing) { 225 | break 226 | } 227 | } 228 | 229 | // reset clearing flag 230 | _state.queueClearing = false 231 | } 232 | } -------------------------------------------------------------------------------- /Trevi/Makefile: -------------------------------------------------------------------------------- 1 | ## USER CONFIGURABLE SETTINGS ## 2 | PROJECT_NAME = HelloTrevi 3 | COMPILE_MODE = Debug 4 | PLATFORM = $(shell uname -s) 5 | ARCH = $(shell uname -m) 6 | SOURCE_DIR = HelloTrevi 7 | MACH_O_TYPE = mh_dylib 8 | 9 | ## LOCATIONS ## 10 | ROOT_DIR = $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) 11 | BUILD_DIR = $(ROOT_DIR)/build 12 | PLATFORM_DIR = $(BUILD_DIR)/$(PROJECT_NAME)/$(COMPILE_MODE)/$(PLATFORM)/$(ARCH) 13 | PLATFORM_BUILD_DIR = $(PLATFORM_DIR)/bin 14 | PLATFORM_LIB_DIR = $(PLATFORM_DIR)/lib 15 | PLATFORM_OBJ_DIR = $(PLATFORM_DIR)/obj 16 | PLATFORM_TEMP_DIR = $(PLATFORM_DIR)/tmp 17 | 18 | ## LIBUV SETTING ## 19 | UV_PATH = $(BUILD_DIR)/libuv 20 | UV_LIB = $(UV_PATH)/out/Debug/libuv.a 21 | define UV_MMAP_STR 22 | module Libuv [system] { 23 | header "uv.h" 24 | link "uv" 25 | export * 26 | } 27 | endef 28 | export UV_MMAP_STR 29 | 30 | ## COMPILER SETTINGS ## 31 | SWIFT = swift -frontend -c -color-diagnostics 32 | SWIFTC = swiftc 33 | ifeq ($(COMPILE_MODE), Debug) 34 | CFLAGS = -Onone -g 35 | else 36 | CFLAGS = -O3 37 | endif 38 | ## LINKER SETTINGS ## 39 | LD = ld 40 | OBJ_EXT = 41 | OBJ_PRE = 42 | 43 | ifeq (mh_dylib, $(MACH_O_TYPE)) 44 | OBJ_EXT = .dylib 45 | OBJ_PRE = lib 46 | LDFLAGS += -dylib 47 | endif 48 | 49 | SOURCE_FILES = $(shell find $(ROOT_DIR) \( -name "*.swift" ! -name "file.swift" \)) 50 | 51 | ## BUILD TARGETS ## 52 | all: clean setup $(TARGET) build 53 | 54 | setup: 55 | $(shell mkdir -p $(BUILD_DIR)) 56 | $(shell mkdir -p $(PLATFORM_BUILD_DIR)) 57 | $(shell mkdir -p $(PLATFORM_LIB_DIR)) 58 | $(shell mkdir -p $(PLATFORM_OBJ_DIR)) 59 | $(shell mkdir -p $(PLATFORM_TEMP_DIR)) 60 | 61 | $(UV_LIB): 62 | @echo "\n\033[1;33m>>> Download Libuv & Make\033[0m" 63 | $(shell mkdir -p $(PLATFORM_LIB_DIR)) 64 | git clone "https://github.com/libuv/libuv.git" $(UV_PATH) && \ 65 | test -d $(UV_PATH)/build/gyp || \ 66 | (mkdir -p ./build && git clone https://chromium.googlesource.com/external/gyp.git $(UV_PATH)/build/gyp) && \ 67 | cd $(UV_PATH) && \ 68 | ./gyp_uv.py -f make && \ 69 | $(MAKE) -C ./out && \ 70 | cp "$(UV_LIB)" $(PLATFORM_LIB_DIR) && \ 71 | cp $(UV_PATH)/include/uv*.h $(PLATFORM_LIB_DIR) && \ 72 | echo "$$UV_MMAP_STR" > $(PLATFORM_LIB_DIR)/module.modulemap 73 | @echo "\n\033[1;33m<<<\033[0m\n" 74 | 75 | # $(TARGET): .PHONY $(UV_LIB) 76 | # @echo "\n\033[1;33m>>> Framework : $@ \033[0m" 77 | # $(SWIFTC) $(CFLAGS) \ 78 | # -emit-library \ 79 | # -o $(PLATFORM_LIB_DIR)/lib$@.dylib \ 80 | # -Xlinker -install_name -Xlinker @rpath/../lib/lib$@.dylib \ 81 | # -emit-module \ 82 | # -emit-module-path $(PLATFORM_LIB_DIR)/$@.swiftmodule \ 83 | # -module-name $@ \ 84 | # -module-link-name $@ \ 85 | # -I$(PLATFORM_LIB_DIR) \ 86 | # -L$(PLATFORM_LIB_DIR) \ 87 | # -v \ 88 | # $(shell find '$(ROOT_DIR)/$@' -name '*.swift') 89 | # @echo "\n\033[1;33m<<<\033[0m\n" 90 | 91 | %.swift: .PHONY $(UV_LIB) 92 | @echo "\n\033[1;33m>>> $(notdir $@)\033[0m" 93 | $(SWIFT) $(CFLAGS) -primary-file $@ \ 94 | $(filter-out $@,$(SOURCE_FILES)) \ 95 | -module-name Trevi \ 96 | -o $(PLATFORM_OBJ_DIR)/$(notdir $*).o \ 97 | -emit-module \ 98 | -emit-module-path $(PLATFORM_OBJ_DIR)/$(notdir $*)~partial.swiftmodule \ 99 | -I$(PLATFORM_LIB_DIR) \ 100 | -L$(PLATFORM_LIB_DIR) 101 | 102 | files: 103 | @echo $(SOURCE_FILES) 104 | 105 | test: $(SOURCE_FILES) 106 | 107 | link: 108 | $(LD) $(LDFLAGS) $(wildcard $(PLATFORM_OBJ_DIR)/*.o) \ 109 | -I$(PLATFORM_LIB_DIR) \ 110 | -L$(PLATFORM_LIB_DIR) \ 111 | -o $(PLATFORM_BUILD_DIR)/$(OBJ_PRE)$(MODULE_NAME)$(OBJ_EXT) 112 | 113 | build: .PHONY 114 | @echo "\n\033[1;33m>>> Build user source codes\033[0m" 115 | $(SWIFTC) $(CFLAGS) $(SOURCE_FILES) \ 116 | -o $(PLATFORM_BUILD_DIR)/$(PROJECT_NAME) \ 117 | -Xlinker -rpath \ 118 | -Xlinker @executable_path/../lib \ 119 | -I$(PLATFORM_LIB_DIR) \ 120 | -L$(PLATFORM_LIB_DIR) \ 121 | -v 122 | @echo "\n\033[1;33m<<<\033[0m\n" 123 | @echo "\033[1;33mBuild complete!\033[0m" 124 | @echo "\033[1;33mAn executable is created on \"\033[1;36m$(PLATFORM_BUILD_DIR)/$(PROJECT_NAME)\033[1;33m\"!\033[0m\n" 125 | 126 | clean: 127 | @echo "\n\033[1;33m>>> Clean\033[0m" 128 | rm -rf $(BUILD_DIR) 129 | @echo "\n\033[1;33m<<<\033[0m\n" 130 | 131 | .PHONY: -------------------------------------------------------------------------------- /Trevi/Source/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yoseob/Trevi/d1a8c2edecfd5cac51a355395744e4f938cd1bea/Trevi/Source/.DS_Store -------------------------------------------------------------------------------- /Trevi/Source/Async.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Async.swift 3 | // Trevi 4 | // 5 | // Created by JangTaehwan on 2016. 2. 11.. 6 | // Copyright © 2016년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Libuv 10 | 11 | 12 | /** 13 | Libuv Async bindings. 14 | */ 15 | public class Async : Handle { 16 | 17 | public let asyncHandle : uv_async_ptr 18 | 19 | public init () { 20 | 21 | self.asyncHandle = uv_async_ptr.alloc(1) 22 | uv_async_init(uv_default_loop(), self.asyncHandle, Async.callback) 23 | 24 | super.init(handle: uv_handle_ptr(self.asyncHandle)) 25 | } 26 | 27 | deinit { 28 | if isAlive { 29 | Handle.close(self.handle) 30 | self.asyncHandle.dealloc(1) 31 | isAlive = false 32 | } 33 | } 34 | } 35 | 36 | 37 | // Async static functions. 38 | 39 | extension Async { 40 | 41 | public static func send(handle : uv_async_ptr) { 42 | 43 | uv_async_send(handle) 44 | } 45 | } 46 | 47 | 48 | // Async static callbacks. 49 | 50 | extension Async { 51 | 52 | public static var callback : uv_async_cb = { (handle) in 53 | 54 | } 55 | } -------------------------------------------------------------------------------- /Trevi/Source/FSBase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FsBase.swift 3 | // Trevi 4 | // 5 | // Created by JangTaehwan on 2016. 2. 27.. 6 | // Copyright © 2016년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Libuv 10 | import Foundation 11 | 12 | /** 13 | Libuv Filesystem bindings and events module, but considering better way. 14 | So, hope to use FileSystem on Trevi temporary. 15 | */ 16 | public class FSBase { 17 | 18 | public static let BUF_SIZE = 1024 19 | public static var dictionary = [uv_fs_ptr : FSBase]() 20 | 21 | public typealias fsCallback = (uv_fs_ptr)->Void 22 | public var events = [uv_fs_type : fsCallback]() 23 | 24 | public let fsRequest : uv_fs_ptr 25 | 26 | public init() { 27 | 28 | self.fsRequest = uv_fs_ptr.alloc(1) 29 | 30 | let buffer = uv_buf_ptr.alloc(1) 31 | self.setWorkData(void_ptr(buffer)) 32 | 33 | FSBase.dictionary[self.fsRequest] = self 34 | } 35 | 36 | deinit { 37 | self.fsRequest.dealloc(1) 38 | self.events.removeAll() 39 | } 40 | 41 | public func setWorkData(dataPtr : void_ptr) { 42 | self.fsRequest.memory.data = dataPtr 43 | } 44 | 45 | } 46 | 47 | public struct FSInfo { 48 | public var request : uv_fs_ptr 49 | public var loop : uv_loop_ptr 50 | public var toRead : UInt64 51 | } 52 | 53 | 54 | // FsBase static functions 55 | 56 | extension FSBase { 57 | 58 | 59 | public static func open(loop : uv_loop_ptr = uv_default_loop(), handle : uv_pipe_ptr! = nil, path : String, flags : Int32, mode : Int32) -> Int32 { 60 | 61 | let fd = UnsafeMutablePointer.alloc(1) 62 | let request = uv_fs_ptr.alloc(1) 63 | 64 | fd.memory = uv_fs_open(loop, request, path, flags, mode, nil) 65 | uv_fs_stat(loop, request, path, nil) 66 | 67 | request.memory.data = void_ptr(fd) 68 | 69 | let info = UnsafeMutablePointer.alloc(1) 70 | 71 | info.memory.request = request 72 | info.memory.loop = loop 73 | info.memory.toRead = request.memory.statbuf.st_size 74 | 75 | if let handle = handle { 76 | handle.memory.data = void_ptr(info) 77 | } 78 | 79 | return fd.memory 80 | } 81 | 82 | public static func close(loop : uv_loop_ptr = uv_default_loop(), request : uv_fs_ptr) { 83 | 84 | let fd = UnsafeMutablePointer(request.memory.data) 85 | let closeRequest = uv_fs_ptr.alloc(1) 86 | 87 | uv_fs_close(loop, closeRequest, fd.memory, onClose) 88 | 89 | fd.dealloc(1) 90 | FSBase.cleanup(request) 91 | } 92 | 93 | public static func read(request : uv_fs_ptr) { 94 | 95 | let buffer = uv_buf_ptr(request.memory.data) 96 | buffer.memory = uv_buf_init(UnsafeMutablePointer.alloc(BUF_SIZE), UInt32(BUF_SIZE)) 97 | 98 | uv_fs_read(uv_default_loop(), request, uv_file(request.memory.result), buffer, 1, -1, onRead) 99 | } 100 | 101 | public static func write(buffer: uv_buf_const_ptr, fd : uv_file) { 102 | 103 | let request : uv_fs_ptr = uv_fs_ptr.alloc(1) 104 | 105 | request.memory.data = void_ptr(buffer) 106 | 107 | uv_fs_write(uv_default_loop(), request, fd, buffer, 1, -1, afterWrite) 108 | } 109 | 110 | public static func unlink(loop : uv_loop_ptr = uv_default_loop(), path : String) { 111 | let request = uv_fs_ptr.alloc(1) 112 | let error = uv_fs_unlink(loop, request, path, FSBase.afterUnlink) 113 | 114 | if error == 0 { 115 | // Should handle error 116 | 117 | } 118 | } 119 | 120 | public static func makeDirectory(loop : uv_loop_ptr = uv_default_loop(), path : String, mode : Int32 = 0o666) { 121 | let request = uv_fs_ptr.alloc(1) 122 | let error = uv_fs_mkdir(loop, request, path, mode, FSBase.afterMakeDirectory) 123 | 124 | if error == 0 { 125 | // Should handle error 126 | 127 | } 128 | } 129 | 130 | public static func cleanup(request : uv_fs_ptr) { 131 | 132 | // FSBase.dictionary[request] = nil 133 | uv_fs_req_cleanup(request) 134 | request.dealloc(1) 135 | } 136 | 137 | } 138 | 139 | // FsBase static callbacks 140 | 141 | extension FSBase { 142 | 143 | public static var after : ((uv_fs_ptr, uv_fs_type)->()) = { (request, type) in 144 | 145 | if let wrap = FSBase.dictionary[request]{ 146 | if let callback = wrap.events[type] { 147 | callback(request) 148 | } 149 | } 150 | } 151 | 152 | public static var onOpen : uv_fs_cb = { request in 153 | 154 | if request.memory.result >= 0 { 155 | 156 | after(request, UV_FS_OPEN) 157 | } 158 | else { 159 | print("Filesystem open error : \(uv_strerror(Int32(request.memory.result)))") 160 | } 161 | 162 | } 163 | 164 | public static var onClose : uv_fs_cb = { request in 165 | 166 | // after(request, UV_FS_CLOSE) 167 | 168 | FSBase.cleanup(request) 169 | } 170 | 171 | public static var onRead : uv_fs_cb = { request in 172 | 173 | if request.memory.result < 0 { 174 | 175 | print("Filesystem read error : \(uv_strerror(Int32(request.memory.result)))") 176 | } 177 | else if request.memory.result == 0 { 178 | 179 | FSBase.close(uv_default_loop(), request: request) 180 | } 181 | else { 182 | 183 | after(request, UV_FS_READ) 184 | } 185 | } 186 | 187 | public static var afterWrite : uv_fs_cb = { request in 188 | 189 | after(request, UV_FS_WRITE) 190 | 191 | let buffer : uv_buf_const_ptr = uv_buf_const_ptr(request.memory.data) 192 | 193 | if buffer.memory.len > 0 { 194 | buffer.memory.base.dealloc(buffer.memory.len) 195 | } 196 | 197 | uv_cancel(uv_req_ptr(request)) 198 | request.memory.data.dealloc(1) 199 | request.dealloc(1) 200 | } 201 | 202 | public static var afterUnlink : uv_fs_cb = { request in 203 | request.dealloc(1) 204 | } 205 | 206 | public static var afterMakeDirectory : uv_fs_cb = { request in 207 | request.dealloc(1) 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /Trevi/Source/Handle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Handle.swift 3 | // Trevi 4 | // 5 | // Created by JangTaehwan on 2016. 2. 17.. 6 | // Copyright © 2016년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | 10 | import Libuv 11 | import Foundation 12 | 13 | /** 14 | Top class in Libuv handle classes. All handle classes are supposed to be inherited this. 15 | Manages all handle events, object, memory. 16 | Libuv handle api reference : http://docs.libuv.org/en/v1.x/index.html 17 | */ 18 | public class Handle { 19 | 20 | public static var dictionary = [uv_handle_ptr : Handle]() 21 | 22 | public var event : Event 23 | public let handle : uv_handle_ptr 24 | 25 | public var isAlive : Bool 26 | 27 | public init (handle : uv_handle_ptr) { 28 | 29 | self.isAlive = true 30 | self.handle = handle 31 | self.event = Event() 32 | 33 | // Set dictionary to get the object by uv handle pointer 34 | Handle.dictionary[self.handle] = self 35 | } 36 | 37 | deinit { 38 | if isAlive { 39 | Handle.close(self.handle) 40 | self.handle.dealloc(1) 41 | isAlive = false 42 | } 43 | } 44 | 45 | } 46 | 47 | 48 | // Handle event inner class 49 | 50 | extension Handle { 51 | 52 | public class Event { 53 | 54 | // Can be set or used after get object with any uv handles from dictionary 55 | 56 | public var onClose : ((uv_handle_ptr)->())! 57 | public var onAlloc : Any! 58 | public var onRead : ((uv_stream_ptr, NSData)->())! 59 | public var afterShutdown : Any! 60 | public var onAfterWrite : ((uv_stream_ptr)->())! 61 | public var onConnection : (uv_stream_ptr -> ())! 62 | public var afterConnect : Any! 63 | public var onTimeout : ((uv_timer_ptr)->())! 64 | 65 | } 66 | } 67 | 68 | 69 | // Handle static functions. 70 | 71 | extension Handle { 72 | 73 | public static func ref(handle : uv_handle_ptr) { 74 | uv_ref(handle) 75 | } 76 | 77 | public static func unref(handle : uv_handle_ptr) { 78 | uv_unref(handle) 79 | } 80 | 81 | public static func getFD(handle : uv_handle_ptr) -> Int32 { 82 | var fd : uv_os_fd_t = uv_os_fd_t.init(littleEndian: -1) 83 | uv_fileno(handle, &fd) 84 | 85 | return fd 86 | } 87 | 88 | public static func isActive(handle : uv_handle_ptr) -> Bool { 89 | 90 | return uv_is_active(handle) != 0 91 | } 92 | 93 | 94 | // Must be called before handle pointer memory is released. 95 | // Free up any resources associated with the handle on Handle.onClose callback. 96 | public static func close(handle : uv_handle_ptr) { 97 | if !Handle.isClosing(handle){ 98 | 99 | uv_close(handle, Handle.onClose) 100 | } 101 | } 102 | 103 | public static func isClosing(handle : uv_handle_ptr) -> Bool { 104 | return uv_is_closing(handle) != 0 105 | } 106 | 107 | } 108 | 109 | 110 | // Handle static callbacks. 111 | 112 | extension Handle { 113 | 114 | // After this callback this Handle object will be deinit 115 | public static var onClose : uv_close_cb = { handle in 116 | 117 | if let wrap = Handle.dictionary[handle] { 118 | if let callback = wrap.event.onClose { 119 | callback(handle) 120 | } 121 | } 122 | 123 | Handle.dictionary[handle] = nil 124 | } 125 | } -------------------------------------------------------------------------------- /Trevi/Source/Libuv/libuv.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yoseob/Trevi/d1a8c2edecfd5cac51a355395744e4f938cd1bea/Trevi/Source/Libuv/libuv.a -------------------------------------------------------------------------------- /Trevi/Source/Libuv/module.modulemap: -------------------------------------------------------------------------------- 1 | module Libuv [system] { 2 | header "uv.h" 3 | link "uv" 4 | export * 5 | } -------------------------------------------------------------------------------- /Trevi/Source/Libuv/uv-darwin.h: -------------------------------------------------------------------------------- 1 | /* Copyright Joyent, Inc. and other Node contributors. All rights reserved. 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy 4 | * of this software and associated documentation files (the "Software"), to 5 | * deal in the Software without restriction, including without limitation the 6 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | * sell copies of the Software, and to permit persons to whom the Software is 8 | * furnished to do so, subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in 11 | * all copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | * IN THE SOFTWARE. 20 | */ 21 | 22 | #ifndef UV_DARWIN_H 23 | #define UV_DARWIN_H 24 | 25 | #if defined(__APPLE__) && defined(__MACH__) 26 | # include 27 | # include 28 | # include 29 | # include 30 | # define UV_PLATFORM_SEM_T semaphore_t 31 | #endif 32 | 33 | #define UV_IO_PRIVATE_PLATFORM_FIELDS \ 34 | int rcount; \ 35 | int wcount; \ 36 | 37 | #define UV_PLATFORM_LOOP_FIELDS \ 38 | uv_thread_t cf_thread; \ 39 | void* _cf_reserved; \ 40 | void* cf_state; \ 41 | uv_mutex_t cf_mutex; \ 42 | uv_sem_t cf_sem; \ 43 | void* cf_signals[2]; \ 44 | 45 | #define UV_PLATFORM_FS_EVENT_FIELDS \ 46 | uv__io_t event_watcher; \ 47 | char* realpath; \ 48 | int realpath_len; \ 49 | int cf_flags; \ 50 | uv_async_t* cf_cb; \ 51 | void* cf_events[2]; \ 52 | void* cf_member[2]; \ 53 | int cf_error; \ 54 | uv_mutex_t cf_mutex; \ 55 | 56 | #define UV_STREAM_PRIVATE_PLATFORM_FIELDS \ 57 | void* select; \ 58 | 59 | #define UV_HAVE_KQUEUE 1 60 | 61 | #endif /* UV_DARWIN_H */ 62 | -------------------------------------------------------------------------------- /Trevi/Source/Libuv/uv-threadpool.h: -------------------------------------------------------------------------------- 1 | /* Copyright Joyent, Inc. and other Node contributors. All rights reserved. 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy 4 | * of this software and associated documentation files (the "Software"), to 5 | * deal in the Software without restriction, including without limitation the 6 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | * sell copies of the Software, and to permit persons to whom the Software is 8 | * furnished to do so, subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in 11 | * all copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | * IN THE SOFTWARE. 20 | */ 21 | 22 | /* 23 | * This file is private to libuv. It provides common functionality to both 24 | * Windows and Unix backends. 25 | */ 26 | 27 | #ifndef UV_THREADPOOL_H_ 28 | #define UV_THREADPOOL_H_ 29 | 30 | struct uv__work { 31 | void (*work)(struct uv__work *w); 32 | void (*done)(struct uv__work *w, int status); 33 | struct uv_loop_s* loop; 34 | void* wq[2]; 35 | }; 36 | 37 | #endif /* UV_THREADPOOL_H_ */ 38 | -------------------------------------------------------------------------------- /Trevi/Source/Libuv/uv-version.h: -------------------------------------------------------------------------------- 1 | /* Copyright Joyent, Inc. and other Node contributors. All rights reserved. 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy 4 | * of this software and associated documentation files (the "Software"), to 5 | * deal in the Software without restriction, including without limitation the 6 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | * sell copies of the Software, and to permit persons to whom the Software is 8 | * furnished to do so, subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in 11 | * all copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | * IN THE SOFTWARE. 20 | */ 21 | 22 | #ifndef UV_VERSION_H 23 | #define UV_VERSION_H 24 | 25 | /* 26 | * Versions with the same major number are ABI stable. API is allowed to 27 | * evolve between minor releases, but only in a backwards compatible way. 28 | * Make sure you update the -soname directives in configure.ac 29 | * and uv.gyp whenever you bump UV_VERSION_MAJOR or UV_VERSION_MINOR (but 30 | * not UV_VERSION_PATCH.) 31 | */ 32 | 33 | #define UV_VERSION_MAJOR 1 34 | #define UV_VERSION_MINOR 8 35 | #define UV_VERSION_PATCH 1 36 | #define UV_VERSION_IS_RELEASE 0 37 | #define UV_VERSION_SUFFIX "dev" 38 | 39 | #define UV_VERSION_HEX ((UV_VERSION_MAJOR << 16) | \ 40 | (UV_VERSION_MINOR << 8) | \ 41 | (UV_VERSION_PATCH)) 42 | 43 | #endif /* UV_VERSION_H */ 44 | -------------------------------------------------------------------------------- /Trevi/Source/LibuvError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LibuvError.swift 3 | // Trevi 4 | // 5 | // Created by JangTaehwan on 2016. 3. 10.. 6 | // Copyright © 2016년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Libuv 10 | import Foundation 11 | 12 | /** 13 | Libuv system error log. 14 | */ 15 | public class LibuvError : ErrorType { 16 | 17 | public static func printState( location : String, error : Int32 ) { 18 | print("Error on : \(location), name : \(uvErrorName(error)), message : \(uvErrorMessage(error))") 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Trevi/Source/LibuvUtility.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LibuvUtility.swift 3 | // Trevi 4 | // 5 | // Created by JangTaehwan on 2016. 2. 17.. 6 | // Copyright © 2016년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | 10 | import Libuv 11 | 12 | 13 | public typealias void_ptr = UnsafeMutablePointer 14 | public typealias sockaddr_ptr = UnsafeMutablePointer 15 | 16 | 17 | public typealias uv_any_handle_ptr = UnsafeMutablePointer 18 | public typealias uv_handle_ptr = UnsafeMutablePointer 19 | public typealias uv_loop_ptr = UnsafeMutablePointer 20 | public typealias uv_poll_ptr = UnsafeMutablePointer 21 | public typealias uv_stream_ptr = UnsafeMutablePointer 22 | public typealias uv_connect_ptr = UnsafeMutablePointer 23 | public typealias uv_pipe_ptr = UnsafeMutablePointer 24 | public typealias uv_tcp_ptr = UnsafeMutablePointer 25 | public typealias uv_shutdown_ptr = UnsafeMutablePointer 26 | public typealias uv_timer_ptr = UnsafeMutablePointer 27 | public typealias uv_async_ptr = UnsafeMutablePointer 28 | 29 | 30 | public struct write_req_t { 31 | let request : uv_write_t 32 | var buffer : uv_buf_ptr 33 | } 34 | 35 | public typealias uv_req_ptr = UnsafeMutablePointer 36 | public typealias uv_write_ptr = UnsafeMutablePointer 37 | public typealias uv_work_ptr = UnsafeMutablePointer 38 | public typealias uv_fs_ptr = UnsafeMutablePointer 39 | public typealias write_req_ptr = UnsafeMutablePointer 40 | 41 | 42 | public typealias uv_buf_ptr = UnsafeMutablePointer 43 | public typealias uv_buf_const_ptr = UnsafePointer 44 | 45 | 46 | func ==(lhs: uv_fs_type, rhs: uv_fs_type) -> Bool { 47 | return lhs.hashValue == rhs.hashValue 48 | } 49 | 50 | extension uv_fs_type : Hashable { 51 | 52 | public var hashValue: Int { 53 | return Int(self.rawValue) 54 | } 55 | } 56 | 57 | 58 | // Reference form 59 | // https://developer.apple.com/library/ios/samplecode/SimpleTunnel/Listings/tunnel_server_UDPServerConnection_swift.html 60 | // * Example 61 | // let addressInfo = Tcp.getPeerName(uv_tcp_ptr(handle)) 62 | // let (ip, port) = getEndpointFromSocketAddress(addressInfo)! 63 | // print("New client! ip : \(ip), port : \(port).") 64 | public func getEndpointFromSocketAddress(socketAddressPointer: sockaddr_ptr) -> (host: String, port: Int)? { 65 | let socketAddress = UnsafePointer(socketAddressPointer).memory 66 | 67 | switch Int32(socketAddress.sa_family) { 68 | case AF_INET: 69 | var socketAddressInet = UnsafePointer(socketAddressPointer).memory 70 | let length = Int(INET_ADDRSTRLEN) + 2 71 | var buffer = [CChar](count: length, repeatedValue: 0) 72 | let hostCString = inet_ntop(AF_INET, &socketAddressInet.sin_addr, &buffer, socklen_t(length)) 73 | let port = Int(UInt16(socketAddressInet.sin_port).byteSwapped) 74 | socketAddressPointer.dealloc(1) 75 | return (String.fromCString(hostCString)!, port) 76 | 77 | case AF_INET6: 78 | var socketAddressInet6 = UnsafePointer(socketAddressPointer).memory 79 | let length = Int(INET6_ADDRSTRLEN) + 2 80 | var buffer = [CChar](count: length, repeatedValue: 0) 81 | let hostCString = inet_ntop(AF_INET6, &socketAddressInet6.sin6_addr, &buffer, socklen_t(length)) 82 | let port = Int(UInt16(socketAddressInet6.sin6_port).byteSwapped) 83 | socketAddressPointer.dealloc(1) 84 | return (String.fromCString(hostCString)!, port) 85 | 86 | default: 87 | return nil 88 | } 89 | } 90 | 91 | public func uvErrorName(type : Int32) -> String { 92 | 93 | return NSString(UTF8String: uv_err_name(type))! as String 94 | } 95 | 96 | public func uvErrorMessage(type : Int32) -> String { 97 | 98 | return NSString(UTF8String: uv_strerror(type))! as String 99 | } 100 | 101 | #if os(OSX) 102 | public func getThreadID() -> mach_port_t { 103 | return pthread_mach_thread_np(pthread_self()) 104 | 105 | } 106 | #endif 107 | 108 | 109 | -------------------------------------------------------------------------------- /Trevi/Source/Loop.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Loop.swift 3 | // Trevi 4 | // 5 | // Created by JangTaehwan on 2016. 2. 21.. 6 | // Copyright © 2016년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Libuv 10 | 11 | 12 | /** 13 | Libuv Loop bindings. 14 | */ 15 | public class Loop { 16 | 17 | public let loopHandle : uv_loop_ptr 18 | 19 | public init() { 20 | 21 | self.loopHandle = uv_loop_ptr.alloc(1) 22 | uv_loop_init(self.loopHandle) 23 | } 24 | 25 | deinit { 26 | self.loopHandle.dealloc(1) 27 | } 28 | 29 | } 30 | 31 | 32 | // Loop static functions. 33 | 34 | extension Loop { 35 | 36 | // Closes all internal loop resources and the loop will be terminated 37 | public static func close(handle : uv_loop_ptr) { 38 | 39 | uv_loop_close(handle) 40 | 41 | // Should handle a signal error on FileSystem writeStream. 42 | // Handle.close(uv_handle_ptr(handle)) 43 | } 44 | 45 | 46 | public static func run(handle : uv_loop_ptr = uv_default_loop(), mode : uv_run_mode) { 47 | 48 | let error = uv_run(handle, mode) 49 | 50 | if error != 0 { 51 | // Should handle error 52 | } 53 | } 54 | 55 | 56 | public static func active(handle : uv_loop_ptr) { 57 | let error = uv_loop_alive(handle) 58 | 59 | if error != 0 { 60 | // Should handle error 61 | } 62 | } 63 | 64 | 65 | public static func getFD(handle : uv_loop_ptr)->Int32 { 66 | return uv_backend_fd(handle) 67 | } 68 | 69 | 70 | public static func walk(handle : uv_loop_ptr) { 71 | 72 | uv_walk(handle, Loop.onWalk, uv_handle_ptr(handle).memory.data) 73 | } 74 | } 75 | 76 | 77 | // Loop static callbacks. 78 | 79 | extension Loop { 80 | 81 | public static var onWalk : uv_walk_cb = { (handle, argument) in 82 | 83 | } 84 | } -------------------------------------------------------------------------------- /Trevi/Source/Pipe.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Pipe.swift 3 | // Trevi 4 | // 5 | // Created by JangTaehwan on 2016. 2. 29.. 6 | // Copyright © 2016년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Libuv 10 | 11 | 12 | /** 13 | Provides interactive actions with file descriptors and Stream modules. 14 | */ 15 | public class Pipe : Stream { 16 | 17 | public let pipeHandle : uv_pipe_ptr 18 | 19 | public init(loop : uv_loop_ptr = uv_default_loop(), ipc : Int32 = 0){ 20 | self.pipeHandle = uv_pipe_ptr.alloc(1) 21 | 22 | uv_pipe_init(loop, self.pipeHandle, ipc) 23 | 24 | super.init(streamHandle: uv_stream_ptr(self.pipeHandle)) 25 | } 26 | 27 | deinit{ 28 | if isAlive { 29 | Handle.close(self.handle) 30 | self.pipeHandle.dealloc(1) 31 | isAlive = false 32 | } 33 | } 34 | 35 | } 36 | 37 | 38 | // Pipe static functions 39 | 40 | extension Pipe { 41 | 42 | public static func open(handle : uv_pipe_ptr, fd : uv_file) { 43 | 44 | let error = uv_pipe_open(handle, fd) 45 | 46 | if error != 0 { 47 | // Should handle error 48 | } 49 | } 50 | 51 | public static func bind(handle : uv_pipe_ptr, path : String) { 52 | 53 | let error = uv_pipe_bind(handle, path) 54 | 55 | if error != 0 { 56 | // Should handle error 57 | } 58 | } 59 | 60 | public static func connect(handle : uv_pipe_ptr, path : String) { 61 | 62 | let request : uv_connect_ptr = uv_connect_ptr.alloc(1) 63 | 64 | uv_pipe_connect(request, handle, path, Pipe.afterConnect) 65 | } 66 | 67 | public static func listen(handle : uv_pipe_ptr, backlog : Int32 = 50) { 68 | 69 | let error = uv_listen(uv_stream_ptr(handle), backlog, Pipe.onConnection) 70 | 71 | if error != 0 { 72 | // Should handle error 73 | } 74 | } 75 | } 76 | 77 | 78 | // Pipe static callbacks 79 | 80 | extension Pipe { 81 | 82 | public static var afterConnect : uv_connect_cb = { (request, status) in 83 | 84 | request.dealloc(1) 85 | } 86 | 87 | public static var onConnection : uv_connection_cb = { (handle, status) in 88 | 89 | var client = Pipe() 90 | 91 | if uv_accept(handle, client.streamHandle) != 0 { 92 | return 93 | } 94 | 95 | // Should add client callbacks 96 | } 97 | 98 | } 99 | 100 | -------------------------------------------------------------------------------- /Trevi/Source/Poll.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Poll.swift 3 | // Trevi 4 | // 5 | // Created by JangTaehwan on 2016. 3. 13.. 6 | // Copyright © 2016년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Libuv 10 | 11 | 12 | /** 13 | Libuv Poll bindings. 14 | */ 15 | 16 | public class Poll : Handle { 17 | 18 | let pollHandle : uv_poll_ptr 19 | 20 | public init(loop : uv_loop_ptr = uv_default_loop(), fd : Int32, isSocket : Bool) { 21 | 22 | self.pollHandle = uv_poll_ptr.alloc(1) 23 | 24 | if isSocket { 25 | uv_poll_init_socket(loop, self.pollHandle, fd) 26 | } 27 | else { 28 | uv_poll_init(loop, self.pollHandle, fd) 29 | } 30 | 31 | super.init(handle: uv_handle_ptr(self.pollHandle)) 32 | } 33 | 34 | deinit { 35 | if isAlive { 36 | Handle.close(self.handle) 37 | self.pollHandle.dealloc(1) 38 | isAlive = false 39 | } 40 | } 41 | } 42 | 43 | 44 | 45 | // Poll static functions. 46 | 47 | extension Poll { 48 | 49 | // event should be one of UV_READABLE and UV_WRITABLE. 50 | public static func start(handle : uv_poll_ptr, event : uv_poll_event) { 51 | 52 | let error = uv_poll_start(handle, Int32(event.rawValue), Poll.onStart) 53 | 54 | // Should handle error 55 | if error == 0 { 56 | 57 | } 58 | } 59 | } 60 | 61 | 62 | // Poll static callbacks. 63 | 64 | extension Poll { 65 | 66 | public static var onStart : uv_poll_cb = { (handle, first, second) in 67 | 68 | if let wrap = Handle.dictionary[uv_handle_ptr(handle)] { 69 | // Should add Poll start callback event 70 | 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /Trevi/Source/Stream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Stream.swift 3 | // Trevi 4 | // 5 | // Created by JangTaehwan on 2016. 2. 17.. 6 | // Copyright © 2016년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | 10 | import Libuv 11 | import Foundation 12 | 13 | 14 | /** 15 | Libuv stream bindings and allow the user to use a closure on event. 16 | Also, provides improved data read, write stream modules. 17 | */ 18 | public class Stream : Handle { 19 | 20 | public let streamHandle : uv_stream_ptr 21 | 22 | public init (streamHandle : uv_stream_ptr){ 23 | self.streamHandle = streamHandle 24 | super.init(handle: uv_handle_ptr(streamHandle)) 25 | } 26 | 27 | deinit{ 28 | if isAlive { 29 | Handle.close(self.handle) 30 | self.streamHandle.dealloc(1) 31 | isAlive = false 32 | } 33 | } 34 | 35 | 36 | public func readStart() { 37 | 38 | // Set onRead event from other thread in thread pool. Not stable yet. 39 | // uv_read_start(self.streamHandle, Stream.onAlloc, Work.onRead) 40 | 41 | uv_read_start(self.streamHandle, Stream.onAlloc, Stream.onRead) 42 | } 43 | 44 | 45 | public func doTryWrite(buffer: UnsafeMutablePointer, count : UnsafeMutablePointer) -> Int32 { 46 | var error : Int32 47 | var written : Int32 48 | var vbuffer : uv_buf_ptr = buffer.memory 49 | var vcount : UInt32 = count.memory 50 | 51 | error = uv_try_write(self.streamHandle, vbuffer, vcount) 52 | 53 | guard (error != UV_ENOSYS.rawValue && error != UV_EAGAIN.rawValue) else { 54 | return 0 55 | } 56 | guard error >= 0 else { 57 | return error 58 | } 59 | 60 | written = error 61 | while vcount > 0 { 62 | if vbuffer[0].len > Int(written) { 63 | vbuffer[0].base.initialize(vbuffer[0].base[Int(written)]) 64 | vbuffer[0].len -= Int(written) 65 | written = 0 66 | break; 67 | } 68 | else { 69 | written -= vbuffer[0].len; 70 | } 71 | 72 | vbuffer = vbuffer.successor() 73 | vcount = vcount.predecessor() 74 | } 75 | 76 | buffer.memory = vbuffer; 77 | count.memory = vcount; 78 | 79 | return 0 80 | } 81 | 82 | } 83 | 84 | 85 | // Stream static functions. 86 | 87 | extension Stream { 88 | 89 | 90 | public static func readStart(handle : uv_stream_ptr) { 91 | 92 | // Set onRead event from other thread in thread pool. Not stable yet. 93 | // uv_read_start(handle, Stream.onAlloc, Work.onRead) 94 | 95 | uv_read_start(handle, Stream.onAlloc, Stream.onRead) 96 | } 97 | 98 | public func readStop(handle : uv_stream_ptr) -> Int32 { 99 | return uv_read_stop(self.streamHandle) 100 | } 101 | 102 | public static func doShutDown(handle : uv_stream_ptr) -> Int32 { 103 | 104 | let request = uv_shutdown_ptr.alloc(1) 105 | var error : Int32 106 | 107 | error = uv_shutdown(request, handle, Stream.afterShutdown) 108 | 109 | return error 110 | } 111 | 112 | 113 | public static func doWrite(data : NSData, handle : uv_stream_ptr, 114 | count : UInt32 = 1, sendHandle : uv_stream_ptr! = nil) -> Int { 115 | 116 | let error : Int32 117 | let buffer = uv_buf_ptr.alloc(1) 118 | let request : write_req_ptr = write_req_ptr.alloc(1) 119 | 120 | request.memory.buffer = buffer 121 | 122 | buffer.memory = uv_buf_init(UnsafeMutablePointer(data.bytes), UInt32(data.length)) 123 | 124 | if sendHandle != nil { 125 | error = uv_write2(uv_write_ptr(request), handle, buffer, count, sendHandle, Stream.afterWrite) 126 | } 127 | else { 128 | error = uv_write(uv_write_ptr(request), handle, buffer, count, Stream.afterWrite) 129 | } 130 | 131 | 132 | if error == 0 { 133 | // Should add count module 134 | 135 | // 136 | } 137 | 138 | return 1 139 | } 140 | 141 | public static func isReadable (handle : uv_stream_ptr) -> Bool { 142 | 143 | return uv_is_readable(handle) == 1 144 | } 145 | 146 | public static func isWritable (handle : uv_stream_ptr) -> Bool { 147 | 148 | return uv_is_writable(handle) == 1 149 | } 150 | 151 | public static func setBlocking (handle : uv_stream_ptr, blocking : Int32) { 152 | 153 | uv_stream_set_blocking(handle, blocking) 154 | } 155 | 156 | public static func isNamedPipe (handle : uv_stream_ptr) -> Bool { 157 | 158 | return handle.memory.type == UV_NAMED_PIPE 159 | } 160 | 161 | public static func isNamedPipeIpc (handle : uv_stream_ptr) -> Bool { 162 | 163 | return Stream.isNamedPipe(handle) && uv_pipe_ptr(handle).memory.ipc != 0 164 | } 165 | 166 | public static func getHandleType (handle : uv_stream_ptr) -> uv_handle_type { 167 | var type : uv_handle_type = UV_UNKNOWN_HANDLE 168 | 169 | if Stream.isNamedPipe(handle) && uv_pipe_pending_count(uv_pipe_ptr(handle)) > 0 { 170 | type = uv_pipe_pending_type(uv_pipe_ptr(handle)) 171 | } 172 | 173 | return type 174 | } 175 | } 176 | 177 | 178 | // Stream static callbacks. 179 | 180 | extension Stream { 181 | 182 | public static var onAlloc : uv_alloc_cb = { (_, suggestedSize, buffer) in 183 | 184 | buffer.initialize(uv_buf_init(UnsafeMutablePointer.alloc(suggestedSize), UInt32(suggestedSize))) 185 | } 186 | 187 | 188 | public static var onRead : uv_read_cb = { (handle, nread, buffer) in 189 | 190 | if nread <= 0 { 191 | if Int32(nread) == UV_EOF.rawValue { 192 | Handle.close(uv_handle_ptr(handle)) 193 | } 194 | else { 195 | 196 | LibuvError.printState("Stream.onRead", error : Int32(nread)) 197 | } 198 | } 199 | else if let wrap = Handle.dictionary[uv_handle_ptr(handle)] { 200 | if let callback = wrap.event.onRead { 201 | 202 | let data = NSData(bytesNoCopy : buffer.memory.base, length : nread) 203 | callback(handle, data) 204 | } 205 | } 206 | } 207 | 208 | 209 | public static var afterShutdown : uv_shutdown_cb = { (request, status) in 210 | 211 | let handle = request.memory.handle 212 | 213 | if status < 0 { 214 | 215 | LibuvError.printState("Stream.afterShutdown", error : status) 216 | } 217 | 218 | Handle.close(uv_handle_ptr(handle)) 219 | request.dealloc(1) 220 | } 221 | 222 | 223 | public static var afterWrite : uv_write_cb = { (request, status) in 224 | 225 | let writeRequest = write_req_ptr(request) 226 | let handle = writeRequest.memory.request.handle 227 | let buffer = writeRequest.memory.buffer 228 | 229 | if let wrap = Handle.dictionary[uv_handle_ptr(handle)] { 230 | if let callback = wrap.event.onAfterWrite { 231 | callback(handle) 232 | } 233 | } 234 | 235 | if buffer.memory.len > 0 { 236 | buffer.memory.base.dealloc(buffer.memory.len) 237 | } 238 | buffer.dealloc(1) 239 | 240 | uv_cancel(uv_req_ptr(request)) 241 | writeRequest.dealloc(1) 242 | } 243 | 244 | } -------------------------------------------------------------------------------- /Trevi/Source/Tcp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Tcp.swift 3 | // Trevi 4 | // 5 | // Created by JangTaehwan on 2016. 2. 17.. 6 | // Copyright © 2016년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Libuv 10 | 11 | 12 | /** 13 | Libuv Tcp bindings and allow the user to use a closure on event. 14 | */ 15 | public class Tcp : Stream { 16 | 17 | public let tcpHandle : uv_tcp_ptr 18 | 19 | public init () { 20 | 21 | self.tcpHandle = uv_tcp_ptr.alloc(1) 22 | 23 | uv_tcp_init(uv_default_loop(), self.tcpHandle) 24 | 25 | super.init(streamHandle : uv_stream_ptr(self.tcpHandle)) 26 | } 27 | 28 | deinit { 29 | if isAlive { 30 | Handle.close(self.handle) 31 | self.tcpHandle.dealloc(1) 32 | isAlive = false 33 | } 34 | } 35 | 36 | } 37 | 38 | 39 | 40 | // Tcp static functions. 41 | 42 | extension Tcp { 43 | 44 | 45 | public static func open (handle : uv_tcp_ptr, fd : uv_os_fd_t) { 46 | 47 | // Sets socket fd to non-block. 48 | uv_tcp_open(handle, fd) 49 | } 50 | 51 | 52 | public static func bind(handle : uv_tcp_ptr, address: String, port: Int32) -> Int32? { 53 | var sockaddr = sockaddr_in() 54 | 55 | let status = withUnsafeMutablePointer(&sockaddr) { (ptr) -> Int32 in 56 | 57 | var error = uv_ip4_addr(address, port, ptr) 58 | 59 | if error == 0 { 60 | error = uv_tcp_bind(handle , UnsafePointer(ptr), 0) 61 | } 62 | 63 | return error 64 | } 65 | 66 | if status != 0 { 67 | LibuvError.printState("Tcp.bind", error : status) 68 | return nil 69 | } 70 | 71 | return status 72 | } 73 | 74 | 75 | public static func bind6(handle : uv_tcp_ptr, address: String, port: Int32) -> Int32? { 76 | var sockaddr = sockaddr_in6() 77 | 78 | let status = withUnsafeMutablePointer(&sockaddr) { (ptr) -> Int32 in 79 | var error = uv_ip6_addr(address, port, ptr) 80 | 81 | if error == 0{ 82 | error = uv_tcp_bind(handle , UnsafePointer(ptr), 0) 83 | } 84 | 85 | return error 86 | } 87 | 88 | if status != 0 { 89 | LibuvError.printState("Tcp.bind6", error : status) 90 | return nil 91 | } 92 | 93 | return status 94 | } 95 | 96 | 97 | public static func listen(handle : uv_tcp_ptr, backlog : Int32 = 50) -> Int32? { 98 | 99 | // Set onConnection event from other thread in thread pool. Not stable yet. 100 | // let error = uv_listen(uv_stream_ptr(handle), backlog, Work.onConnection) 101 | 102 | let error = uv_listen(uv_stream_ptr(handle), backlog, Tcp.onConnection) 103 | 104 | if error != 0 { 105 | LibuvError.printState("Tcp.listen", error : error) 106 | return nil 107 | } 108 | 109 | Loop.run(mode: UV_RUN_DEFAULT) 110 | 111 | return error 112 | } 113 | 114 | 115 | public static func connect(handle : uv_tcp_ptr) -> Int32? { 116 | let request = uv_connect_ptr.alloc(1) 117 | let address = Tcp.getSocketName(handle) 118 | let error = uv_tcp_connect(request, handle, address, Tcp.afterConnect) 119 | 120 | if error != 0 { 121 | LibuvError.printState("Tcp.connect", error : error) 122 | return nil 123 | } 124 | 125 | return error 126 | } 127 | 128 | 129 | // Enable / disable Nagle’s algorithm. 130 | public static func setNoDelay (handle : uv_tcp_ptr, enable : Int32) { 131 | 132 | uv_tcp_nodelay(handle, enable) 133 | } 134 | 135 | 136 | public static func setKeepAlive (handle : uv_tcp_ptr, enable : Int32, delay : UInt32) { 137 | 138 | uv_tcp_keepalive(handle, enable, delay) 139 | } 140 | 141 | 142 | public static func setSimultaneousAccepts (handle : uv_tcp_ptr, enable : Int32) { 143 | 144 | uv_tcp_simultaneous_accepts(handle, enable) 145 | } 146 | 147 | 148 | // Should add dealloc module on return value sockaddr_ptr. 149 | // Temporary it is dealloced 150 | 151 | public static func getSocketName(handle : uv_tcp_ptr) -> sockaddr_ptr { 152 | 153 | var len = Int32(sizeof(sockaddr)) 154 | let name = sockaddr_ptr.alloc(Int(len)) 155 | 156 | uv_tcp_getsockname(handle, name, &len) 157 | 158 | return name 159 | } 160 | 161 | public static func getPeerName(handle : uv_tcp_ptr) -> sockaddr_ptr { 162 | 163 | var len = Int32(sizeof(sockaddr)) 164 | let name = sockaddr_ptr.alloc(Int(len)) 165 | 166 | uv_tcp_getpeername(handle, name, &len) 167 | 168 | return name 169 | } 170 | 171 | } 172 | 173 | 174 | // Tcp static callbacks. 175 | 176 | extension Tcp { 177 | 178 | public static var onConnection : uv_connection_cb = { (handle, status) in 179 | 180 | var client = Tcp() 181 | 182 | if uv_accept(handle, client.streamHandle) != 0 { 183 | return 184 | } 185 | 186 | if let wrap = Handle.dictionary[uv_handle_ptr(handle)] { 187 | if let callback = wrap.event.onConnection { 188 | callback(client.streamHandle) 189 | } 190 | } 191 | 192 | client.readStart() 193 | } 194 | 195 | public static var afterConnect : uv_connect_cb = { (request, status) in 196 | 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /Trevi/Source/Timer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Timer.swift 3 | // Trevi 4 | // 5 | // Created by JangTaehwan on 2016. 2. 21.. 6 | // Copyright © 2016년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Libuv 10 | 11 | 12 | /** 13 | Libuv timer bindings and allow the user to use a closure on event. 14 | */ 15 | public class Timer : Handle { 16 | 17 | public let timerhandle : uv_timer_ptr 18 | 19 | public init() { 20 | 21 | self.timerhandle = uv_timer_ptr.alloc(1) 22 | uv_timer_init(uv_default_loop(), self.timerhandle) 23 | 24 | super.init(handle: uv_handle_ptr(self.timerhandle)) 25 | 26 | } 27 | 28 | deinit { 29 | if isAlive { 30 | Handle.close(self.handle) 31 | self.timerhandle.dealloc(1) 32 | isAlive = false 33 | } 34 | } 35 | 36 | 37 | public func close() { 38 | 39 | Handle.close(self.handle) 40 | } 41 | 42 | } 43 | 44 | 45 | // Timer static functions. 46 | 47 | extension Timer { 48 | 49 | public static func start(handle : uv_timer_ptr, timeout : UInt64, count : UInt64 = 1) { 50 | uv_timer_start(handle, Timer.onTimeout , timeout, count) 51 | } 52 | 53 | public static func stop(handle : uv_timer_ptr) { 54 | uv_timer_stop(handle) 55 | } 56 | 57 | public static func again(handle : uv_timer_ptr) { 58 | uv_timer_again(handle) 59 | } 60 | 61 | public static func setRepeat(handle : uv_timer_ptr, count : UInt64) { 62 | uv_timer_set_repeat(handle, count) 63 | } 64 | } 65 | 66 | 67 | // Timer static callbacks. 68 | 69 | extension Timer { 70 | 71 | public static var onTimeout : uv_timer_cb = { (handle) in 72 | 73 | if let wrap = Handle.dictionary[uv_handle_ptr(handle)] { 74 | if let callback = wrap.event.onTimeout { 75 | callback(handle) 76 | } 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /Trevi/Source/Work.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Work.swift 3 | // Trevi 4 | // 5 | // Created by JangTaehwan on 2016. 2. 25.. 6 | // Copyright © 2016년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Libuv 10 | import Foundation 11 | 12 | /** 13 | Provides a threadpool on Tcp.onConnection and Stream.onRead events. 14 | */ 15 | public class Work { 16 | 17 | public let workRequest : uv_work_ptr 18 | 19 | init(){ 20 | self.workRequest = uv_work_ptr.alloc(1) 21 | } 22 | 23 | deinit{ 24 | self.workRequest.dealloc(1) 25 | } 26 | 27 | public func setWorkData(dataPtr : void_ptr) { 28 | self.workRequest.memory.data = dataPtr 29 | } 30 | } 31 | 32 | 33 | // Work static functions. 34 | 35 | extension Work { 36 | 37 | struct connectionInfo { 38 | var handle : uv_stream_ptr 39 | var status : Int32 40 | } 41 | 42 | 43 | // Not implemented on Trevi. 44 | // Consider using between Thread and Process server model. 45 | 46 | public static var onConnection : uv_connection_cb = { (handle, status) in 47 | 48 | let work : Work = Work() 49 | let info = UnsafeMutablePointer.alloc(1) 50 | 51 | info.memory.handle = handle 52 | info.memory.status = status 53 | 54 | work.workRequest.memory.data = void_ptr(info) 55 | 56 | uv_queue_work( uv_default_loop(), work.workRequest, workConnection, afterWork ) 57 | } 58 | 59 | struct readInfo { 60 | var handle : uv_stream_ptr 61 | var nread : Int 62 | var buffer : uv_buf_const_ptr 63 | } 64 | 65 | public static var onRead : uv_read_cb = { (handle, nread, buffer) in 66 | 67 | let work : Work = Work() 68 | let info = UnsafeMutablePointer.alloc(1) 69 | 70 | info.memory.handle = handle 71 | info.memory.nread = nread 72 | info.memory.buffer = buffer 73 | 74 | uv_queue_work( uv_default_loop(), work.workRequest, workRead, afterWork ) 75 | } 76 | } 77 | 78 | 79 | // Work static callbacks. 80 | 81 | extension Work { 82 | 83 | public static var workConnection : uv_work_cb = { (handle) in 84 | let info = UnsafeMutablePointer(handle.memory.data) 85 | 86 | Tcp.onConnection(info.memory.handle, info.memory.status) 87 | info.dealloc(1) 88 | } 89 | 90 | public static var workRead : uv_work_cb = { (handle) in 91 | let info = UnsafeMutablePointer(handle.memory.data) 92 | 93 | Tcp.onRead(info.memory.handle, info.memory.nread, info.memory.buffer) 94 | info.dealloc(1) 95 | } 96 | 97 | public static var afterWork : uv_after_work_cb = { (handle, status) in 98 | 99 | handle.dealloc(1) 100 | } 101 | } 102 | 103 | -------------------------------------------------------------------------------- /Trevi/Test/FileServer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FileServer.swift 3 | // Trevi 4 | // 5 | // Created by JangTaehwan on 2016. 3. 3.. 6 | // Copyright © 2016년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Libuv 10 | import Foundation 11 | 12 | 13 | public class FileServer { 14 | 15 | public init(){ 16 | print("init") 17 | } 18 | deinit{ 19 | print("deinit") 20 | } 21 | 22 | public func fileTestStart() { 23 | 24 | let readableStream = FileSystem.ReadStream(path: "/Users/Ingyure/Documents/testImage1.jpg") 25 | let writableStream = FileSystem.WriteStream(path: "/Users/Ingyure/Documents/testImage2.jpg") 26 | 27 | readableStream!.pipeStream(writableStream!) 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /Trevi/Test/NetEchoServer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetEchoServer.swift 3 | // Trevi 4 | // 5 | // Created by JangTaehwan on 2016. 3. 3.. 6 | // Copyright © 2016년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Libuv 10 | import Foundation 11 | 12 | 13 | public class NetEchoServer : Net { 14 | 15 | public init(){ 16 | 17 | super.init() 18 | self.on("connection", connectionListener) 19 | } 20 | 21 | public override func listen(port: Int32) -> Int32? { 22 | 23 | // print("Main thread : \(getThreadID())") 24 | print("Echo Server starts ip : \(ip), port : \(port).") 25 | 26 | guard let _ = super.listen(port) else { 27 | return nil 28 | } 29 | 30 | return 0 31 | } 32 | 33 | 34 | func connectionListener(sock: AnyObject){ 35 | 36 | let socket = sock as! Socket 37 | 38 | 39 | // let addressInfo = Tcp.getPeerName(uv_tcp_ptr(socket.handle)) 40 | // let (ip, port) = getEndpointFromSocketAddress(addressInfo)! 41 | // print("New client! ip : \(ip), port : \(port).") 42 | // print("Connect thread : \(getThreadID())") 43 | 44 | socket.ondata = { data, nread in 45 | 46 | // print("Read thread : \(getThreadID())") 47 | // print("Read length: \(nread)") 48 | socket.write(data, handle: socket.handle) 49 | } 50 | 51 | socket.onend = { 52 | // print("Close thread : \(getThreadID())") 53 | } 54 | 55 | // let fileserver = FileServer() 56 | // fileserver.fileTestStart() 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /Trevi/Trevi.h: -------------------------------------------------------------------------------- 1 | // 2 | // Trevi.h 3 | // Trevi 4 | // 5 | // Created by LeeYoseob on 2015. 11. 30.. 6 | // Copyright © 2015년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for Trevi. 12 | FOUNDATION_EXPORT double TreviVersionNumber; 13 | 14 | //! Project version string for Trevi. 15 | FOUNDATION_EXPORT const unsigned char TreviVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | -------------------------------------------------------------------------------- /Trevi/Utility/File.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // Trevi 4 | // 5 | // Created by SeungHyun Lee on 2015. 12. 5.. 6 | // Copyright © 2015년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | /* 13 | This Protocol use File Type Class Of Super 14 | When need path, name, value, type of File object, impliment this Protocol 15 | Now It use file descripter Multipart/form-data 16 | */ 17 | 18 | public protocol File{ 19 | 20 | var name: String! {get set} 21 | var value: String! {get set} 22 | 23 | var type: String! {get set} 24 | var path: String! {get set} 25 | } -------------------------------------------------------------------------------- /Trevi/Utility/Json.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Json.swift 3 | // Trevi 4 | // 5 | // Created by LeeYoseob on 2015. 12. 9.. 6 | // Copyright © 2015년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum JSON { 12 | case Array( [JSON] ) 13 | case Dictionary( [Swift.String:JSON] ) 14 | case String( Swift.String ) 15 | case Number( Float ) 16 | case Null 17 | } 18 | 19 | extension JSON { 20 | 21 | public var string: Swift.String? { 22 | switch self { 23 | case .String(let s): 24 | return s 25 | default: 26 | return nil 27 | } 28 | } 29 | 30 | public var int: Int? { 31 | switch self { 32 | case .Number(let d): 33 | return Int ( d ) 34 | default: 35 | return nil 36 | } 37 | } 38 | 39 | public var float: Float? { 40 | switch self { 41 | case .Number(let d): 42 | return d 43 | default: 44 | return nil 45 | } 46 | } 47 | 48 | public var bool: Bool? { 49 | switch self { 50 | case .Number(let d): 51 | return (d != 0) 52 | default: 53 | return nil 54 | } 55 | } 56 | 57 | public var isNull: Bool { 58 | switch self { 59 | case Null: 60 | return true 61 | default: 62 | return false 63 | } 64 | } 65 | 66 | public func wrap ( json: AnyObject ) -> JSON { 67 | if let str = json as? Swift.String { 68 | return .String ( str ) 69 | } 70 | if let num = json as? NSNumber { 71 | return .Number ( num.floatValue ) 72 | } 73 | if let dictionary = json as? [Swift.String:AnyObject] { 74 | 75 | return .Dictionary ( internalConvertDictionary ( dictionary ) ) 76 | } 77 | if let array = json as? [AnyObject] { 78 | return .Array ( internalConvertArray ( array ) ) 79 | } 80 | assert ( json is NSNull, "Unsupported Type" ) 81 | return .Null 82 | } 83 | 84 | private func internalConvertDictionary ( dic: [Swift.String:AnyObject] ) -> [Swift.String:JSON]! { 85 | // var newDictionary = [:] 86 | // for (k,v) in dic{ 87 | // switch v{ 88 | // case let arr as [AnyObject]: 89 | // var newarr = internalConvertArray(arr) 90 | // 91 | // print(arr) 92 | // case let dic as [Swift.String: AnyObject]: 93 | // internalConvertDictionary(dic) 94 | // default: 95 | // wrap(v) 96 | // break 97 | // 98 | // } 99 | // } 100 | 101 | return nil; 102 | } 103 | 104 | private func internalConvertArray ( arr: [AnyObject] ) -> [JSON]! { 105 | return nil 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /Trevi/Utility/Utility.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Utility.swift 3 | // Trevi 4 | // 5 | // Created by LeeYoseob on 2015. 11. 30.. 6 | // Copyright © 2015년 LeeYoseob. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public let __dirname = NSFileManager.defaultManager().currentDirectoryPath 12 | 13 | #if os(Linux) 14 | // Wrapper for casting between AnyObject and String 15 | public class StringWrapper { 16 | public var string: String 17 | 18 | public init(string: String) { 19 | self.string = string 20 | } 21 | } 22 | #endif 23 | 24 | extension String { 25 | public func length() -> Int { 26 | return self.characters.count 27 | } 28 | 29 | public func trim() -> String { 30 | return self.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet()) 31 | } 32 | 33 | public func substring(start: Int, length: Int) -> String { 34 | return self[self.startIndex.advancedBy(start) ..< self.startIndex.advancedBy(start + length)] 35 | } 36 | 37 | public func getIndex(location: Int, encoding: UInt = NSUnicodeStringEncoding) -> String.Index? { 38 | switch (encoding) { 39 | case NSUTF8StringEncoding: 40 | return String.Index ( utf8.startIndex.advancedBy ( location, limit: utf8.endIndex ), within: self )! 41 | case NSUTF16StringEncoding: 42 | return String.Index ( utf16.startIndex.advancedBy ( location, limit: utf16.endIndex ), within: self )! 43 | case NSUnicodeStringEncoding: 44 | return startIndex.advancedBy ( location ) 45 | default: 46 | return nil 47 | } 48 | } 49 | } 50 | 51 | /** 52 | - Parameter string: The string to search. 53 | - Parameter pattern: The regular expression pattern to compile. 54 | - Parameter options: The regular expression options that are applied to the expression during matching. See NSRegularExpressionOptions for possible values. 55 | 56 | - Returns: An array of tuples that include NSRange and String which are searched with regular expression. 57 | */ 58 | public func searchWithRegularExpression ( string: String, pattern: String, options: NSRegularExpressionOptions = [] ) -> [[String : (range: NSRange, text: String)]] { 59 | var searched = [[String : (range: NSRange, text: String)]]() 60 | 61 | if let regex: NSRegularExpression = try? NSRegularExpression ( pattern: pattern, options: options ) { 62 | for matches in regex.matchesInString ( string, options: [], range: NSMakeRange( 0, string.characters.count ) ) { 63 | var group = [String : (range: NSRange, text: String)]() 64 | for idx in 0 ..< matches.numberOfRanges { 65 | let range = matches.rangeAtIndex( idx ) 66 | group.updateValue((matches.rangeAtIndex(idx), string[string.startIndex.advancedBy(range.location) ..< string.startIndex.advancedBy(range.location + range.length)]), forKey: "$\(idx)") 67 | } 68 | searched.append(group) 69 | } 70 | } 71 | 72 | return searched 73 | } 74 | 75 | public func getCurrentDatetime(format: String = "yyyy/MM/dd hh:mm:ss a z") -> String { 76 | let formatter = NSDateFormatter() 77 | formatter.dateFormat = format 78 | return formatter.stringFromDate(NSDate()) 79 | } 80 | 81 | public func bridge(obj : T) -> UnsafePointer { 82 | return UnsafePointer(Unmanaged.passUnretained(obj).toOpaque()) 83 | } 84 | 85 | public func bridge(ptr : UnsafePointer) -> T { 86 | return Unmanaged.fromOpaque(COpaquePointer(ptr)).takeUnretainedValue() 87 | } --------------------------------------------------------------------------------