├── .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 |
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 |
47 |
© Team TREVI, 2015
48 |
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 | }
--------------------------------------------------------------------------------