├── README.md ├── SwiftServe.xcworkspace └── contents.xcworkspacedata ├── SwiftServe ├── SwiftServe.xcodeproj │ └── project.pbxproj ├── SwiftServe │ ├── Connection.swift │ ├── ErrorPage.swift │ ├── Filter.swift │ ├── FilterChain.swift │ ├── GCDAsyncSocket.h │ ├── GCDAsyncSocket.m │ ├── GZipCompressor.swift │ ├── HTTPMethod.swift │ ├── HeaderKey.swift │ ├── Info.plist │ ├── JLRoutes │ │ ├── JLRoutes.h │ │ └── JLRoutes.m │ ├── JSONSerialization.swift │ ├── Logging.swift │ ├── MimeType.swift │ ├── NSData+BaristaExtensions.h │ ├── NSData+BaristaExtensions.m │ ├── NSDataExtensions.swift │ ├── Request.swift │ ├── Response.swift │ ├── Router.swift │ ├── Server.swift │ ├── SimpleEndpoint.swift │ ├── StatusCode.swift │ ├── SwiftServe-Bridging-Header.h │ └── SwiftServe.h └── SwiftServeTests │ ├── Info.plist │ └── SwiftServeTests.swift └── SwiftServeDemo ├── SwiftServeDemo.xcodeproj └── project.pbxproj ├── SwiftServeDemo ├── AppDelegate.swift ├── Base.lproj │ └── MainMenu.xib ├── Images.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Info.plist ├── SwiftServeDemo.xcdatamodeld │ ├── .xccurrentversion │ └── SwiftServeDemo.xcdatamodel │ │ └── contents └── main.swift └── SwiftServeDemoTests ├── Info.plist └── SwiftServeDemoTests.swift /README.md: -------------------------------------------------------------------------------- 1 | SwiftServe 2 | ========== 3 | An HTTP server written in Swift. 4 | 5 | The main purpose is to create a server for serving JSON endpoints using Core Data. 6 | 7 | Wish List 8 | ========= 9 | SwiftServe is nowhere near complete. Some ofthe next items to include are: 10 | * Authentication (basic, OAuth) 11 | * Object-relational mapping for Core Data 12 | * Registration and processing of REST endpoints 13 | * many more...and I am seeking a collaborator or two 14 | -------------------------------------------------------------------------------- /SwiftServe.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /SwiftServe/SwiftServe.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | E409CFF5197A098500186C94 /* Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = E409CFF4197A098500186C94 /* Filter.swift */; }; 11 | E409CFF7197A099600186C94 /* ErrorPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E409CFF6197A099600186C94 /* ErrorPage.swift */; }; 12 | E409CFF9197A0A3900186C94 /* FilterChain.swift in Sources */ = {isa = PBXBuildFile; fileRef = E409CFF8197A0A3900186C94 /* FilterChain.swift */; }; 13 | E409CFFD197A149E00186C94 /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = E409CFFC197A149E00186C94 /* Logging.swift */; }; 14 | E41B3316197AEB0B00F6C188 /* JLRoutes.h in Headers */ = {isa = PBXBuildFile; fileRef = E41B3314197AEB0B00F6C188 /* JLRoutes.h */; settings = {ATTRIBUTES = (Public, ); }; }; 15 | E41B3317197AEB0B00F6C188 /* JLRoutes.m in Sources */ = {isa = PBXBuildFile; fileRef = E41B3315197AEB0B00F6C188 /* JLRoutes.m */; }; 16 | E46522DF197DCB29005AEE9F /* HTTPMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = E46522DE197DCB29005AEE9F /* HTTPMethod.swift */; }; 17 | E47EB6AA197B29C600D6ADEA /* GZipCompressor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47EB6A9197B29C600D6ADEA /* GZipCompressor.swift */; }; 18 | E47EB6B0197B2D1700D6ADEA /* NSData+BaristaExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = E47EB6AC197B2D1700D6ADEA /* NSData+BaristaExtensions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 19 | E47EB6B1197B2D1700D6ADEA /* NSData+BaristaExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = E47EB6AD197B2D1700D6ADEA /* NSData+BaristaExtensions.m */; }; 20 | E47EB6B5197B2DB700D6ADEA /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = E47EB6B4197B2DB700D6ADEA /* libz.dylib */; }; 21 | E4AA9C7D1979C66D00D87153 /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4AA9C7C1979C66D00D87153 /* Response.swift */; }; 22 | E4AA9C7F1979CB1E00D87153 /* HeaderKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4AA9C7E1979CB1E00D87153 /* HeaderKey.swift */; }; 23 | E4B7D3FF1980289F00F3DBFB /* MimeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B7D3FE1980289F00F3DBFB /* MimeType.swift */; }; 24 | E4B7D401198040C000F3DBFB /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B7D400198040C000F3DBFB /* Router.swift */; }; 25 | E4C8CC97197886BD000B8FAE /* SwiftServe.h in Headers */ = {isa = PBXBuildFile; fileRef = E4C8CC96197886BC000B8FAE /* SwiftServe.h */; settings = {ATTRIBUTES = (Public, ); }; }; 26 | E4C8CC9D197886BD000B8FAE /* SwiftServe.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E4C8CC91197886BC000B8FAE /* SwiftServe.framework */; }; 27 | E4C8CCA4197886BD000B8FAE /* SwiftServeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8CCA3197886BD000B8FAE /* SwiftServeTests.swift */; }; 28 | E4C8CCE119788740000B8FAE /* Server.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8CCE019788740000B8FAE /* Server.swift */; }; 29 | E4C8CCE519789724000B8FAE /* GCDAsyncSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = E4C8CCE419789724000B8FAE /* GCDAsyncSocket.m */; }; 30 | E4C8CCE71978972F000B8FAE /* GCDAsyncSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = E4C8CCE61978972F000B8FAE /* GCDAsyncSocket.h */; settings = {ATTRIBUTES = (Public, ); }; }; 31 | E4C8CCEB19796B1F000B8FAE /* Connection.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8CCEA19796B1F000B8FAE /* Connection.swift */; }; 32 | E4C8CCEE19796FE9000B8FAE /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8CCED19796FE9000B8FAE /* Request.swift */; }; 33 | E4C8CCF219797D4F000B8FAE /* NSDataExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8CCF119797D4F000B8FAE /* NSDataExtensions.swift */; }; 34 | E4EFA981197D7EA80077C7F8 /* JSONSerialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4EFA980197D7EA80077C7F8 /* JSONSerialization.swift */; }; 35 | E4F27F74197F229D00C1E6B6 /* SimpleEndpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F27F73197F229D00C1E6B6 /* SimpleEndpoint.swift */; }; 36 | E4F6F1E51979E3970090EBC8 /* StatusCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F6F1E41979E3970090EBC8 /* StatusCode.swift */; }; 37 | /* End PBXBuildFile section */ 38 | 39 | /* Begin PBXContainerItemProxy section */ 40 | E4C8CC9E197886BD000B8FAE /* PBXContainerItemProxy */ = { 41 | isa = PBXContainerItemProxy; 42 | containerPortal = E4C8CC88197886BC000B8FAE /* Project object */; 43 | proxyType = 1; 44 | remoteGlobalIDString = E4C8CC90197886BC000B8FAE; 45 | remoteInfo = SwiftServe; 46 | }; 47 | E4C8CCE21978921F000B8FAE /* PBXContainerItemProxy */ = { 48 | isa = PBXContainerItemProxy; 49 | containerPortal = E4C8CC88197886BC000B8FAE /* Project object */; 50 | proxyType = 1; 51 | remoteGlobalIDString = E4C8CC90197886BC000B8FAE; 52 | remoteInfo = SwiftServe; 53 | }; 54 | /* End PBXContainerItemProxy section */ 55 | 56 | /* Begin PBXFileReference section */ 57 | E409CFF4197A098500186C94 /* Filter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Filter.swift; sourceTree = ""; }; 58 | E409CFF6197A099600186C94 /* ErrorPage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ErrorPage.swift; sourceTree = ""; }; 59 | E409CFF8197A0A3900186C94 /* FilterChain.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FilterChain.swift; sourceTree = ""; }; 60 | E409CFFC197A149E00186C94 /* Logging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logging.swift; sourceTree = ""; }; 61 | E41B3314197AEB0B00F6C188 /* JLRoutes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JLRoutes.h; sourceTree = ""; }; 62 | E41B3315197AEB0B00F6C188 /* JLRoutes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JLRoutes.m; sourceTree = ""; }; 63 | E46522DE197DCB29005AEE9F /* HTTPMethod.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPMethod.swift; sourceTree = ""; }; 64 | E47EB6A9197B29C600D6ADEA /* GZipCompressor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GZipCompressor.swift; sourceTree = ""; }; 65 | E47EB6AC197B2D1700D6ADEA /* NSData+BaristaExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSData+BaristaExtensions.h"; path = "../NSData+BaristaExtensions.h"; sourceTree = ""; }; 66 | E47EB6AD197B2D1700D6ADEA /* NSData+BaristaExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSData+BaristaExtensions.m"; path = "../NSData+BaristaExtensions.m"; sourceTree = ""; }; 67 | E47EB6B4197B2DB700D6ADEA /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; }; 68 | E4AA9C7C1979C66D00D87153 /* Response.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Response.swift; sourceTree = ""; }; 69 | E4AA9C7E1979CB1E00D87153 /* HeaderKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeaderKey.swift; sourceTree = ""; }; 70 | E4B7D3FE1980289F00F3DBFB /* MimeType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MimeType.swift; sourceTree = ""; }; 71 | E4B7D400198040C000F3DBFB /* Router.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = ""; }; 72 | E4C8CC91197886BC000B8FAE /* SwiftServe.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftServe.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 73 | E4C8CC95197886BC000B8FAE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 74 | E4C8CC96197886BC000B8FAE /* SwiftServe.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SwiftServe.h; sourceTree = ""; }; 75 | E4C8CC9C197886BD000B8FAE /* SwiftServeTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftServeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 76 | E4C8CCA2197886BD000B8FAE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 77 | E4C8CCA3197886BD000B8FAE /* SwiftServeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftServeTests.swift; sourceTree = ""; }; 78 | E4C8CCE019788740000B8FAE /* Server.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Server.swift; sourceTree = ""; }; 79 | E4C8CCE419789724000B8FAE /* GCDAsyncSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDAsyncSocket.m; sourceTree = ""; }; 80 | E4C8CCE61978972F000B8FAE /* GCDAsyncSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDAsyncSocket.h; sourceTree = ""; }; 81 | E4C8CCEA19796B1F000B8FAE /* Connection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Connection.swift; sourceTree = ""; }; 82 | E4C8CCED19796FE9000B8FAE /* Request.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Request.swift; sourceTree = ""; }; 83 | E4C8CCF119797D4F000B8FAE /* NSDataExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSDataExtensions.swift; sourceTree = ""; }; 84 | E4EFA980197D7EA80077C7F8 /* JSONSerialization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSONSerialization.swift; sourceTree = ""; }; 85 | E4F27F73197F229D00C1E6B6 /* SimpleEndpoint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SimpleEndpoint.swift; sourceTree = ""; }; 86 | E4F6F1E41979E3970090EBC8 /* StatusCode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusCode.swift; sourceTree = ""; }; 87 | /* End PBXFileReference section */ 88 | 89 | /* Begin PBXFrameworksBuildPhase section */ 90 | E4C8CC8D197886BC000B8FAE /* Frameworks */ = { 91 | isa = PBXFrameworksBuildPhase; 92 | buildActionMask = 2147483647; 93 | files = ( 94 | E47EB6B5197B2DB700D6ADEA /* libz.dylib in Frameworks */, 95 | ); 96 | runOnlyForDeploymentPostprocessing = 0; 97 | }; 98 | E4C8CC99197886BD000B8FAE /* Frameworks */ = { 99 | isa = PBXFrameworksBuildPhase; 100 | buildActionMask = 2147483647; 101 | files = ( 102 | E4C8CC9D197886BD000B8FAE /* SwiftServe.framework in Frameworks */, 103 | ); 104 | runOnlyForDeploymentPostprocessing = 0; 105 | }; 106 | /* End PBXFrameworksBuildPhase section */ 107 | 108 | /* Begin PBXGroup section */ 109 | E409CFF3197A095000186C94 /* Filters */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | E409CFF4197A098500186C94 /* Filter.swift */, 113 | E4B7D400198040C000F3DBFB /* Router.swift */, 114 | E47EB6A9197B29C600D6ADEA /* GZipCompressor.swift */, 115 | E409CFF6197A099600186C94 /* ErrorPage.swift */, 116 | E409CFFC197A149E00186C94 /* Logging.swift */, 117 | E4EFA980197D7EA80077C7F8 /* JSONSerialization.swift */, 118 | E4F27F73197F229D00C1E6B6 /* SimpleEndpoint.swift */, 119 | ); 120 | name = Filters; 121 | sourceTree = ""; 122 | }; 123 | E41B3313197AEB0B00F6C188 /* JLRoutes */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | E41B3314197AEB0B00F6C188 /* JLRoutes.h */, 127 | E41B3315197AEB0B00F6C188 /* JLRoutes.m */, 128 | ); 129 | name = JLRoutes; 130 | sourceTree = ""; 131 | }; 132 | E47EB6B6197B2DCA00D6ADEA /* Frameworks */ = { 133 | isa = PBXGroup; 134 | children = ( 135 | E47EB6B7197B37F600D6ADEA /* Steve Streza */, 136 | E4C8CCEC19796BFC000B8FAE /* GCDAsyncSocket */, 137 | E41B3313197AEB0B00F6C188 /* JLRoutes */, 138 | E47EB6B4197B2DB700D6ADEA /* libz.dylib */, 139 | ); 140 | name = Frameworks; 141 | path = JLRoutes; 142 | sourceTree = ""; 143 | }; 144 | E47EB6B7197B37F600D6ADEA /* Steve Streza */ = { 145 | isa = PBXGroup; 146 | children = ( 147 | E47EB6AC197B2D1700D6ADEA /* NSData+BaristaExtensions.h */, 148 | E47EB6AD197B2D1700D6ADEA /* NSData+BaristaExtensions.m */, 149 | ); 150 | name = "Steve Streza"; 151 | sourceTree = ""; 152 | }; 153 | E4C8CC87197886BC000B8FAE = { 154 | isa = PBXGroup; 155 | children = ( 156 | E4C8CC93197886BC000B8FAE /* SwiftServe */, 157 | E4C8CCA0197886BD000B8FAE /* SwiftServeTests */, 158 | E4C8CC92197886BC000B8FAE /* Products */, 159 | ); 160 | sourceTree = ""; 161 | }; 162 | E4C8CC92197886BC000B8FAE /* Products */ = { 163 | isa = PBXGroup; 164 | children = ( 165 | E4C8CC91197886BC000B8FAE /* SwiftServe.framework */, 166 | E4C8CC9C197886BD000B8FAE /* SwiftServeTests.xctest */, 167 | ); 168 | name = Products; 169 | sourceTree = ""; 170 | }; 171 | E4C8CC93197886BC000B8FAE /* SwiftServe */ = { 172 | isa = PBXGroup; 173 | children = ( 174 | E4C8CC96197886BC000B8FAE /* SwiftServe.h */, 175 | E4F6F1E61979E3EA0090EBC8 /* Common */, 176 | E4C8CCEF19797D2D000B8FAE /* Extensions */, 177 | E409CFF3197A095000186C94 /* Filters */, 178 | E4C8CCDB19788706000B8FAE /* Core */, 179 | E47EB6B6197B2DCA00D6ADEA /* Frameworks */, 180 | E4C8CC94197886BC000B8FAE /* Supporting Files */, 181 | ); 182 | path = SwiftServe; 183 | sourceTree = ""; 184 | }; 185 | E4C8CC94197886BC000B8FAE /* Supporting Files */ = { 186 | isa = PBXGroup; 187 | children = ( 188 | E4C8CC95197886BC000B8FAE /* Info.plist */, 189 | ); 190 | name = "Supporting Files"; 191 | sourceTree = ""; 192 | }; 193 | E4C8CCA0197886BD000B8FAE /* SwiftServeTests */ = { 194 | isa = PBXGroup; 195 | children = ( 196 | E4C8CCA3197886BD000B8FAE /* SwiftServeTests.swift */, 197 | E4C8CCA1197886BD000B8FAE /* Supporting Files */, 198 | ); 199 | path = SwiftServeTests; 200 | sourceTree = ""; 201 | }; 202 | E4C8CCA1197886BD000B8FAE /* Supporting Files */ = { 203 | isa = PBXGroup; 204 | children = ( 205 | E4C8CCA2197886BD000B8FAE /* Info.plist */, 206 | ); 207 | name = "Supporting Files"; 208 | sourceTree = ""; 209 | }; 210 | E4C8CCDB19788706000B8FAE /* Core */ = { 211 | isa = PBXGroup; 212 | children = ( 213 | E4C8CCE019788740000B8FAE /* Server.swift */, 214 | E409CFF8197A0A3900186C94 /* FilterChain.swift */, 215 | E4C8CCEA19796B1F000B8FAE /* Connection.swift */, 216 | E4C8CCED19796FE9000B8FAE /* Request.swift */, 217 | E4AA9C7C1979C66D00D87153 /* Response.swift */, 218 | ); 219 | name = Core; 220 | sourceTree = ""; 221 | }; 222 | E4C8CCEC19796BFC000B8FAE /* GCDAsyncSocket */ = { 223 | isa = PBXGroup; 224 | children = ( 225 | E4C8CCE61978972F000B8FAE /* GCDAsyncSocket.h */, 226 | E4C8CCE419789724000B8FAE /* GCDAsyncSocket.m */, 227 | ); 228 | name = GCDAsyncSocket; 229 | path = ..; 230 | sourceTree = ""; 231 | }; 232 | E4C8CCEF19797D2D000B8FAE /* Extensions */ = { 233 | isa = PBXGroup; 234 | children = ( 235 | E4C8CCF119797D4F000B8FAE /* NSDataExtensions.swift */, 236 | ); 237 | name = Extensions; 238 | sourceTree = ""; 239 | }; 240 | E4F6F1E61979E3EA0090EBC8 /* Common */ = { 241 | isa = PBXGroup; 242 | children = ( 243 | E4AA9C7E1979CB1E00D87153 /* HeaderKey.swift */, 244 | E4B7D3FE1980289F00F3DBFB /* MimeType.swift */, 245 | E46522DE197DCB29005AEE9F /* HTTPMethod.swift */, 246 | E4F6F1E41979E3970090EBC8 /* StatusCode.swift */, 247 | ); 248 | name = Common; 249 | sourceTree = ""; 250 | }; 251 | /* End PBXGroup section */ 252 | 253 | /* Begin PBXHeadersBuildPhase section */ 254 | E4C8CC8E197886BC000B8FAE /* Headers */ = { 255 | isa = PBXHeadersBuildPhase; 256 | buildActionMask = 2147483647; 257 | files = ( 258 | E47EB6B0197B2D1700D6ADEA /* NSData+BaristaExtensions.h in Headers */, 259 | E4C8CC97197886BD000B8FAE /* SwiftServe.h in Headers */, 260 | E4C8CCE71978972F000B8FAE /* GCDAsyncSocket.h in Headers */, 261 | E41B3316197AEB0B00F6C188 /* JLRoutes.h in Headers */, 262 | ); 263 | runOnlyForDeploymentPostprocessing = 0; 264 | }; 265 | /* End PBXHeadersBuildPhase section */ 266 | 267 | /* Begin PBXNativeTarget section */ 268 | E4C8CC90197886BC000B8FAE /* SwiftServe */ = { 269 | isa = PBXNativeTarget; 270 | buildConfigurationList = E4C8CCA7197886BD000B8FAE /* Build configuration list for PBXNativeTarget "SwiftServe" */; 271 | buildPhases = ( 272 | E4C8CC8C197886BC000B8FAE /* Sources */, 273 | E4C8CC8D197886BC000B8FAE /* Frameworks */, 274 | E4C8CC8E197886BC000B8FAE /* Headers */, 275 | E4C8CC8F197886BC000B8FAE /* Resources */, 276 | ); 277 | buildRules = ( 278 | ); 279 | dependencies = ( 280 | ); 281 | name = SwiftServe; 282 | productName = SwiftServe; 283 | productReference = E4C8CC91197886BC000B8FAE /* SwiftServe.framework */; 284 | productType = "com.apple.product-type.framework"; 285 | }; 286 | E4C8CC9B197886BD000B8FAE /* SwiftServeTests */ = { 287 | isa = PBXNativeTarget; 288 | buildConfigurationList = E4C8CCAA197886BD000B8FAE /* Build configuration list for PBXNativeTarget "SwiftServeTests" */; 289 | buildPhases = ( 290 | E4C8CC98197886BD000B8FAE /* Sources */, 291 | E4C8CC99197886BD000B8FAE /* Frameworks */, 292 | E4C8CC9A197886BD000B8FAE /* Resources */, 293 | ); 294 | buildRules = ( 295 | ); 296 | dependencies = ( 297 | E4C8CC9F197886BD000B8FAE /* PBXTargetDependency */, 298 | E4C8CCE31978921F000B8FAE /* PBXTargetDependency */, 299 | ); 300 | name = SwiftServeTests; 301 | productName = SwiftServeTests; 302 | productReference = E4C8CC9C197886BD000B8FAE /* SwiftServeTests.xctest */; 303 | productType = "com.apple.product-type.bundle.unit-test"; 304 | }; 305 | /* End PBXNativeTarget section */ 306 | 307 | /* Begin PBXProject section */ 308 | E4C8CC88197886BC000B8FAE /* Project object */ = { 309 | isa = PBXProject; 310 | attributes = { 311 | LastUpgradeCheck = 0600; 312 | ORGANIZATIONNAME = "Anthony Picciano"; 313 | TargetAttributes = { 314 | E4C8CC90197886BC000B8FAE = { 315 | CreatedOnToolsVersion = 6.0; 316 | }; 317 | E4C8CC9B197886BD000B8FAE = { 318 | CreatedOnToolsVersion = 6.0; 319 | TestTargetID = E4C8CC90197886BC000B8FAE; 320 | }; 321 | }; 322 | }; 323 | buildConfigurationList = E4C8CC8B197886BC000B8FAE /* Build configuration list for PBXProject "SwiftServe" */; 324 | compatibilityVersion = "Xcode 3.2"; 325 | developmentRegion = English; 326 | hasScannedForEncodings = 0; 327 | knownRegions = ( 328 | en, 329 | ); 330 | mainGroup = E4C8CC87197886BC000B8FAE; 331 | productRefGroup = E4C8CC92197886BC000B8FAE /* Products */; 332 | projectDirPath = ""; 333 | projectRoot = ""; 334 | targets = ( 335 | E4C8CC90197886BC000B8FAE /* SwiftServe */, 336 | E4C8CC9B197886BD000B8FAE /* SwiftServeTests */, 337 | ); 338 | }; 339 | /* End PBXProject section */ 340 | 341 | /* Begin PBXResourcesBuildPhase section */ 342 | E4C8CC8F197886BC000B8FAE /* Resources */ = { 343 | isa = PBXResourcesBuildPhase; 344 | buildActionMask = 2147483647; 345 | files = ( 346 | ); 347 | runOnlyForDeploymentPostprocessing = 0; 348 | }; 349 | E4C8CC9A197886BD000B8FAE /* Resources */ = { 350 | isa = PBXResourcesBuildPhase; 351 | buildActionMask = 2147483647; 352 | files = ( 353 | ); 354 | runOnlyForDeploymentPostprocessing = 0; 355 | }; 356 | /* End PBXResourcesBuildPhase section */ 357 | 358 | /* Begin PBXSourcesBuildPhase section */ 359 | E4C8CC8C197886BC000B8FAE /* Sources */ = { 360 | isa = PBXSourcesBuildPhase; 361 | buildActionMask = 2147483647; 362 | files = ( 363 | E4F27F74197F229D00C1E6B6 /* SimpleEndpoint.swift in Sources */, 364 | E4C8CCE519789724000B8FAE /* GCDAsyncSocket.m in Sources */, 365 | E47EB6B1197B2D1700D6ADEA /* NSData+BaristaExtensions.m in Sources */, 366 | E47EB6AA197B29C600D6ADEA /* GZipCompressor.swift in Sources */, 367 | E409CFFD197A149E00186C94 /* Logging.swift in Sources */, 368 | E4AA9C7F1979CB1E00D87153 /* HeaderKey.swift in Sources */, 369 | E41B3317197AEB0B00F6C188 /* JLRoutes.m in Sources */, 370 | E4F6F1E51979E3970090EBC8 /* StatusCode.swift in Sources */, 371 | E4C8CCEE19796FE9000B8FAE /* Request.swift in Sources */, 372 | E4C8CCEB19796B1F000B8FAE /* Connection.swift in Sources */, 373 | E409CFF7197A099600186C94 /* ErrorPage.swift in Sources */, 374 | E4B7D3FF1980289F00F3DBFB /* MimeType.swift in Sources */, 375 | E4B7D401198040C000F3DBFB /* Router.swift in Sources */, 376 | E4EFA981197D7EA80077C7F8 /* JSONSerialization.swift in Sources */, 377 | E409CFF9197A0A3900186C94 /* FilterChain.swift in Sources */, 378 | E46522DF197DCB29005AEE9F /* HTTPMethod.swift in Sources */, 379 | E4C8CCF219797D4F000B8FAE /* NSDataExtensions.swift in Sources */, 380 | E4AA9C7D1979C66D00D87153 /* Response.swift in Sources */, 381 | E409CFF5197A098500186C94 /* Filter.swift in Sources */, 382 | E4C8CCE119788740000B8FAE /* Server.swift in Sources */, 383 | ); 384 | runOnlyForDeploymentPostprocessing = 0; 385 | }; 386 | E4C8CC98197886BD000B8FAE /* Sources */ = { 387 | isa = PBXSourcesBuildPhase; 388 | buildActionMask = 2147483647; 389 | files = ( 390 | E4C8CCA4197886BD000B8FAE /* SwiftServeTests.swift in Sources */, 391 | ); 392 | runOnlyForDeploymentPostprocessing = 0; 393 | }; 394 | /* End PBXSourcesBuildPhase section */ 395 | 396 | /* Begin PBXTargetDependency section */ 397 | E4C8CC9F197886BD000B8FAE /* PBXTargetDependency */ = { 398 | isa = PBXTargetDependency; 399 | target = E4C8CC90197886BC000B8FAE /* SwiftServe */; 400 | targetProxy = E4C8CC9E197886BD000B8FAE /* PBXContainerItemProxy */; 401 | }; 402 | E4C8CCE31978921F000B8FAE /* PBXTargetDependency */ = { 403 | isa = PBXTargetDependency; 404 | target = E4C8CC90197886BC000B8FAE /* SwiftServe */; 405 | targetProxy = E4C8CCE21978921F000B8FAE /* PBXContainerItemProxy */; 406 | }; 407 | /* End PBXTargetDependency section */ 408 | 409 | /* Begin XCBuildConfiguration section */ 410 | E4C8CCA5197886BD000B8FAE /* Debug */ = { 411 | isa = XCBuildConfiguration; 412 | buildSettings = { 413 | ALWAYS_SEARCH_USER_PATHS = NO; 414 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 415 | CLANG_CXX_LIBRARY = "libc++"; 416 | CLANG_ENABLE_MODULES = YES; 417 | CLANG_ENABLE_OBJC_ARC = YES; 418 | CLANG_WARN_BOOL_CONVERSION = YES; 419 | CLANG_WARN_CONSTANT_CONVERSION = YES; 420 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 421 | CLANG_WARN_EMPTY_BODY = YES; 422 | CLANG_WARN_ENUM_CONVERSION = YES; 423 | CLANG_WARN_INT_CONVERSION = YES; 424 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 425 | CLANG_WARN_UNREACHABLE_CODE = YES; 426 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 427 | COPY_PHASE_STRIP = NO; 428 | CURRENT_PROJECT_VERSION = 1; 429 | ENABLE_STRICT_OBJC_MSGSEND = YES; 430 | GCC_C_LANGUAGE_STANDARD = gnu99; 431 | GCC_DYNAMIC_NO_PIC = NO; 432 | GCC_OPTIMIZATION_LEVEL = 0; 433 | GCC_PREPROCESSOR_DEFINITIONS = ( 434 | "DEBUG=1", 435 | "$(inherited)", 436 | ); 437 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 438 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 439 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 440 | GCC_WARN_UNDECLARED_SELECTOR = YES; 441 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 442 | GCC_WARN_UNUSED_FUNCTION = YES; 443 | GCC_WARN_UNUSED_VARIABLE = YES; 444 | MACOSX_DEPLOYMENT_TARGET = 10.9; 445 | METAL_ENABLE_DEBUG_INFO = YES; 446 | ONLY_ACTIVE_ARCH = YES; 447 | SDKROOT = macosx; 448 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 449 | VERSIONING_SYSTEM = "apple-generic"; 450 | VERSION_INFO_PREFIX = ""; 451 | }; 452 | name = Debug; 453 | }; 454 | E4C8CCA6197886BD000B8FAE /* Release */ = { 455 | isa = XCBuildConfiguration; 456 | buildSettings = { 457 | ALWAYS_SEARCH_USER_PATHS = NO; 458 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 459 | CLANG_CXX_LIBRARY = "libc++"; 460 | CLANG_ENABLE_MODULES = YES; 461 | CLANG_ENABLE_OBJC_ARC = YES; 462 | CLANG_WARN_BOOL_CONVERSION = YES; 463 | CLANG_WARN_CONSTANT_CONVERSION = YES; 464 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 465 | CLANG_WARN_EMPTY_BODY = YES; 466 | CLANG_WARN_ENUM_CONVERSION = YES; 467 | CLANG_WARN_INT_CONVERSION = YES; 468 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 469 | CLANG_WARN_UNREACHABLE_CODE = YES; 470 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 471 | COPY_PHASE_STRIP = YES; 472 | CURRENT_PROJECT_VERSION = 1; 473 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 474 | ENABLE_NS_ASSERTIONS = NO; 475 | ENABLE_STRICT_OBJC_MSGSEND = YES; 476 | GCC_C_LANGUAGE_STANDARD = gnu99; 477 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 478 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 479 | GCC_WARN_UNDECLARED_SELECTOR = YES; 480 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 481 | GCC_WARN_UNUSED_FUNCTION = YES; 482 | GCC_WARN_UNUSED_VARIABLE = YES; 483 | MACOSX_DEPLOYMENT_TARGET = 10.9; 484 | METAL_ENABLE_DEBUG_INFO = NO; 485 | SDKROOT = macosx; 486 | VERSIONING_SYSTEM = "apple-generic"; 487 | VERSION_INFO_PREFIX = ""; 488 | }; 489 | name = Release; 490 | }; 491 | E4C8CCA8197886BD000B8FAE /* Debug */ = { 492 | isa = XCBuildConfiguration; 493 | buildSettings = { 494 | CLANG_ENABLE_MODULES = YES; 495 | COMBINE_HIDPI_IMAGES = YES; 496 | DEFINES_MODULE = YES; 497 | DYLIB_COMPATIBILITY_VERSION = 1; 498 | DYLIB_CURRENT_VERSION = 1; 499 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 500 | FRAMEWORK_VERSION = A; 501 | INFOPLIST_FILE = SwiftServe/Info.plist; 502 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 503 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 504 | LIBRARY_SEARCH_PATHS = ( 505 | "$(inherited)", 506 | "$(SDKROOT)/usr/lib/system", 507 | ); 508 | PRODUCT_NAME = "$(TARGET_NAME)"; 509 | SKIP_INSTALL = YES; 510 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 511 | }; 512 | name = Debug; 513 | }; 514 | E4C8CCA9197886BD000B8FAE /* Release */ = { 515 | isa = XCBuildConfiguration; 516 | buildSettings = { 517 | CLANG_ENABLE_MODULES = YES; 518 | COMBINE_HIDPI_IMAGES = YES; 519 | DEFINES_MODULE = YES; 520 | DYLIB_COMPATIBILITY_VERSION = 1; 521 | DYLIB_CURRENT_VERSION = 1; 522 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 523 | FRAMEWORK_VERSION = A; 524 | INFOPLIST_FILE = SwiftServe/Info.plist; 525 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 526 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 527 | LIBRARY_SEARCH_PATHS = ( 528 | "$(inherited)", 529 | "$(SDKROOT)/usr/lib/system", 530 | ); 531 | PRODUCT_NAME = "$(TARGET_NAME)"; 532 | SKIP_INSTALL = YES; 533 | }; 534 | name = Release; 535 | }; 536 | E4C8CCAB197886BD000B8FAE /* Debug */ = { 537 | isa = XCBuildConfiguration; 538 | buildSettings = { 539 | COMBINE_HIDPI_IMAGES = YES; 540 | FRAMEWORK_SEARCH_PATHS = ( 541 | "$(DEVELOPER_FRAMEWORKS_DIR)", 542 | "$(inherited)", 543 | ); 544 | GCC_PREPROCESSOR_DEFINITIONS = ( 545 | "DEBUG=1", 546 | "$(inherited)", 547 | ); 548 | INFOPLIST_FILE = SwiftServeTests/Info.plist; 549 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 550 | METAL_ENABLE_DEBUG_INFO = YES; 551 | PRODUCT_NAME = "$(TARGET_NAME)"; 552 | }; 553 | name = Debug; 554 | }; 555 | E4C8CCAC197886BD000B8FAE /* Release */ = { 556 | isa = XCBuildConfiguration; 557 | buildSettings = { 558 | COMBINE_HIDPI_IMAGES = YES; 559 | FRAMEWORK_SEARCH_PATHS = ( 560 | "$(DEVELOPER_FRAMEWORKS_DIR)", 561 | "$(inherited)", 562 | ); 563 | INFOPLIST_FILE = SwiftServeTests/Info.plist; 564 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 565 | METAL_ENABLE_DEBUG_INFO = NO; 566 | PRODUCT_NAME = "$(TARGET_NAME)"; 567 | }; 568 | name = Release; 569 | }; 570 | /* End XCBuildConfiguration section */ 571 | 572 | /* Begin XCConfigurationList section */ 573 | E4C8CC8B197886BC000B8FAE /* Build configuration list for PBXProject "SwiftServe" */ = { 574 | isa = XCConfigurationList; 575 | buildConfigurations = ( 576 | E4C8CCA5197886BD000B8FAE /* Debug */, 577 | E4C8CCA6197886BD000B8FAE /* Release */, 578 | ); 579 | defaultConfigurationIsVisible = 0; 580 | defaultConfigurationName = Release; 581 | }; 582 | E4C8CCA7197886BD000B8FAE /* Build configuration list for PBXNativeTarget "SwiftServe" */ = { 583 | isa = XCConfigurationList; 584 | buildConfigurations = ( 585 | E4C8CCA8197886BD000B8FAE /* Debug */, 586 | E4C8CCA9197886BD000B8FAE /* Release */, 587 | ); 588 | defaultConfigurationIsVisible = 0; 589 | defaultConfigurationName = Release; 590 | }; 591 | E4C8CCAA197886BD000B8FAE /* Build configuration list for PBXNativeTarget "SwiftServeTests" */ = { 592 | isa = XCConfigurationList; 593 | buildConfigurations = ( 594 | E4C8CCAB197886BD000B8FAE /* Debug */, 595 | E4C8CCAC197886BD000B8FAE /* Release */, 596 | ); 597 | defaultConfigurationIsVisible = 0; 598 | defaultConfigurationName = Release; 599 | }; 600 | /* End XCConfigurationList section */ 601 | }; 602 | rootObject = E4C8CC88197886BC000B8FAE /* Project object */; 603 | } 604 | -------------------------------------------------------------------------------- /SwiftServe/SwiftServe/Connection.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Connection.swift 3 | // SwiftServe 4 | // 5 | // Created by Anthony Picciano on 7/18/14. 6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class Connection: NSObject, GCDAsyncSocketDelegate 12 | { 13 | let filterChain:FilterChain; 14 | let socket:GCDAsyncSocket 15 | let requestData:NSMutableData 16 | var customValues = Dictionary() 17 | 18 | var request:Request? 19 | var response:Response? 20 | var expectedContentLength:Int? 21 | var didSendResponse = false; 22 | 23 | init(socket:GCDAsyncSocket, filterChain:FilterChain) 24 | { 25 | self.socket = socket 26 | self.filterChain = filterChain 27 | requestData = NSMutableData() 28 | super.init() 29 | 30 | socket.delegate = self 31 | socket.readDataWithTimeout(10, tag: 0) 32 | } 33 | 34 | func socket(socket:GCDAsyncSocket!, didReadData data:NSData!, withTag:Int) 35 | { 36 | if !request 37 | { 38 | request = Request(data: data) 39 | var contentLength:String? = request!.value(forHeaderKey: HeaderKey.ContentLength) 40 | expectedContentLength = contentLength ? contentLength!.toInt() : 0 41 | } 42 | else 43 | { 44 | requestData.appendData(data) 45 | } 46 | 47 | if requestData.length == expectedContentLength 48 | { 49 | request!.appendRequestData(requestData) 50 | response = Response(); 51 | 52 | filterChain.processFilters(self) 53 | sendResponse() 54 | } 55 | else 56 | { 57 | socket.readDataWithTimeout(10, tag: 0) 58 | } 59 | } 60 | 61 | func sendResponse() 62 | { 63 | if didSendResponse 64 | { 65 | return; 66 | } 67 | 68 | didSendResponse = true; 69 | socket.writeData(response!.messageData, withTimeout: 5, tag: 0) 70 | socket.delegate = nil 71 | socket.disconnect() 72 | } 73 | } 74 | 75 | 76 | -------------------------------------------------------------------------------- /SwiftServe/SwiftServe/ErrorPage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ErrorPage.swift 3 | // SwiftServe 4 | // 5 | // Created by Anthony Picciano on 7/18/14. 6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class ErrorPage:Filter 12 | { 13 | override func processResponse(connection:Connection) 14 | { 15 | if connection.response!.data.length > 0 16 | { 17 | // there is already response data (perhaps JSON?), just return 18 | return 19 | } 20 | 21 | switch connection.response!.statusCode.code { 22 | case StatusCode.NOT_SET.code: 23 | connection.response!.statusCode = StatusCode.NOT_IMPLEMENTED 24 | fallthrough 25 | 26 | case 1...199, 300...599: 27 | sendErrorPage(connection) 28 | 29 | default: 30 | break 31 | } 32 | } 33 | 34 | func sendErrorPage(connection:Connection) 35 | { 36 | let appName = NSBundle.mainBundle().infoDictionary.objectForKey(kCFBundleNameKey) as String 37 | let version = NSBundle.mainBundle().infoDictionary.objectForKey("CFBundleShortVersionString") as String 38 | let host = connection.request!.value(forHeaderKey: HeaderKey.Host) 39 | let statusCodeCode:String = String(connection.response!.statusCode.code) 40 | let statusCodeDescription:String = connection.response!.statusCode.description 41 | let requestDescription:String = connection.request!.description 42 | let connectedHost:String = connection.socket.connectedHost 43 | 44 | var message:String 45 | 46 | if let constHost = host 47 | { 48 | message = "" 49 | + "\(statusCodeCode) \(statusCodeDescription)" 50 | + "

\(statusCodeDescription)

" 51 | + "

The request \"\(requestDescription)\" from \(connectedHost) failed.

" 52 | + "
\(appName)/\(version) (MacOSX) at \(constHost)
" 53 | } 54 | else 55 | { 56 | message = "" 57 | + "\(connection.response!.statusCode.code) \(connection.response!.statusCode.description)" 58 | + "

\(connection.response!.statusCode.description)

" 59 | + "

The request \"\(connection.request!)\" from \(connection.socket.connectedHost) failed.

" 60 | + "
\(appName)/\(version) (MacOSX)
" 61 | } 62 | 63 | let messageData = message.bridgeToObjectiveC().dataUsingEncoding(NSUTF8StringEncoding) 64 | connection.response!.data.appendData(messageData) 65 | } 66 | } -------------------------------------------------------------------------------- /SwiftServe/SwiftServe/Filter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Filter.swift 3 | // SwiftServe 4 | // 5 | // Created by Anthony Picciano on 7/18/14. 6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // TODO: class variable are not yet supported 12 | let connectionKey = "SwiftServe.Connection" 13 | 14 | class Filter 15 | { 16 | var includePaths:JLRoutes? 17 | var nextFilter:Filter? 18 | 19 | init() 20 | { 21 | 22 | } 23 | 24 | func processFilter(connection:Connection) 25 | { 26 | if !includePaths || includePaths!.canRouteURL(connection.request!.url) 27 | { 28 | processRequest(connection) 29 | } 30 | 31 | if nextFilter 32 | { 33 | nextFilter!.processFilter(connection) 34 | } 35 | 36 | if !includePaths || includePaths!.canRouteURL(connection.request!.url) 37 | { 38 | processResponse(connection) 39 | } 40 | } 41 | 42 | func processRequest(connection:Connection) 43 | { 44 | // override this method in subclasses 45 | } 46 | 47 | func processResponse(connection:Connection) 48 | { 49 | // override this method in subclasses 50 | } 51 | 52 | func includePath(path:String) 53 | { 54 | if !includePaths 55 | { 56 | let classname = NSString(UTF8String: object_getClassName(self)) 57 | includePaths = JLRoutes(forScheme: classname) 58 | } 59 | 60 | includePaths!.addRoute(path, handler: nil) 61 | } 62 | } -------------------------------------------------------------------------------- /SwiftServe/SwiftServe/FilterChain.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FilterChain.swift 3 | // SwiftServe 4 | // 5 | // Created by Anthony Picciano on 7/18/14. 6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class FilterChain 12 | { 13 | var filters = Array() 14 | 15 | func add(filter:Filter) 16 | { 17 | if filters.count > 0 18 | { 19 | let index = filters.count - 1 20 | let lastFiler = filters[index] 21 | lastFiler.nextFilter = filter 22 | } 23 | filters.append(filter) 24 | } 25 | 26 | func processFilters(connection:Connection) 27 | { 28 | if filters.count > 0 29 | { 30 | let firstFilter = filters[0] 31 | firstFilter.processFilter(connection) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /SwiftServe/SwiftServe/GZipCompressor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GZipCompressor.swift 3 | // SwiftServe 4 | // 5 | // Created by Anthony Picciano on 7/19/14. 6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class GZipCompressor:Filter 12 | { 13 | override func processResponse(connection:Connection) 14 | { 15 | let data = connection.response!.data 16 | 17 | if data != nil && shouldGZipResponse(connection) 18 | { 19 | let compressedData = data.barista_gzipDeflate() 20 | if compressedData != nil && !compressedData.isEqualToData(data) && compressedData.length < data.length 21 | { 22 | connection.response!.data.setData(compressedData) 23 | connection.response!.setValue("gzip", forHeaderKey: HeaderKey.ContentEncoding) 24 | } 25 | } 26 | } 27 | 28 | func shouldGZipResponse(connection:Connection) -> Bool 29 | { 30 | let acceptEncoding:String? = connection.request!.value(forHeaderKey: HeaderKey.AcceptEncoding) 31 | return acceptEncoding && acceptEncoding!.rangeOfString("gzip") 32 | } 33 | } -------------------------------------------------------------------------------- /SwiftServe/SwiftServe/HTTPMethod.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HTTPMethod.swift 3 | // SwiftServe 4 | // 5 | // Created by Anthony Picciano on 7/21/14. 6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum HTTPMethod:String 12 | { 13 | // common methods 14 | case GET = "GET" 15 | case POST = "POST" 16 | case PUT = "PUT" 17 | case DELETE = "DELETE" 18 | 19 | // lesser used methods 20 | case OPTIONS = "OPTIONS" 21 | case HEAD = "HEAD" 22 | case TRACE = "TRACE" 23 | } -------------------------------------------------------------------------------- /SwiftServe/SwiftServe/HeaderKey.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HeaderKey.swift 3 | // SwiftServe 4 | // 5 | // Created by Anthony Picciano on 7/18/14. 6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum HeaderKey:String 12 | { 13 | case Accept = "Accept" 14 | case AcceptEncoding = "Accept-Encoding" 15 | case AcceptLanguage = "Accept-Language" 16 | case Authorization = "Authorization" 17 | case CacheControl = "Cache-Control" 18 | case Connection = "Connection" 19 | case Content = "Content" 20 | case ContentEncoding = "Content-Encoding" 21 | case ContentLength = "Content-Length" 22 | case ContentType = "Content-Type" 23 | case Cookie = "Cookie" 24 | case DNT = "DNT" 25 | case Host = "Host" 26 | case UserAgent = "User-Agent" 27 | } -------------------------------------------------------------------------------- /SwiftServe/SwiftServe/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.picciano.${PRODUCT_NAME:rfc1034identifier} 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 © 2014 Anthony Picciano. All rights reserved. 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /SwiftServe/SwiftServe/JLRoutes/JLRoutes.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013, Joel Levin 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 6 | 7 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 8 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | Neither the name of JLRoutes nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 11 | */ 12 | 13 | #import 14 | 15 | 16 | static NSString *const kJLRoutePatternKey = @"JLRoutePattern"; 17 | static NSString *const kJLRouteURLKey = @"JLRouteURL"; 18 | static NSString *const kJLRouteNamespaceKey = @"JLRouteNamespace"; 19 | static NSString *const kJLRouteWildcardComponentsKey = @"JLRouteWildcardComponents"; 20 | static NSString *const kJLRoutesGlobalNamespaceKey = @"JLRoutesGlobalNamespace"; 21 | 22 | 23 | @interface JLRoutes : NSObject 24 | /** @class JLRoutes 25 | JLRoutes is a way to manage URL routes and invoke them from a URL. 26 | */ 27 | 28 | /// Returns the global routing namespace (this is used by the +addRoute methods by default) 29 | + (instancetype)globalRoutes; 30 | 31 | /// Returns a routing namespace for the given scheme 32 | + (instancetype)routesForScheme:(NSString *)scheme; 33 | 34 | /// Tells JLRoutes that it should manually replace '+' in parsed values to ' '. Defaults to YES. 35 | + (void)setShouldDecodePlusSymbols:(BOOL)shouldDeecode; 36 | + (BOOL)shouldDecodePlusSymbols; 37 | 38 | /// Registers a routePattern with default priority (0) in the receiving scheme namespace. 39 | + (void)addRoute:(NSString *)routePattern handler:(BOOL (^)(NSDictionary *parameters))handlerBlock; 40 | - (void)addRoute:(NSString *)routePattern handler:(BOOL (^)(NSDictionary *parameters))handlerBlock; // instance method 41 | 42 | /// Removes a routePattern from the receiving scheme namespace. 43 | + (void)removeRoute:(NSString *)routePattern; 44 | - (void)removeRoute:(NSString *)routePattern; // instance method 45 | 46 | /// Removes all routes from the receiving scheme namespace. 47 | + (void)removeAllRoutes; 48 | - (void)removeAllRoutes; // instance method 49 | 50 | /// Unregister and delete an entire scheme namespace 51 | + (void)unregisterRouteScheme:(NSString *)scheme; 52 | 53 | /// Registers a routePattern with default priority (0) using dictionary-style subscripting. 54 | - (void)setObject:(id)handlerBlock forKeyedSubscript:(NSString *)routePatten; 55 | 56 | /// Registers a routePattern in the global scheme namespace with a handlerBlock to call when the route pattern is matched by a URL. 57 | /// The block returns a BOOL representing if the handlerBlock actually handled the route or not. If 58 | /// a block returns NO, JLRoutes will continue trying to find a matching route. 59 | + (void)addRoute:(NSString *)routePattern priority:(NSUInteger)priority handler:(BOOL (^)(NSDictionary *parameters))handlerBlock; 60 | - (void)addRoute:(NSString *)routePattern priority:(NSUInteger)priority handler:(BOOL (^)(NSDictionary *parameters))handlerBlock; // instance method 61 | 62 | /// Routes a URL, calling handler blocks (for patterns that match URL) until one returns YES, optionally specifying add'l parameters 63 | + (BOOL)routeURL:(NSURL *)URL; 64 | + (BOOL)routeURL:(NSURL *)URL withParameters:(NSDictionary *)parameters; 65 | 66 | - (BOOL)routeURL:(NSURL *)URL; // instance method 67 | - (BOOL)routeURL:(NSURL *)URL withParameters:(NSDictionary *)parameters; // instance method 68 | 69 | /// Returns whether a route exists for a URL 70 | + (BOOL)canRouteURL:(NSURL *)URL; 71 | + (BOOL)canRouteURL:(NSURL *)URL withParameters:(NSDictionary *)parameters; 72 | 73 | - (BOOL)canRouteURL:(NSURL *)URL; // instance method 74 | - (BOOL)canRouteURL:(NSURL *)URL withParameters:(NSDictionary *)parameters; // instance method 75 | 76 | /// Prints the entire routing table 77 | + (NSString *)description; 78 | 79 | /// Allows configuration of verbose logging. Default is NO. This is mostly just helpful with debugging. 80 | + (void)setVerboseLoggingEnabled:(BOOL)loggingEnabled; 81 | + (BOOL)isVerboseLoggingEnabled; 82 | 83 | /// Controls whether or not this routes controller will try to match a URL with global routes if it can't be matched in the current namespace. Default is NO. 84 | @property (nonatomic, assign) BOOL shouldFallbackToGlobalRoutes; 85 | 86 | /// Called any time routeURL returns NO. Respects shouldFallbackToGlobalRoutes. 87 | @property (nonatomic, copy) void (^unmatchedURLHandler)(JLRoutes *routes, NSURL *URL, NSDictionary *parameters); 88 | 89 | @end 90 | -------------------------------------------------------------------------------- /SwiftServe/SwiftServe/JLRoutes/JLRoutes.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013, Joel Levin 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 6 | 7 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 8 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | Neither the name of JLRoutes nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 11 | */ 12 | 13 | #import "JLRoutes.h" 14 | 15 | 16 | static NSMutableDictionary *routeControllersMap = nil; 17 | static BOOL verboseLoggingEnabled = NO; 18 | static BOOL shouldDecodePlusSymbols = YES; 19 | 20 | 21 | @interface JLRoutes () 22 | 23 | @property (nonatomic, strong) NSMutableArray *routes; 24 | @property (nonatomic, strong) NSString *namespaceKey; 25 | 26 | + (void)verboseLogWithFormat:(NSString *)format, ...; 27 | + (BOOL)routeURL:(NSURL *)URL withController:(JLRoutes *)routesController parameters:(NSDictionary *)parameters; 28 | - (BOOL)isGlobalRoutesController; 29 | 30 | @end 31 | 32 | 33 | @interface NSString (JLRoutes) 34 | 35 | - (NSString *)JLRoutes_URLDecodedString; 36 | - (NSNumber *)JLRoutes_URLDecodedNumber; 37 | - (NSDictionary *)JLRoutes_URLParameterDictionary; 38 | 39 | @end 40 | 41 | 42 | @implementation NSString (JLRoutes) 43 | 44 | - (NSString *)JLRoutes_URLDecodedString { 45 | NSString *input = shouldDecodePlusSymbols ? [self stringByReplacingOccurrencesOfString:@"+" withString:@" " options:NSLiteralSearch range:NSMakeRange(0, self.length)] : self; 46 | return [input stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; 47 | } 48 | 49 | - (NSNumber *)JLRoutes_URLDecodedNumber { 50 | NSNumberFormatter * f = [[NSNumberFormatter alloc] init]; 51 | [f setNumberStyle:NSNumberFormatterDecimalStyle]; 52 | return [f numberFromString:self]; 53 | } 54 | 55 | - (NSDictionary *)JLRoutes_URLParameterDictionary { 56 | NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; 57 | 58 | if (self.length && [self rangeOfString:@"="].location != NSNotFound) { 59 | NSArray *keyValuePairs = [self componentsSeparatedByString:@"&"]; 60 | for (NSString *keyValuePair in keyValuePairs) { 61 | NSArray *pair = [keyValuePair componentsSeparatedByString:@"="]; 62 | // don't assume we actually got a real key=value pair. start by assuming we only got @[key] before checking count 63 | NSString *paramValue = pair.count == 2 ? pair[1] : @""; 64 | // CFURLCreateStringByReplacingPercentEscapesUsingEncoding may return NULL 65 | parameters[pair[0]] = [paramValue JLRoutes_URLDecodedString] ?: @""; 66 | } 67 | } 68 | 69 | return parameters; 70 | } 71 | 72 | @end 73 | 74 | 75 | @interface _JLRoute : NSObject 76 | 77 | @property (nonatomic, weak) JLRoutes *parentRoutesController; 78 | @property (nonatomic, strong) NSString *pattern; 79 | @property (nonatomic, strong) BOOL (^block)(NSDictionary *parameters); 80 | @property (nonatomic, assign) NSUInteger priority; 81 | @property (nonatomic, strong) NSArray *patternPathComponents; 82 | 83 | - (NSDictionary *)parametersForURL:(NSURL *)URL components:(NSArray *)URLComponents; 84 | 85 | @end 86 | 87 | 88 | @implementation _JLRoute 89 | 90 | - (NSDictionary *)parametersForURL:(NSURL *)URL components:(NSArray *)URLComponents { 91 | NSDictionary *routeParameters = nil; 92 | 93 | if (!self.patternPathComponents) { 94 | self.patternPathComponents = [[self.pattern pathComponents] filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"NOT SELF like '/'"]]; 95 | } 96 | 97 | // do a quick component count check to quickly eliminate incorrect patterns 98 | BOOL componentCountEqual = self.patternPathComponents.count == URLComponents.count; 99 | BOOL routeContainsWildcard = !NSEqualRanges([self.pattern rangeOfString:@"*"], NSMakeRange(NSNotFound, 0)); 100 | if (componentCountEqual || routeContainsWildcard) { 101 | // now that we've identified a possible match, move component by component to check if it's a match 102 | NSUInteger componentIndex = 0; 103 | NSMutableDictionary *variables = [NSMutableDictionary dictionary]; 104 | BOOL isMatch = YES; 105 | 106 | for (NSString *patternComponent in self.patternPathComponents) { 107 | NSString *URLComponent = nil; 108 | if (componentIndex < [URLComponents count]) { 109 | URLComponent = URLComponents[componentIndex]; 110 | } else if ([patternComponent isEqualToString:@"*"]) { // match /foo by /foo/* 111 | URLComponent = [URLComponents lastObject]; 112 | } 113 | 114 | if ([patternComponent hasPrefix:@":"]) { 115 | // this component is a variable 116 | NSString *variableName = [patternComponent substringFromIndex:1]; 117 | NSString *variableValue = URLComponent; 118 | if ([variableName length] > 0 && [variableValue length] > 0) { 119 | if ([variableValue JLRoutes_URLDecodedNumber] != nil) { 120 | variables[variableName] = [variableValue JLRoutes_URLDecodedNumber]; 121 | } else { 122 | variables[variableName] = [variableValue JLRoutes_URLDecodedString]; 123 | } 124 | } 125 | } else if ([patternComponent isEqualToString:@"*"]) { 126 | // match wildcards 127 | variables[kJLRouteWildcardComponentsKey] = [URLComponents subarrayWithRange:NSMakeRange(componentIndex, URLComponents.count-componentIndex)]; 128 | isMatch = YES; 129 | break; 130 | } else if (![patternComponent isEqualToString:URLComponent]) { 131 | // a non-variable component did not match, so this route doesn't match up - on to the next one 132 | isMatch = NO; 133 | break; 134 | } 135 | componentIndex++; 136 | } 137 | 138 | if (isMatch) { 139 | routeParameters = variables; 140 | } 141 | } 142 | 143 | return routeParameters; 144 | } 145 | 146 | 147 | - (NSString *)description { 148 | return [NSString stringWithFormat:@"JLRoute %@ (%@)", self.pattern, @(self.priority)]; 149 | } 150 | 151 | 152 | @end 153 | 154 | 155 | @implementation JLRoutes 156 | 157 | - (id)init { 158 | if ((self = [super init])) { 159 | self.routes = [NSMutableArray array]; 160 | } 161 | return self; 162 | } 163 | 164 | + (void)setShouldDecodePlusSymbols:(BOOL)shouldDecode { 165 | shouldDecodePlusSymbols = shouldDecode; 166 | } 167 | 168 | + (BOOL)shouldDecodePlusSymbols { 169 | return shouldDecodePlusSymbols; 170 | } 171 | 172 | #pragma mark - 173 | #pragma mark Routing API 174 | 175 | + (instancetype)globalRoutes { 176 | return [self routesForScheme:kJLRoutesGlobalNamespaceKey]; 177 | } 178 | 179 | 180 | + (instancetype)routesForScheme:(NSString *)scheme { 181 | JLRoutes *routesController = nil; 182 | 183 | static dispatch_once_t onceToken; 184 | dispatch_once(&onceToken, ^{ 185 | routeControllersMap = [[NSMutableDictionary alloc] init]; 186 | }); 187 | 188 | if (!routeControllersMap[scheme]) { 189 | routesController = [[self alloc] init]; 190 | routesController.namespaceKey = scheme; 191 | routeControllersMap[scheme] = routesController; 192 | } 193 | 194 | routesController = routeControllersMap[scheme]; 195 | 196 | return routesController; 197 | } 198 | 199 | 200 | + (void)addRoute:(NSString *)routePattern handler:(BOOL (^)(NSDictionary *parameters))handlerBlock { 201 | [[self globalRoutes] addRoute:routePattern handler:handlerBlock]; 202 | } 203 | 204 | 205 | + (void)addRoute:(NSString *)routePattern priority:(NSUInteger)priority handler:(BOOL (^)(NSDictionary *parameters))handlerBlock { 206 | [[self globalRoutes] addRoute:routePattern priority:priority handler:handlerBlock]; 207 | } 208 | 209 | 210 | - (void)addRoute:(NSString *)routePattern handler:(BOOL (^)(NSDictionary *parameters))handlerBlock { 211 | [self addRoute:routePattern priority:0 handler:handlerBlock]; 212 | } 213 | 214 | 215 | - (void)addRoute:(NSString *)routePattern priority:(NSUInteger)priority handler:(BOOL (^)(NSDictionary *parameters))handlerBlock { 216 | _JLRoute *route = [[_JLRoute alloc] init]; 217 | route.pattern = routePattern; 218 | route.priority = priority; 219 | route.block = [handlerBlock copy]; 220 | route.parentRoutesController = self; 221 | 222 | if (!route.block) { 223 | route.block = [^BOOL (NSDictionary *params) { 224 | return YES; 225 | } copy]; 226 | } 227 | 228 | if (priority == 0) { 229 | [self.routes addObject:route]; 230 | } else { 231 | NSArray *existingRoutes = self.routes; 232 | NSUInteger index = 0; 233 | for (_JLRoute *existingRoute in existingRoutes) { 234 | if (existingRoute.priority < priority) { 235 | [self.routes insertObject:route atIndex:index]; 236 | break; 237 | } 238 | index++; 239 | } 240 | } 241 | } 242 | 243 | 244 | + (void)removeRoute:(NSString *)routePattern { 245 | [[JLRoutes globalRoutes] removeRoute:routePattern]; 246 | } 247 | 248 | 249 | - (void)removeRoute:(NSString *)routePattern { 250 | if (![routePattern hasPrefix:@"/"]) { 251 | routePattern = [NSString stringWithFormat:@"/%@", routePattern]; 252 | } 253 | 254 | NSInteger routeIndex = NSNotFound; 255 | NSInteger index = 0; 256 | 257 | for (_JLRoute *route in self.routes) { 258 | if ([route.pattern isEqualToString:routePattern]) { 259 | routeIndex = index; 260 | break; 261 | } 262 | index++; 263 | } 264 | 265 | if (routeIndex != NSNotFound) { 266 | [self.routes removeObjectAtIndex:(NSUInteger)routeIndex]; 267 | } 268 | } 269 | 270 | 271 | + (void)removeAllRoutes { 272 | [[JLRoutes globalRoutes] removeAllRoutes]; 273 | } 274 | 275 | 276 | - (void)removeAllRoutes { 277 | [self.routes removeAllObjects]; 278 | } 279 | 280 | 281 | + (void)unregisterRouteScheme:(NSString *)scheme { 282 | [routeControllersMap removeObjectForKey:scheme]; 283 | } 284 | 285 | 286 | + (BOOL)routeURL:(NSURL *)URL { 287 | return [self routeURL:URL withParameters:nil executeRouteBlock:YES]; 288 | } 289 | 290 | + (BOOL)routeURL:(NSURL *)URL withParameters:(NSDictionary *)parameters { 291 | return [self routeURL:URL withParameters:parameters executeRouteBlock:YES]; 292 | } 293 | 294 | 295 | + (BOOL)canRouteURL:(NSURL *)URL { 296 | return [self routeURL:URL withParameters:nil executeRouteBlock:NO]; 297 | } 298 | 299 | + (BOOL)canRouteURL:(NSURL *)URL withParameters:(NSDictionary *)parameters { 300 | return [self routeURL:URL withParameters:parameters executeRouteBlock:NO]; 301 | } 302 | 303 | + (BOOL)routeURL:(NSURL *)URL withParameters:(NSDictionary *)parameters executeRouteBlock:(BOOL)execute { 304 | if (!URL) { 305 | return NO; 306 | } 307 | 308 | // figure out which routes controller to use based on the scheme 309 | JLRoutes *routesController = routeControllersMap[[URL scheme]] ?: [self globalRoutes]; 310 | 311 | return [self routeURL:URL withController:routesController parameters:parameters executeBlock:execute]; 312 | } 313 | 314 | - (BOOL)routeURL:(NSURL *)URL { 315 | return [[self class] routeURL:URL withController:self]; 316 | } 317 | 318 | - (BOOL)routeURL:(NSURL *)URL withParameters:(NSDictionary *)parameters { 319 | return [[self class] routeURL:URL withController:self parameters:parameters]; 320 | } 321 | 322 | - (BOOL)canRouteURL:(NSURL *)URL { 323 | return [[self class] routeURL:URL withController:self parameters:nil executeBlock:NO]; 324 | } 325 | 326 | - (BOOL)canRouteURL:(NSURL *)URL withParameters:(NSDictionary *)parameters { 327 | return [[self class] routeURL:URL withController:self parameters:parameters executeBlock:NO]; 328 | } 329 | 330 | #pragma mark - 331 | #pragma mark Debugging Aids 332 | 333 | - (NSString *)description { 334 | return [self.routes description]; 335 | } 336 | 337 | 338 | + (NSString *)description { 339 | NSMutableString *descriptionString = [NSMutableString stringWithString:@"\n"]; 340 | 341 | for (NSString *routesNamespace in routeControllersMap) { 342 | JLRoutes *routesController = routeControllersMap[routesNamespace]; 343 | [descriptionString appendFormat:@"\"%@\":\n%@\n\n", routesController.namespaceKey, routesController.routes]; 344 | } 345 | 346 | return descriptionString; 347 | } 348 | 349 | 350 | + (void)setVerboseLoggingEnabled:(BOOL)loggingEnabled { 351 | verboseLoggingEnabled = loggingEnabled; 352 | } 353 | 354 | 355 | + (BOOL)isVerboseLoggingEnabled { 356 | return verboseLoggingEnabled; 357 | } 358 | 359 | 360 | #pragma mark - 361 | #pragma mark Internal API 362 | 363 | + (BOOL)routeURL:(NSURL *)URL withController:(JLRoutes *)routesController { 364 | return [self routeURL:URL withController:routesController parameters:nil executeBlock:YES]; 365 | } 366 | 367 | + (BOOL)routeURL:(NSURL *)URL withController:(JLRoutes *)routesController parameters:(NSDictionary *)parameters { 368 | return [self routeURL:URL withController:routesController parameters:parameters executeBlock:YES]; 369 | } 370 | 371 | + (BOOL)routeURL:(NSURL *)URL withController:(JLRoutes *)routesController parameters:(NSDictionary *)parameters executeBlock:(BOOL)executeBlock { 372 | [self verboseLogWithFormat:@"Trying to route URL %@", URL]; 373 | BOOL didRoute = NO; 374 | NSArray *routes = routesController.routes; 375 | NSDictionary *queryParameters = [URL.query JLRoutes_URLParameterDictionary]; 376 | [self verboseLogWithFormat:@"Parsed query parameters: %@", queryParameters]; 377 | 378 | NSDictionary *fragmentParameters = [URL.fragment JLRoutes_URLParameterDictionary]; 379 | [self verboseLogWithFormat:@"Parsed fragment parameters: %@", fragmentParameters]; 380 | 381 | // break the URL down into path components and filter out any leading/trailing slashes from it 382 | NSArray *pathComponents = [(URL.pathComponents ?: @[]) filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"NOT SELF like '/'"]]; 383 | 384 | if ([URL.host rangeOfString:@"."].location == NSNotFound && ![URL.host isEqualToString:@"localhost"]) { 385 | // For backward compatibility, handle scheme://path/to/ressource as if path was part of the 386 | // path if it doesn't look like a domain name (no dot in it) 387 | pathComponents = [@[URL.host] arrayByAddingObjectsFromArray:pathComponents]; 388 | } 389 | 390 | [self verboseLogWithFormat:@"URL path components: %@", pathComponents]; 391 | 392 | for (_JLRoute *route in routes) { 393 | NSDictionary *matchParameters = [route parametersForURL:URL components:pathComponents]; 394 | if (matchParameters) { 395 | [self verboseLogWithFormat:@"Successfully matched %@", route]; 396 | if (!executeBlock) { 397 | return YES; 398 | } 399 | 400 | // add the URL parameters 401 | NSMutableDictionary *finalParameters = [NSMutableDictionary dictionary]; 402 | 403 | // in increasing order of precedence: query, fragment, route, builtin 404 | [finalParameters addEntriesFromDictionary:queryParameters]; 405 | [finalParameters addEntriesFromDictionary:fragmentParameters]; 406 | [finalParameters addEntriesFromDictionary:matchParameters]; 407 | [finalParameters addEntriesFromDictionary:parameters]; 408 | finalParameters[kJLRoutePatternKey] = route.pattern; 409 | finalParameters[kJLRouteURLKey] = URL; 410 | __strong __typeof(route.parentRoutesController) strongParentRoutesController = route.parentRoutesController; 411 | finalParameters[kJLRouteNamespaceKey] = strongParentRoutesController.namespaceKey ?: [NSNull null]; 412 | 413 | [self verboseLogWithFormat:@"Final parameters are %@", finalParameters]; 414 | didRoute = route.block(finalParameters); 415 | if (didRoute) { 416 | break; 417 | } 418 | } 419 | } 420 | 421 | if (!didRoute) { 422 | [self verboseLogWithFormat:@"Could not find a matching route, returning NO"]; 423 | } 424 | 425 | // if we couldn't find a match and this routes controller specifies to fallback and its also not the global routes controller, then... 426 | if (!didRoute && routesController.shouldFallbackToGlobalRoutes && ![routesController isGlobalRoutesController]) { 427 | [self verboseLogWithFormat:@"Falling back to global routes..."]; 428 | didRoute = [self routeURL:URL withController:[self globalRoutes] parameters:parameters executeBlock:executeBlock]; 429 | } 430 | 431 | // if, after everything, we did not route anything and we have an unmatched URL handler, then call it 432 | if (!didRoute && routesController.unmatchedURLHandler) { 433 | routesController.unmatchedURLHandler(routesController, URL, parameters); 434 | } 435 | 436 | return didRoute; 437 | } 438 | 439 | 440 | - (BOOL)isGlobalRoutesController { 441 | return [self.namespaceKey isEqualToString:kJLRoutesGlobalNamespaceKey]; 442 | } 443 | 444 | 445 | + (void)verboseLogWithFormat:(NSString *)format, ... { 446 | if (verboseLoggingEnabled && format) { 447 | va_list argsList; 448 | va_start(argsList, format); 449 | #pragma clang diagnostic push 450 | #pragma clang diagnostic ignored "-Wformat-nonliteral" 451 | NSString *formattedLogMessage = [[NSString alloc] initWithFormat:format arguments:argsList]; 452 | #pragma clang diagnostic pop 453 | 454 | va_end(argsList); 455 | NSLog(@"[JLRoutes]: %@", formattedLogMessage); 456 | } 457 | } 458 | 459 | #pragma mark - 460 | #pragma mark Subscripting 461 | 462 | - (void)setObject:(id)handlerBlock forKeyedSubscript:(NSString *)routePatten { 463 | [self addRoute:routePatten handler:handlerBlock]; 464 | } 465 | 466 | @end 467 | -------------------------------------------------------------------------------- /SwiftServe/SwiftServe/JSONSerialization.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JSONSerialization.swift 3 | // SwiftServe 4 | // 5 | // Created by Anthony Picciano on 7/21/14. 6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // TODO: class variable are not yet supported 12 | let jsonRequestObjectKey = "JSONRequestObject" 13 | let jsonResponseObjectKey = "JSONResponseObject" 14 | 15 | extension Connection 16 | { 17 | func JSONRequestObject() -> NSDictionary? 18 | { 19 | let value:Any? = customValues[jsonRequestObjectKey] 20 | return value as? NSDictionary 21 | } 22 | 23 | func setJSONRequestObject(object:NSDictionary) 24 | { 25 | customValues[jsonRequestObjectKey] = object 26 | } 27 | 28 | func JSONResponseObject() -> NSDictionary? 29 | { 30 | let value:Any? = customValues[jsonResponseObjectKey] 31 | return value as? NSDictionary 32 | } 33 | 34 | func setJSONResponseObject(object:NSDictionary) 35 | { 36 | customValues[jsonResponseObjectKey] = object 37 | } 38 | } 39 | 40 | class JSONSerialization:Filter 41 | { 42 | let options:NSJSONWritingOptions 43 | 44 | init (usePrettyPrint:Bool = false) 45 | { 46 | if usePrettyPrint 47 | { 48 | options = NSJSONWritingOptions.PrettyPrinted 49 | } 50 | else 51 | { 52 | options = nil 53 | } 54 | } 55 | 56 | override func processRequest(connection: Connection) 57 | { 58 | let contentType:String? = connection.request!.value(forHeaderKey: HeaderKey.ContentType) 59 | let contentTypeIsJSON = contentType && contentType!.rangeOfString(MimeType.APPLICATION_JSON.string) 60 | let httpMethod:String = connection.request!.httpMethod 61 | 62 | if httpMethod != HTTPMethod.POST.toRaw() 63 | && httpMethod != HTTPMethod.PUT.toRaw() 64 | && httpMethod != HTTPMethod.DELETE.toRaw() 65 | { 66 | return 67 | } 68 | 69 | if contentTypeIsJSON 70 | { 71 | let data = connection.request!.data 72 | var error:NSError? 73 | let object:AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &error) 74 | 75 | if error 76 | { 77 | connection.response!.statusCode = StatusCode.BAD_REQUEST 78 | 79 | let errorMessage = [ "message":"Missing or invalid JSON was received.", "debugDescription":error!.debugDescription ] 80 | connection.setJSONResponseObject(errorMessage); 81 | 82 | return 83 | } 84 | 85 | if object 86 | { 87 | connection.setJSONRequestObject(object as NSDictionary) 88 | } 89 | } 90 | } 91 | 92 | override func processResponse(connection: Connection) 93 | { 94 | let responseObject = connection.JSONResponseObject() 95 | let accept:String? = connection.request!.value(forHeaderKey: HeaderKey.Accept) 96 | let acceptJSON = accept && accept!.rangeOfString(MimeType.APPLICATION_JSON.string) 97 | 98 | if acceptJSON || responseObject 99 | { 100 | if responseObject 101 | { 102 | var error:NSError? 103 | let data = NSJSONSerialization.dataWithJSONObject(responseObject, options: options, error: &error) 104 | 105 | if (connection.response!.statusCode.code == StatusCode.NOT_SET.code) 106 | { 107 | connection.response!.statusCode = StatusCode.OK 108 | } 109 | connection.response!.setValue(MimeType.APPLICATION_JSON.string, forHeaderKey: HeaderKey.ContentType) 110 | connection.response!.data.appendData(data) 111 | } 112 | else 113 | { 114 | // Missing response object 115 | connection.response!.statusCode = StatusCode.NOT_IMPLEMENTED 116 | 117 | let errorMessage = [ "message":"Server did not return a response." ] 118 | connection.setJSONResponseObject(errorMessage); 119 | 120 | processResponse(connection) 121 | } 122 | } 123 | } 124 | } -------------------------------------------------------------------------------- /SwiftServe/SwiftServe/Logging.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Logging.swift 3 | // SwiftServe 4 | // 5 | // Created by Anthony Picciano on 7/18/14. 6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class Logging:Filter 12 | { 13 | let df = NSDateFormatter() 14 | 15 | var dateFormatString:String 16 | 17 | init(dateFormatString:String = "MMM/dd/yyyy hh:mm:ss ZZZZ") 18 | { 19 | self.dateFormatString = dateFormatString 20 | } 21 | 22 | override func processResponse(connection: Connection) 23 | { 24 | println(logEntry(connection)) 25 | } 26 | 27 | func logEntry(connection: Connection) -> String 28 | { 29 | let host:String = connection.socket.connectedHost 30 | let user:String = "-" //TODO: Get Remote-User 31 | let date:String = dateAsFormattedString() 32 | let requestDescription:String = connection.request!.description 33 | let responseDescription:String = connection.response!.description 34 | 35 | let log = "\(host) \(user) [\(date)] \"\(requestDescription)\" \(responseDescription)" 36 | return log 37 | } 38 | 39 | func dateAsFormattedString() -> String 40 | { 41 | df.dateFormat = dateFormatString 42 | let dateString:String = df.stringFromDate(NSDate()) 43 | return dateString 44 | } 45 | } -------------------------------------------------------------------------------- /SwiftServe/SwiftServe/MimeType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MimeType.swift 3 | // SwiftServe 4 | // 5 | // Created by Anthony Picciano on 7/23/14. 6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct MimeType 12 | { 13 | let string:String 14 | 15 | static let APPLICATION_JSON = MimeType(string:"application/json") 16 | } -------------------------------------------------------------------------------- /SwiftServe/SwiftServe/NSData+BaristaExtensions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSData+BaristaExtensions.h 3 | // Barista 4 | // 5 | // Created by Steve Streza on 4/28/13. 6 | // Copyright (c)barista_2013 Mustacheware. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSData (BaristaExtensions) 12 | 13 | // ZLIB 14 | - (NSData *)barista_zlibInflate; 15 | - (NSData *)barista_zlibDeflate; 16 | 17 | // GZIP 18 | - (NSData *)barista_gzipInflate; 19 | - (NSData *)barista_gzipDeflate; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /SwiftServe/SwiftServe/NSData+BaristaExtensions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSData+BaristaExtensions.m 3 | // Barista 4 | // 5 | // Created by Steve Streza on 4/28/13. 6 | // Copyright (c) 2013: Steve Streza 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 9 | // this software and associated documentation files (the "Software"), to deal in 10 | // the Software without restriction, including without limitation the rights to 11 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12 | // of the Software, and to permit persons to whom the Software is furnished to do 13 | // so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | #import "NSData+BaristaExtensions.h" 28 | #include 29 | 30 | @implementation NSData (BaristaExtensions) 31 | 32 | - (NSData *)barista_zlibInflate 33 | { 34 | if ([self length] == 0) return self; 35 | 36 | unsigned full_length = (uInt)[self length]; 37 | unsigned half_length = (uInt)[self length] / 2; 38 | 39 | NSMutableData *decompressed = [NSMutableData dataWithLength: full_length + half_length]; 40 | BOOL done = NO; 41 | int status; 42 | 43 | z_stream strm; 44 | strm.next_in = (Bytef *)[self bytes]; 45 | strm.avail_in = (uInt)[self length]; 46 | strm.total_out = 0; 47 | strm.zalloc = Z_NULL; 48 | strm.zfree = Z_NULL; 49 | 50 | if (inflateInit (&strm) != Z_OK) return nil; 51 | 52 | while (!done) 53 | { 54 | // Make sure we have enough room and reset the lengths. 55 | if (strm.total_out >= [decompressed length]) 56 | [decompressed increaseLengthBy: half_length]; 57 | strm.next_out = [decompressed mutableBytes] + strm.total_out; 58 | strm.avail_out = [decompressed length] - strm.total_out; 59 | 60 | // Inflate another chunk. 61 | status = inflate (&strm, Z_SYNC_FLUSH); 62 | if (status == Z_STREAM_END) done = YES; 63 | else if (status != Z_OK) break; 64 | } 65 | if (inflateEnd (&strm) != Z_OK) return nil; 66 | 67 | // Set real length. 68 | if (done) 69 | { 70 | [decompressed setLength: strm.total_out]; 71 | return [NSData dataWithData: decompressed]; 72 | } 73 | else return nil; 74 | } 75 | 76 | - (NSData *)barista_zlibDeflate 77 | { 78 | if ([self length] == 0) return self; 79 | 80 | z_stream strm; 81 | 82 | strm.zalloc = Z_NULL; 83 | strm.zfree = Z_NULL; 84 | strm.opaque = Z_NULL; 85 | strm.total_out = 0; 86 | strm.next_in=(Bytef *)[self bytes]; 87 | strm.avail_in = [self length]; 88 | 89 | // Compresssion Levels: 90 | // Z_NO_COMPRESSION 91 | // Z_BEST_SPEED 92 | // Z_BEST_COMPRESSION 93 | // Z_DEFAULT_COMPRESSION 94 | 95 | if (deflateInit(&strm, Z_DEFAULT_COMPRESSION) != Z_OK) return nil; 96 | 97 | NSMutableData *compressed = [NSMutableData dataWithLength:16 * 1024]; // 16K chuncks for expansion 98 | 99 | do { 100 | 101 | if (strm.total_out >= [compressed length]) 102 | [compressed increaseLengthBy:16 * 1024]; 103 | 104 | strm.next_out = [compressed mutableBytes] + strm.total_out; 105 | strm.avail_out = [compressed length] - strm.total_out; 106 | 107 | deflate(&strm, Z_FINISH); 108 | 109 | } while (strm.avail_out == 0); 110 | 111 | deflateEnd(&strm); 112 | 113 | [compressed setLength: strm.total_out]; 114 | return [NSData dataWithData: compressed]; 115 | } 116 | 117 | - (NSData *)barista_gzipInflate 118 | { 119 | if ([self length] == 0) return self; 120 | 121 | unsigned full_length = [self length]; 122 | unsigned half_length = [self length] / 2; 123 | 124 | NSMutableData *decompressed = [NSMutableData dataWithLength: full_length + half_length]; 125 | BOOL done = NO; 126 | int status; 127 | 128 | z_stream strm; 129 | strm.next_in = (Bytef *)[self bytes]; 130 | strm.avail_in = [self length]; 131 | strm.total_out = 0; 132 | strm.zalloc = Z_NULL; 133 | strm.zfree = Z_NULL; 134 | 135 | if (inflateInit2(&strm, (15+32)) != Z_OK) return nil; 136 | while (!done) 137 | { 138 | // Make sure we have enough room and reset the lengths. 139 | if (strm.total_out >= [decompressed length]) 140 | [decompressed increaseLengthBy: half_length]; 141 | strm.next_out = [decompressed mutableBytes] + strm.total_out; 142 | strm.avail_out = [decompressed length] - strm.total_out; 143 | 144 | // Inflate another chunk. 145 | status = inflate (&strm, Z_SYNC_FLUSH); 146 | if (status == Z_STREAM_END) done = YES; 147 | else if (status != Z_OK) break; 148 | } 149 | if (inflateEnd (&strm) != Z_OK) return nil; 150 | 151 | // Set real length. 152 | if (done) 153 | { 154 | [decompressed setLength: strm.total_out]; 155 | return [NSData dataWithData: decompressed]; 156 | } 157 | else return nil; 158 | } 159 | 160 | - (NSData *)barista_gzipDeflate 161 | { 162 | if ([self length] == 0) return self; 163 | 164 | z_stream strm; 165 | 166 | strm.zalloc = Z_NULL; 167 | strm.zfree = Z_NULL; 168 | strm.opaque = Z_NULL; 169 | strm.total_out = 0; 170 | strm.next_in=(Bytef *)[self bytes]; 171 | strm.avail_in = (unsigned int)[self length]; 172 | 173 | // Compresssion Levels: 174 | // Z_NO_COMPRESSION 175 | // Z_BEST_SPEED 176 | // Z_BEST_COMPRESSION 177 | // Z_DEFAULT_COMPRESSION 178 | 179 | if (deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (15+16), 8, Z_DEFAULT_STRATEGY) != Z_OK) return nil; 180 | 181 | NSMutableData *compressed = [NSMutableData dataWithLength:16 * 1024]; // 16K chunks for expansion 182 | 183 | do { 184 | 185 | if (strm.total_out >= [compressed length]) 186 | [compressed increaseLengthBy:16 * 1024]; 187 | 188 | strm.next_out = [compressed mutableBytes] + strm.total_out; 189 | strm.avail_out = (unsigned int)([compressed length] - strm.total_out); 190 | 191 | deflate(&strm, Z_FINISH); 192 | 193 | } while (strm.avail_out == 0); 194 | 195 | deflateEnd(&strm); 196 | 197 | [compressed setLength: strm.total_out]; 198 | return [NSData dataWithData:compressed]; 199 | } 200 | 201 | @end 202 | -------------------------------------------------------------------------------- /SwiftServe/SwiftServe/NSDataExtensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSDataExptensions.swift 3 | // SwiftServe 4 | // 5 | // Created by Anthony Picciano on 7/18/14. 6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension NSData 12 | { 13 | func byteDataAsUInt8() -> UInt8[] 14 | { 15 | var array = UInt8[](count:length, repeatedValue: 0) 16 | getBytes(&array) 17 | 18 | return array 19 | } 20 | } -------------------------------------------------------------------------------- /SwiftServe/SwiftServe/Request.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Request.swift 3 | // SwiftServe 4 | // 5 | // Created by Anthony Picciano on 7/18/14. 6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class Request:Printable 12 | { 13 | let message:CFHTTPMessage 14 | var headers = Dictionary() 15 | 16 | init(data:NSData) 17 | { 18 | message = CFHTTPMessageCreateEmpty(nil, 1).takeRetainedValue() 19 | CFHTTPMessageAppendBytes(message, data.byteDataAsUInt8(), data.length) 20 | parseHeaders() 21 | } 22 | 23 | func parseHeaders() 24 | { 25 | let headersNSDictionary:NSDictionary = CFHTTPMessageCopyAllHeaderFields(message).takeRetainedValue() 26 | 27 | for (key : AnyObject, value : AnyObject) in headersNSDictionary 28 | { 29 | headers[key as String] = value as? String 30 | // println("\(key): \(value)") 31 | } 32 | } 33 | 34 | func appendRequestData(data:NSData) 35 | { 36 | CFHTTPMessageAppendBytes(message, data.byteDataAsUInt8(), data.length) 37 | } 38 | 39 | var httpMethod:String 40 | { 41 | return CFHTTPMessageCopyRequestMethod(message).takeRetainedValue() 42 | } 43 | 44 | var url:NSURL 45 | { 46 | return CFHTTPMessageCopyRequestURL(message).takeRetainedValue() 47 | } 48 | 49 | var version:String 50 | { 51 | return CFHTTPMessageCopyVersion(message).takeRetainedValue() 52 | } 53 | 54 | var data:NSData 55 | { 56 | return CFHTTPMessageCopyBody(message).takeRetainedValue() 57 | } 58 | 59 | func value(forHeaderKey key:HeaderKey) -> String? 60 | { 61 | return headers[key.toRaw()]; 62 | } 63 | 64 | var description:String 65 | { 66 | let result = httpMethod + " " + url.path + " " + version 67 | return result 68 | } 69 | } -------------------------------------------------------------------------------- /SwiftServe/SwiftServe/Response.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Response.swift 3 | // SwiftServe 4 | // 5 | // Created by Anthony Picciano on 7/18/14. 6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class Response:Printable 12 | { 13 | let data:NSMutableData 14 | var headers:Dictionary 15 | var statusCode:StatusCode; 16 | 17 | init(statusCode:StatusCode = StatusCode.NOT_SET) 18 | { 19 | data = NSMutableData() 20 | 21 | self.statusCode = statusCode 22 | 23 | headers = Dictionary() 24 | setValue("close", forHeaderKey: HeaderKey.Content) 25 | } 26 | 27 | var message:CFHTTPMessageRef 28 | { 29 | // Create the response object reference 30 | let message = CFHTTPMessageCreateResponse(nil, statusCode.code, "", kCFHTTPVersion1_1).takeRetainedValue() 31 | 32 | // Set the content length 33 | CFHTTPMessageSetHeaderFieldValue(message, HeaderKey.ContentLength.toRaw() as NSString, "\(data.length)" as NSString); 34 | 35 | // Add the headers 36 | for (key, value) in headers 37 | { 38 | CFHTTPMessageSetHeaderFieldValue(message, key as NSString, value as NSString); 39 | } 40 | 41 | // Add the message body 42 | CFHTTPMessageSetBody(message, data) 43 | 44 | return message 45 | } 46 | 47 | var messageData:NSData 48 | { 49 | return CFHTTPMessageCopySerializedMessage(message).takeRetainedValue() 50 | } 51 | 52 | func setValue(value:String?, forHeaderKey key:HeaderKey) 53 | { 54 | headers[key.toRaw()] = value; 55 | } 56 | 57 | func value(forHeaderKey key:HeaderKey) -> String? 58 | { 59 | return headers[key.toRaw()]; 60 | } 61 | 62 | var description:String 63 | { 64 | return "\(statusCode.code) \(data.length)" 65 | } 66 | } -------------------------------------------------------------------------------- /SwiftServe/SwiftServe/Router.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Router.swift 3 | // SwiftServe 4 | // 5 | // Created by Anthony Picciano on 7/23/14. 6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class Router:Filter 12 | { 13 | override func processFilter(connection:Connection) 14 | { 15 | var handled = false 16 | 17 | if !includePaths || includePaths!.canRouteURL(connection.request!.url) 18 | { 19 | let classname = NSString(UTF8String: object_getClassName(self)) 20 | let httpMethod = connection.request!.httpMethod 21 | let scheme = classname + httpMethod 22 | let parameters = [connectionKey:connection] 23 | 24 | handled = JLRoutes(forScheme: scheme).routeURL(connection.request!.url, withParameters: parameters) 25 | } 26 | 27 | if !handled && nextFilter 28 | { 29 | nextFilter!.processFilter(connection) 30 | } 31 | } 32 | 33 | func processRoutes(connection:Connection, parameters:Dictionary) -> Bool 34 | { 35 | // override this method in subclasses 36 | return true 37 | } 38 | 39 | func handler(parameters:NSDictionary!) -> Bool 40 | { 41 | if let connection = parameters[connectionKey] as? Connection 42 | { 43 | return processRoutes(connection, parameters: parameters) 44 | } 45 | 46 | return false 47 | } 48 | 49 | func addEndpoint(endpoint:String, httpMethod:HTTPMethod) 50 | { 51 | let classname = NSString(UTF8String: object_getClassName(self)) 52 | let scheme = classname + httpMethod.toRaw() 53 | JLRoutes(forScheme: scheme).addRoute(endpoint, handler: handler) 54 | } 55 | } -------------------------------------------------------------------------------- /SwiftServe/SwiftServe/Server.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Server.swift 3 | // SwiftServe 4 | // 5 | // Created by Anthony Picciano on 7/17/14. 6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class Server: GCDAsyncSocketDelegate 12 | { 13 | let filterChain = FilterChain() 14 | let boundHost:String? 15 | let port:UInt16 16 | @lazy var socket:GCDAsyncSocket = GCDAsyncSocket(delegate: self, delegateQueue: dispatch_get_main_queue()) 17 | var isListening = false 18 | 19 | init(boundHost: String?, port: UInt16) 20 | { 21 | if boundHost { 22 | self.boundHost = boundHost! 23 | } 24 | self.port = port 25 | 26 | if let constBoundHost = self.boundHost 27 | { 28 | println("Server created with host: \(constBoundHost) and port: \(self.port).") 29 | } 30 | else 31 | { 32 | println("Server created with host: * and port: \(self.port).") 33 | } 34 | } 35 | 36 | convenience init(port:UInt16) 37 | { 38 | self.init(boundHost: nil, port: port) 39 | } 40 | 41 | func startServer() -> Bool 42 | { 43 | if isListening 44 | { 45 | return false 46 | } 47 | 48 | var error:NSError? 49 | if !socket.acceptOnInterface(boundHost, port: port, error: &error) 50 | { 51 | println("Couldn't start socket: \(error)") 52 | } 53 | else 54 | { 55 | println("Listening on \(port).") 56 | isListening = true 57 | } 58 | 59 | return isListening 60 | } 61 | 62 | func stopServer() -> Bool 63 | { 64 | if isListening 65 | { 66 | socket.disconnect() 67 | isListening = false 68 | return true 69 | } 70 | return false 71 | } 72 | 73 | func runForever() 74 | { 75 | while true 76 | { 77 | NSRunLoop.mainRunLoop().run() 78 | } 79 | } 80 | 81 | func socket(socket:GCDAsyncSocket!, didAcceptNewSocket newSocket:GCDAsyncSocket!) 82 | { 83 | Connection(socket: newSocket, filterChain:filterChain) 84 | } 85 | 86 | } -------------------------------------------------------------------------------- /SwiftServe/SwiftServe/SimpleEndpoint.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SimpleEndpoint.swift 3 | // SwiftServe 4 | // 5 | // Created by Anthony Picciano on 7/22/14. 6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class SimpleEndpoint:Router 12 | { 13 | override func processRoutes(connection:Connection, parameters:Dictionary) -> Bool 14 | { 15 | 16 | if let id = parameters["id"] as? NSNumber 17 | { 18 | connection.response!.statusCode = StatusCode.OK 19 | 20 | let response = ["name":"Anthony", "answer":id, "keys":["asdf", "qwerty", "dvorak"] ] 21 | connection.setJSONResponseObject(response) 22 | } 23 | else 24 | { 25 | connection.response!.statusCode = StatusCode.BAD_REQUEST 26 | 27 | let response = ["message":"The id must be specified as numeric."] 28 | connection.setJSONResponseObject(response) 29 | } 30 | 31 | return true 32 | } 33 | } -------------------------------------------------------------------------------- /SwiftServe/SwiftServe/StatusCode.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StatusCode.swift 3 | // SwiftServe 4 | // 5 | // Created by Anthony Picciano on 7/18/14. 6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct StatusCode 12 | { 13 | let code:Int 14 | let description:String 15 | 16 | static let CONTINUE = StatusCode(code:100, description:"Continue") 17 | static let SWITCHING_PROTOCOLS = StatusCode(code: 101, description:"Switching Protocols") 18 | static let OK = StatusCode(code: 200, description:"Okay") 19 | static let CREATED = StatusCode(code: 201, description:"Created") 20 | static let ACCEPTED = StatusCode(code: 202, description:"Accepted") 21 | static let NON_AUTHORITIVE_INFORMATION = StatusCode(code: 203, description:"Non-authoritive Information") 22 | static let NO_CONTENT = StatusCode(code: 204, description:"No Content") 23 | static let RESET_CONTENT = StatusCode(code: 205, description:"Reset Content") 24 | static let PARTIAL_CONTENT = StatusCode(code: 206, description:"Partial Content") 25 | static let MULTIPLE_CHOICES = StatusCode(code: 300, description:"Multiple Choices") 26 | static let MOVED_PERMANENTLY = StatusCode(code: 301, description:"Moved Permanently") 27 | static let FOUND = StatusCode(code: 302, description:"Found") 28 | static let SEE_OTHER = StatusCode(code: 303, description:"See Other") 29 | static let NOT_MODIFIED = StatusCode(code: 304, description:"Not Modified") 30 | static let USE_PROXY = StatusCode(code: 305, description:"Use Proxy") 31 | static let TEMPORARY_REDIRECT = StatusCode(code: 307, description:"Temporary Redirect") 32 | static let BAD_REQUEST = StatusCode(code: 400, description:"Bad Request") 33 | static let UNAUTHORIZED = StatusCode(code: 401, description:"Unauthorized") 34 | static let PAYMENT_REQUIRED = StatusCode(code: 402, description:"Payment Required") 35 | static let FORBIDDEN = StatusCode(code: 403, description:"Forbidden") 36 | static let NOT_FOUND = StatusCode(code: 404, description:"Not Found") 37 | static let METHOD_NOT_ALLOWED = StatusCode(code: 405, description:"Method Not Allowed") 38 | static let NOT_ACCEPTABLE = StatusCode(code: 406, description:"Not Acceptable") 39 | static let PROXY_AUTHENTICATION_REQUIRED = StatusCode(code: 407, description:"Proxy Authentication Required") 40 | static let REQUEST_TIMEOUT = StatusCode(code: 408, description:"Request Timeout") 41 | static let CONFLICT = StatusCode(code: 409, description:"Conflict") 42 | static let GONE = StatusCode(code: 410, description:"Gone") 43 | static let LENGTH_REQUIRED = StatusCode(code: 411, description:"Length Required") 44 | static let PRECONDITION_FAILED = StatusCode(code: 412, description:"Precondition Failed") 45 | static let REQUEST_ENTITY_TOO_LARGE = StatusCode(code: 413, description:"Request Entity Too Large") 46 | static let REQUEST_URI_TOO_LONG = StatusCode(code: 414, description:"Request URI Too Long") 47 | static let UNSUPPORTED_MEDIA_TYPE = StatusCode(code: 415, description:"Unsupported Media Type") 48 | static let REQUESTED_RANGE_NOT_SATISFIABLE = StatusCode(code: 416, description:"Requested Range Not Satisfiable") 49 | static let EXPECTATION_FAILED = StatusCode(code: 417, description:"Expectation Failed") 50 | static let SERVER_ERROR = StatusCode(code: 500, description:"Server Error") 51 | static let NOT_IMPLEMENTED = StatusCode(code: 501, description:"Not Implemented") 52 | static let BAD_GATEWAY = StatusCode(code: 502, description:"Bad Gateway") 53 | static let SERVICE_UNAVAILABLE = StatusCode(code: 503, description:"Service Unavailable") 54 | static let GATEWAY_TIMEOUT = StatusCode(code: 504, description:"Gateway Timeout") 55 | static let HTTP_VERSION_NOT_SUPPORTED = StatusCode(code: 505, description:"HTTP Version Not Supported") 56 | 57 | static let NOT_SET = StatusCode(code: 0, description:"Status Not Set") 58 | } 59 | -------------------------------------------------------------------------------- /SwiftServe/SwiftServe/SwiftServe-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftServe-Bridging-Header.h 3 | // SwiftServe 4 | // 5 | // Created by Anthony Picciano on 7/19/14. 6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved. 7 | // 8 | 9 | -------------------------------------------------------------------------------- /SwiftServe/SwiftServe/SwiftServe.h: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftServe.h 3 | // SwiftServe 4 | // 5 | // Created by Anthony Picciano on 7/17/14. 6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | #import 13 | 14 | //! Project version number for SwiftServe. 15 | FOUNDATION_EXPORT double SwiftServeVersionNumber; 16 | 17 | //! Project version string for SwiftServe. 18 | FOUNDATION_EXPORT const unsigned char SwiftServeVersionString[]; 19 | 20 | // In this header, you should import all the public headers of your framework using statements like #import 21 | 22 | -------------------------------------------------------------------------------- /SwiftServe/SwiftServeTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.picciano.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /SwiftServe/SwiftServeTests/SwiftServeTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftServeTests.swift 3 | // SwiftServeTests 4 | // 5 | // Created by Anthony Picciano on 7/17/14. 6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class SwiftServeTests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | // Put setup code here. This method is called before the invocation of each test method in the class. 16 | } 17 | 18 | override func tearDown() { 19 | // Put teardown code here. This method is called after the invocation of each test method in the class. 20 | super.tearDown() 21 | } 22 | 23 | func testExample() { 24 | // This is an example of a functional test case. 25 | XCTAssert(true, "Pass") 26 | } 27 | 28 | func testPerformanceExample() { 29 | // This is an example of a performance test case. 30 | self.measureBlock() { 31 | // Put the code you want to measure the time of here. 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /SwiftServeDemo/SwiftServeDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | E4C8CCBC197886E1000B8FAE /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8CCBB197886E1000B8FAE /* main.swift */; }; 11 | E4C8CCBE197886E1000B8FAE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8CCBD197886E1000B8FAE /* AppDelegate.swift */; }; 12 | E4C8CCC1197886E1000B8FAE /* SwiftServeDemo.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = E4C8CCBF197886E1000B8FAE /* SwiftServeDemo.xcdatamodeld */; }; 13 | E4C8CCC3197886E1000B8FAE /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E4C8CCC2197886E1000B8FAE /* Images.xcassets */; }; 14 | E4C8CCC6197886E1000B8FAE /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = E4C8CCC4197886E1000B8FAE /* MainMenu.xib */; }; 15 | E4C8CCD2197886E1000B8FAE /* SwiftServeDemoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8CCD1197886E1000B8FAE /* SwiftServeDemoTests.swift */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXContainerItemProxy section */ 19 | E4C8CCCC197886E1000B8FAE /* PBXContainerItemProxy */ = { 20 | isa = PBXContainerItemProxy; 21 | containerPortal = E4C8CCAE197886E1000B8FAE /* Project object */; 22 | proxyType = 1; 23 | remoteGlobalIDString = E4C8CCB5197886E1000B8FAE; 24 | remoteInfo = SwiftServeDemo; 25 | }; 26 | /* End PBXContainerItemProxy section */ 27 | 28 | /* Begin PBXFileReference section */ 29 | E4C8CCB6197886E1000B8FAE /* SwiftServeDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftServeDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | E4C8CCBA197886E1000B8FAE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 31 | E4C8CCBB197886E1000B8FAE /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; 32 | E4C8CCBD197886E1000B8FAE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 33 | E4C8CCC0197886E1000B8FAE /* SwiftServeDemo.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = SwiftServeDemo.xcdatamodel; sourceTree = ""; }; 34 | E4C8CCC2197886E1000B8FAE /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 35 | E4C8CCC5197886E1000B8FAE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 36 | E4C8CCCB197886E1000B8FAE /* SwiftServeDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftServeDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | E4C8CCD0197886E1000B8FAE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 38 | E4C8CCD1197886E1000B8FAE /* SwiftServeDemoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftServeDemoTests.swift; sourceTree = ""; }; 39 | /* End PBXFileReference section */ 40 | 41 | /* Begin PBXFrameworksBuildPhase section */ 42 | E4C8CCB3197886E1000B8FAE /* Frameworks */ = { 43 | isa = PBXFrameworksBuildPhase; 44 | buildActionMask = 2147483647; 45 | files = ( 46 | ); 47 | runOnlyForDeploymentPostprocessing = 0; 48 | }; 49 | E4C8CCC8197886E1000B8FAE /* Frameworks */ = { 50 | isa = PBXFrameworksBuildPhase; 51 | buildActionMask = 2147483647; 52 | files = ( 53 | ); 54 | runOnlyForDeploymentPostprocessing = 0; 55 | }; 56 | /* End PBXFrameworksBuildPhase section */ 57 | 58 | /* Begin PBXGroup section */ 59 | E4C8CCAD197886E1000B8FAE = { 60 | isa = PBXGroup; 61 | children = ( 62 | E4C8CCB8197886E1000B8FAE /* SwiftServeDemo */, 63 | E4C8CCCE197886E1000B8FAE /* SwiftServeDemoTests */, 64 | E4C8CCB7197886E1000B8FAE /* Products */, 65 | ); 66 | sourceTree = ""; 67 | }; 68 | E4C8CCB7197886E1000B8FAE /* Products */ = { 69 | isa = PBXGroup; 70 | children = ( 71 | E4C8CCB6197886E1000B8FAE /* SwiftServeDemo.app */, 72 | E4C8CCCB197886E1000B8FAE /* SwiftServeDemoTests.xctest */, 73 | ); 74 | name = Products; 75 | sourceTree = ""; 76 | }; 77 | E4C8CCB8197886E1000B8FAE /* SwiftServeDemo */ = { 78 | isa = PBXGroup; 79 | children = ( 80 | E4C8CCBD197886E1000B8FAE /* AppDelegate.swift */, 81 | E4C8CCC2197886E1000B8FAE /* Images.xcassets */, 82 | E4C8CCC4197886E1000B8FAE /* MainMenu.xib */, 83 | E4C8CCBF197886E1000B8FAE /* SwiftServeDemo.xcdatamodeld */, 84 | E4C8CCB9197886E1000B8FAE /* Supporting Files */, 85 | ); 86 | path = SwiftServeDemo; 87 | sourceTree = ""; 88 | }; 89 | E4C8CCB9197886E1000B8FAE /* Supporting Files */ = { 90 | isa = PBXGroup; 91 | children = ( 92 | E4C8CCBA197886E1000B8FAE /* Info.plist */, 93 | E4C8CCBB197886E1000B8FAE /* main.swift */, 94 | ); 95 | name = "Supporting Files"; 96 | sourceTree = ""; 97 | }; 98 | E4C8CCCE197886E1000B8FAE /* SwiftServeDemoTests */ = { 99 | isa = PBXGroup; 100 | children = ( 101 | E4C8CCD1197886E1000B8FAE /* SwiftServeDemoTests.swift */, 102 | E4C8CCCF197886E1000B8FAE /* Supporting Files */, 103 | ); 104 | path = SwiftServeDemoTests; 105 | sourceTree = ""; 106 | }; 107 | E4C8CCCF197886E1000B8FAE /* Supporting Files */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | E4C8CCD0197886E1000B8FAE /* Info.plist */, 111 | ); 112 | name = "Supporting Files"; 113 | sourceTree = ""; 114 | }; 115 | /* End PBXGroup section */ 116 | 117 | /* Begin PBXNativeTarget section */ 118 | E4C8CCB5197886E1000B8FAE /* SwiftServeDemo */ = { 119 | isa = PBXNativeTarget; 120 | buildConfigurationList = E4C8CCD5197886E1000B8FAE /* Build configuration list for PBXNativeTarget "SwiftServeDemo" */; 121 | buildPhases = ( 122 | E4C8CCB2197886E1000B8FAE /* Sources */, 123 | E4C8CCB3197886E1000B8FAE /* Frameworks */, 124 | E4C8CCB4197886E1000B8FAE /* Resources */, 125 | ); 126 | buildRules = ( 127 | ); 128 | dependencies = ( 129 | ); 130 | name = SwiftServeDemo; 131 | productName = SwiftServeDemo; 132 | productReference = E4C8CCB6197886E1000B8FAE /* SwiftServeDemo.app */; 133 | productType = "com.apple.product-type.application"; 134 | }; 135 | E4C8CCCA197886E1000B8FAE /* SwiftServeDemoTests */ = { 136 | isa = PBXNativeTarget; 137 | buildConfigurationList = E4C8CCD8197886E1000B8FAE /* Build configuration list for PBXNativeTarget "SwiftServeDemoTests" */; 138 | buildPhases = ( 139 | E4C8CCC7197886E1000B8FAE /* Sources */, 140 | E4C8CCC8197886E1000B8FAE /* Frameworks */, 141 | E4C8CCC9197886E1000B8FAE /* Resources */, 142 | ); 143 | buildRules = ( 144 | ); 145 | dependencies = ( 146 | E4C8CCCD197886E1000B8FAE /* PBXTargetDependency */, 147 | ); 148 | name = SwiftServeDemoTests; 149 | productName = SwiftServeDemoTests; 150 | productReference = E4C8CCCB197886E1000B8FAE /* SwiftServeDemoTests.xctest */; 151 | productType = "com.apple.product-type.bundle.unit-test"; 152 | }; 153 | /* End PBXNativeTarget section */ 154 | 155 | /* Begin PBXProject section */ 156 | E4C8CCAE197886E1000B8FAE /* Project object */ = { 157 | isa = PBXProject; 158 | attributes = { 159 | LastUpgradeCheck = 0600; 160 | ORGANIZATIONNAME = "Anthony Picciano"; 161 | TargetAttributes = { 162 | E4C8CCB5197886E1000B8FAE = { 163 | CreatedOnToolsVersion = 6.0; 164 | }; 165 | E4C8CCCA197886E1000B8FAE = { 166 | CreatedOnToolsVersion = 6.0; 167 | TestTargetID = E4C8CCB5197886E1000B8FAE; 168 | }; 169 | }; 170 | }; 171 | buildConfigurationList = E4C8CCB1197886E1000B8FAE /* Build configuration list for PBXProject "SwiftServeDemo" */; 172 | compatibilityVersion = "Xcode 3.2"; 173 | developmentRegion = English; 174 | hasScannedForEncodings = 0; 175 | knownRegions = ( 176 | en, 177 | Base, 178 | ); 179 | mainGroup = E4C8CCAD197886E1000B8FAE; 180 | productRefGroup = E4C8CCB7197886E1000B8FAE /* Products */; 181 | projectDirPath = ""; 182 | projectRoot = ""; 183 | targets = ( 184 | E4C8CCB5197886E1000B8FAE /* SwiftServeDemo */, 185 | E4C8CCCA197886E1000B8FAE /* SwiftServeDemoTests */, 186 | ); 187 | }; 188 | /* End PBXProject section */ 189 | 190 | /* Begin PBXResourcesBuildPhase section */ 191 | E4C8CCB4197886E1000B8FAE /* Resources */ = { 192 | isa = PBXResourcesBuildPhase; 193 | buildActionMask = 2147483647; 194 | files = ( 195 | E4C8CCC3197886E1000B8FAE /* Images.xcassets in Resources */, 196 | E4C8CCC6197886E1000B8FAE /* MainMenu.xib in Resources */, 197 | ); 198 | runOnlyForDeploymentPostprocessing = 0; 199 | }; 200 | E4C8CCC9197886E1000B8FAE /* Resources */ = { 201 | isa = PBXResourcesBuildPhase; 202 | buildActionMask = 2147483647; 203 | files = ( 204 | ); 205 | runOnlyForDeploymentPostprocessing = 0; 206 | }; 207 | /* End PBXResourcesBuildPhase section */ 208 | 209 | /* Begin PBXSourcesBuildPhase section */ 210 | E4C8CCB2197886E1000B8FAE /* Sources */ = { 211 | isa = PBXSourcesBuildPhase; 212 | buildActionMask = 2147483647; 213 | files = ( 214 | E4C8CCBE197886E1000B8FAE /* AppDelegate.swift in Sources */, 215 | E4C8CCBC197886E1000B8FAE /* main.swift in Sources */, 216 | E4C8CCC1197886E1000B8FAE /* SwiftServeDemo.xcdatamodeld in Sources */, 217 | ); 218 | runOnlyForDeploymentPostprocessing = 0; 219 | }; 220 | E4C8CCC7197886E1000B8FAE /* Sources */ = { 221 | isa = PBXSourcesBuildPhase; 222 | buildActionMask = 2147483647; 223 | files = ( 224 | E4C8CCD2197886E1000B8FAE /* SwiftServeDemoTests.swift in Sources */, 225 | ); 226 | runOnlyForDeploymentPostprocessing = 0; 227 | }; 228 | /* End PBXSourcesBuildPhase section */ 229 | 230 | /* Begin PBXTargetDependency section */ 231 | E4C8CCCD197886E1000B8FAE /* PBXTargetDependency */ = { 232 | isa = PBXTargetDependency; 233 | target = E4C8CCB5197886E1000B8FAE /* SwiftServeDemo */; 234 | targetProxy = E4C8CCCC197886E1000B8FAE /* PBXContainerItemProxy */; 235 | }; 236 | /* End PBXTargetDependency section */ 237 | 238 | /* Begin PBXVariantGroup section */ 239 | E4C8CCC4197886E1000B8FAE /* MainMenu.xib */ = { 240 | isa = PBXVariantGroup; 241 | children = ( 242 | E4C8CCC5197886E1000B8FAE /* Base */, 243 | ); 244 | name = MainMenu.xib; 245 | sourceTree = ""; 246 | }; 247 | /* End PBXVariantGroup section */ 248 | 249 | /* Begin XCBuildConfiguration section */ 250 | E4C8CCD3197886E1000B8FAE /* Debug */ = { 251 | isa = XCBuildConfiguration; 252 | buildSettings = { 253 | ALWAYS_SEARCH_USER_PATHS = NO; 254 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 255 | CLANG_CXX_LIBRARY = "libc++"; 256 | CLANG_ENABLE_MODULES = YES; 257 | CLANG_ENABLE_OBJC_ARC = YES; 258 | CLANG_WARN_BOOL_CONVERSION = YES; 259 | CLANG_WARN_CONSTANT_CONVERSION = YES; 260 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 261 | CLANG_WARN_EMPTY_BODY = YES; 262 | CLANG_WARN_ENUM_CONVERSION = YES; 263 | CLANG_WARN_INT_CONVERSION = YES; 264 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 265 | CLANG_WARN_UNREACHABLE_CODE = YES; 266 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 267 | CODE_SIGN_IDENTITY = "-"; 268 | COPY_PHASE_STRIP = NO; 269 | ENABLE_STRICT_OBJC_MSGSEND = YES; 270 | GCC_C_LANGUAGE_STANDARD = gnu99; 271 | GCC_DYNAMIC_NO_PIC = NO; 272 | GCC_OPTIMIZATION_LEVEL = 0; 273 | GCC_PREPROCESSOR_DEFINITIONS = ( 274 | "DEBUG=1", 275 | "$(inherited)", 276 | ); 277 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 278 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 279 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 280 | GCC_WARN_UNDECLARED_SELECTOR = YES; 281 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 282 | GCC_WARN_UNUSED_FUNCTION = YES; 283 | GCC_WARN_UNUSED_VARIABLE = YES; 284 | MACOSX_DEPLOYMENT_TARGET = 10.9; 285 | METAL_ENABLE_DEBUG_INFO = YES; 286 | ONLY_ACTIVE_ARCH = YES; 287 | SDKROOT = macosx; 288 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 289 | }; 290 | name = Debug; 291 | }; 292 | E4C8CCD4197886E1000B8FAE /* Release */ = { 293 | isa = XCBuildConfiguration; 294 | buildSettings = { 295 | ALWAYS_SEARCH_USER_PATHS = NO; 296 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 297 | CLANG_CXX_LIBRARY = "libc++"; 298 | CLANG_ENABLE_MODULES = YES; 299 | CLANG_ENABLE_OBJC_ARC = YES; 300 | CLANG_WARN_BOOL_CONVERSION = YES; 301 | CLANG_WARN_CONSTANT_CONVERSION = YES; 302 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 303 | CLANG_WARN_EMPTY_BODY = YES; 304 | CLANG_WARN_ENUM_CONVERSION = YES; 305 | CLANG_WARN_INT_CONVERSION = YES; 306 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 307 | CLANG_WARN_UNREACHABLE_CODE = YES; 308 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 309 | CODE_SIGN_IDENTITY = "-"; 310 | COPY_PHASE_STRIP = YES; 311 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 312 | ENABLE_NS_ASSERTIONS = NO; 313 | ENABLE_STRICT_OBJC_MSGSEND = YES; 314 | GCC_C_LANGUAGE_STANDARD = gnu99; 315 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 316 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 317 | GCC_WARN_UNDECLARED_SELECTOR = YES; 318 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 319 | GCC_WARN_UNUSED_FUNCTION = YES; 320 | GCC_WARN_UNUSED_VARIABLE = YES; 321 | MACOSX_DEPLOYMENT_TARGET = 10.9; 322 | METAL_ENABLE_DEBUG_INFO = NO; 323 | SDKROOT = macosx; 324 | }; 325 | name = Release; 326 | }; 327 | E4C8CCD6197886E1000B8FAE /* Debug */ = { 328 | isa = XCBuildConfiguration; 329 | buildSettings = { 330 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 331 | COMBINE_HIDPI_IMAGES = YES; 332 | INFOPLIST_FILE = SwiftServeDemo/Info.plist; 333 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 334 | PRODUCT_NAME = "$(TARGET_NAME)"; 335 | }; 336 | name = Debug; 337 | }; 338 | E4C8CCD7197886E1000B8FAE /* Release */ = { 339 | isa = XCBuildConfiguration; 340 | buildSettings = { 341 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 342 | COMBINE_HIDPI_IMAGES = YES; 343 | INFOPLIST_FILE = SwiftServeDemo/Info.plist; 344 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 345 | PRODUCT_NAME = "$(TARGET_NAME)"; 346 | }; 347 | name = Release; 348 | }; 349 | E4C8CCD9197886E1000B8FAE /* Debug */ = { 350 | isa = XCBuildConfiguration; 351 | buildSettings = { 352 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/SwiftServeDemo.app/Contents/MacOS/SwiftServeDemo"; 353 | COMBINE_HIDPI_IMAGES = YES; 354 | FRAMEWORK_SEARCH_PATHS = ( 355 | "$(DEVELOPER_FRAMEWORKS_DIR)", 356 | "$(inherited)", 357 | ); 358 | GCC_PREPROCESSOR_DEFINITIONS = ( 359 | "DEBUG=1", 360 | "$(inherited)", 361 | ); 362 | INFOPLIST_FILE = SwiftServeDemoTests/Info.plist; 363 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 364 | METAL_ENABLE_DEBUG_INFO = YES; 365 | PRODUCT_NAME = "$(TARGET_NAME)"; 366 | TEST_HOST = "$(BUNDLE_LOADER)"; 367 | }; 368 | name = Debug; 369 | }; 370 | E4C8CCDA197886E1000B8FAE /* Release */ = { 371 | isa = XCBuildConfiguration; 372 | buildSettings = { 373 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/SwiftServeDemo.app/Contents/MacOS/SwiftServeDemo"; 374 | COMBINE_HIDPI_IMAGES = YES; 375 | FRAMEWORK_SEARCH_PATHS = ( 376 | "$(DEVELOPER_FRAMEWORKS_DIR)", 377 | "$(inherited)", 378 | ); 379 | INFOPLIST_FILE = SwiftServeDemoTests/Info.plist; 380 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 381 | METAL_ENABLE_DEBUG_INFO = NO; 382 | PRODUCT_NAME = "$(TARGET_NAME)"; 383 | TEST_HOST = "$(BUNDLE_LOADER)"; 384 | }; 385 | name = Release; 386 | }; 387 | /* End XCBuildConfiguration section */ 388 | 389 | /* Begin XCConfigurationList section */ 390 | E4C8CCB1197886E1000B8FAE /* Build configuration list for PBXProject "SwiftServeDemo" */ = { 391 | isa = XCConfigurationList; 392 | buildConfigurations = ( 393 | E4C8CCD3197886E1000B8FAE /* Debug */, 394 | E4C8CCD4197886E1000B8FAE /* Release */, 395 | ); 396 | defaultConfigurationIsVisible = 0; 397 | defaultConfigurationName = Release; 398 | }; 399 | E4C8CCD5197886E1000B8FAE /* Build configuration list for PBXNativeTarget "SwiftServeDemo" */ = { 400 | isa = XCConfigurationList; 401 | buildConfigurations = ( 402 | E4C8CCD6197886E1000B8FAE /* Debug */, 403 | E4C8CCD7197886E1000B8FAE /* Release */, 404 | ); 405 | defaultConfigurationIsVisible = 0; 406 | }; 407 | E4C8CCD8197886E1000B8FAE /* Build configuration list for PBXNativeTarget "SwiftServeDemoTests" */ = { 408 | isa = XCConfigurationList; 409 | buildConfigurations = ( 410 | E4C8CCD9197886E1000B8FAE /* Debug */, 411 | E4C8CCDA197886E1000B8FAE /* Release */, 412 | ); 413 | defaultConfigurationIsVisible = 0; 414 | }; 415 | /* End XCConfigurationList section */ 416 | 417 | /* Begin XCVersionGroup section */ 418 | E4C8CCBF197886E1000B8FAE /* SwiftServeDemo.xcdatamodeld */ = { 419 | isa = XCVersionGroup; 420 | children = ( 421 | E4C8CCC0197886E1000B8FAE /* SwiftServeDemo.xcdatamodel */, 422 | ); 423 | currentVersion = E4C8CCC0197886E1000B8FAE /* SwiftServeDemo.xcdatamodel */; 424 | path = SwiftServeDemo.xcdatamodeld; 425 | sourceTree = ""; 426 | versionGroupType = wrapper.xcdatamodel; 427 | }; 428 | /* End XCVersionGroup section */ 429 | }; 430 | rootObject = E4C8CCAE197886E1000B8FAE /* Project object */; 431 | } 432 | -------------------------------------------------------------------------------- /SwiftServeDemo/SwiftServeDemo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SwiftServeDemo 4 | // 5 | // Created by Anthony Picciano on 7/17/14. 6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import SwiftServe 11 | 12 | class AppDelegate: NSObject, NSApplicationDelegate { 13 | 14 | @IBOutlet var window: NSWindow 15 | @lazy var server:Server = Server(boundHost: nil, port: 3333) 16 | 17 | func applicationDidFinishLaunching(aNotification: NSNotification?) 18 | { 19 | server.filterChain.add(Logging()) 20 | server.filterChain.add(GZipCompressor()) 21 | server.filterChain.add(ErrorPage()) 22 | server.filterChain.add(JSONSerialization(usePrettyPrint: true)) 23 | 24 | var simpleEndpoint = SimpleEndpoint() 25 | simpleEndpoint.includePath("/api/*") 26 | simpleEndpoint.addEndpoint("/api/complex/:id", httpMethod: HTTPMethod.GET) 27 | server.filterChain.add(simpleEndpoint) 28 | 29 | startServer(self) 30 | } 31 | 32 | @IBAction func startServer(sender: AnyObject) 33 | { 34 | let success = server.startServer() 35 | } 36 | 37 | func applicationWillTerminate(aNotification: NSNotification?) 38 | { 39 | 40 | } 41 | 42 | @IBAction func saveAction(sender: AnyObject) { 43 | // Performs the save action for the application, which is to send the save: message to the application's managed object context. Any encountered errors are presented to the user. 44 | var error: NSError? = nil 45 | 46 | if let moc = self.managedObjectContext { 47 | if !moc.commitEditing() { 48 | println("\(NSStringFromClass(self.dynamicType)) unable to commit editing before saving") 49 | } 50 | if !moc.save(&error) { 51 | NSApplication.sharedApplication().presentError(error) 52 | } 53 | } 54 | } 55 | 56 | var applicationFilesDirectory: NSURL { 57 | // Returns the directory the application uses to store the Core Data store file. This code uses a directory named "com.picciano.SwiftServeDemo" in the user's Application Support directory. 58 | let fileManager = NSFileManager.defaultManager() 59 | let urls = fileManager.URLsForDirectory(.ApplicationSupportDirectory, inDomains: .UserDomainMask) 60 | let appSupportURL: AnyObject = urls[urls.endIndex - 1] 61 | return appSupportURL.URLByAppendingPathComponent("com.picciano.SwiftServeDemo") 62 | } 63 | 64 | var managedObjectModel: NSManagedObjectModel { 65 | // Creates if necessary and returns the managed object model for the application. 66 | if let mom = _managedObjectModel { 67 | return mom 68 | } 69 | 70 | let modelURL = NSBundle.mainBundle().URLForResource("SwiftServeDemo", withExtension: "momd") 71 | _managedObjectModel = NSManagedObjectModel(contentsOfURL: modelURL) 72 | return _managedObjectModel! 73 | } 74 | var _managedObjectModel: NSManagedObjectModel? = nil 75 | 76 | var persistentStoreCoordinator: NSPersistentStoreCoordinator? { 77 | // Returns the persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it. (The directory for the store is created, if necessary.) 78 | if let psc = _persistentStoreCoordinator { 79 | return psc 80 | } 81 | 82 | let mom = self.managedObjectModel 83 | 84 | let fileManager = NSFileManager.defaultManager() 85 | let applicationFilesDirectory = self.applicationFilesDirectory 86 | var error: NSError? = nil 87 | 88 | let optProperties: NSDictionary? = applicationFilesDirectory.resourceValuesForKeys([NSURLIsDirectoryKey], error: &error) 89 | 90 | if let properties = optProperties { 91 | if !properties[NSURLIsDirectoryKey].boolValue { 92 | // Customize and localize this error. 93 | let failureDescription = "Expected a folder to store application data, found a file \(applicationFilesDirectory.path)." 94 | let dict = NSMutableDictionary() 95 | dict[NSLocalizedDescriptionKey] = failureDescription 96 | error = NSError.errorWithDomain("YOUR_ERROR_DOMAIN", code: 101, userInfo: dict) 97 | 98 | NSApplication.sharedApplication().presentError(error) 99 | return nil 100 | } 101 | } else { 102 | var ok = false 103 | if error!.code == NSFileReadNoSuchFileError { 104 | ok = fileManager.createDirectoryAtPath(applicationFilesDirectory.path, withIntermediateDirectories: true, attributes: nil, error: &error) 105 | } 106 | if !ok { 107 | NSApplication.sharedApplication().presentError(error) 108 | return nil 109 | } 110 | } 111 | 112 | let url = applicationFilesDirectory.URLByAppendingPathComponent("SwiftServeDemo.storedata") 113 | var coordinator = NSPersistentStoreCoordinator(managedObjectModel: mom) 114 | if coordinator.addPersistentStoreWithType(NSXMLStoreType, configuration: nil, URL: url, options: nil, error: &error) == nil { 115 | NSApplication.sharedApplication().presentError(error) 116 | return nil 117 | } 118 | _persistentStoreCoordinator = coordinator 119 | 120 | return _persistentStoreCoordinator 121 | } 122 | var _persistentStoreCoordinator: NSPersistentStoreCoordinator? = nil 123 | 124 | var managedObjectContext: NSManagedObjectContext? { 125 | // Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) 126 | if let moc = _managedObjectContext { 127 | return moc 128 | } 129 | 130 | let coordinator = self.persistentStoreCoordinator 131 | if !coordinator { 132 | var dict = NSMutableDictionary() 133 | dict[NSLocalizedDescriptionKey] = "Failed to initialize the store" 134 | dict[NSLocalizedFailureReasonErrorKey] = "There was an error building up the data file." 135 | let error = NSError.errorWithDomain("YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict) 136 | NSApplication.sharedApplication().presentError(error) 137 | return nil 138 | } 139 | _managedObjectContext = NSManagedObjectContext() 140 | _managedObjectContext!.persistentStoreCoordinator = coordinator! 141 | 142 | return _managedObjectContext 143 | } 144 | var _managedObjectContext: NSManagedObjectContext? = nil 145 | 146 | func windowWillReturnUndoManager(window: NSWindow?) -> NSUndoManager? { 147 | // Returns the NSUndoManager for the application. In this case, the manager returned is that of the managed object context for the application. 148 | if let moc = self.managedObjectContext { 149 | return moc.undoManager 150 | } else { 151 | return nil 152 | } 153 | } 154 | 155 | func applicationShouldTerminate(sender: NSApplication) -> NSApplicationTerminateReply { 156 | // Save changes in the application's managed object context before the application terminates. 157 | 158 | if !_managedObjectContext { 159 | // Accesses the underlying stored property because we don't want to cause the lazy initialization 160 | return .TerminateNow 161 | } 162 | let moc = self.managedObjectContext! 163 | if !moc.commitEditing() { 164 | println("\(NSStringFromClass(self.dynamicType)) unable to commit editing to terminate") 165 | return .TerminateCancel 166 | } 167 | 168 | if !moc.hasChanges { 169 | return .TerminateNow 170 | } 171 | 172 | var error: NSError? = nil 173 | if !moc.save(&error) { 174 | // Customize this code block to include application-specific recovery steps. 175 | let result = sender.presentError(error) 176 | if (result) { 177 | return .TerminateCancel 178 | } 179 | 180 | let question = "Could not save changes while quitting. Quit anyway?" // NSLocalizedString(@"Could not save changes while quitting. Quit anyway?", @"Quit without saves error question message") 181 | let info = "Quitting now will lose any changes you have made since the last successful save" // NSLocalizedString(@"Quitting now will lose any changes you have made since the last successful save", @"Quit without saves error question info"); 182 | let quitButton = "Quit anyway" // NSLocalizedString(@"Quit anyway", @"Quit anyway button title") 183 | let cancelButton = "Cancel" // NSLocalizedString(@"Cancel", @"Cancel button title") 184 | let alert = NSAlert() 185 | alert.messageText = question 186 | alert.informativeText = info 187 | alert.addButtonWithTitle(quitButton) 188 | alert.addButtonWithTitle(cancelButton) 189 | 190 | let answer = alert.runModal() 191 | if answer == NSAlertFirstButtonReturn { 192 | return .TerminateCancel 193 | } 194 | } 195 | 196 | return .TerminateNow 197 | } 198 | 199 | } 200 | 201 | -------------------------------------------------------------------------------- /SwiftServeDemo/SwiftServeDemo/Base.lproj/MainMenu.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | Default 539 | 540 | 541 | 542 | 543 | 544 | 545 | Left to Right 546 | 547 | 548 | 549 | 550 | 551 | 552 | Right to Left 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | Default 564 | 565 | 566 | 567 | 568 | 569 | 570 | Left to Right 571 | 572 | 573 | 574 | 575 | 576 | 577 | Right to Left 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | -------------------------------------------------------------------------------- /SwiftServeDemo/SwiftServeDemo/Images.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 | } -------------------------------------------------------------------------------- /SwiftServeDemo/SwiftServeDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | com.picciano.${PRODUCT_NAME:rfc1034identifier} 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 © 2014 Anthony Picciano. All rights reserved. 29 | NSMainNibFile 30 | MainMenu 31 | NSPrincipalClass 32 | NSApplication 33 | 34 | 35 | -------------------------------------------------------------------------------- /SwiftServeDemo/SwiftServeDemo/SwiftServeDemo.xcdatamodeld/.xccurrentversion: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | _XCCurrentVersionName 6 | SwiftServeDemo.xcdatamodel 7 | 8 | 9 | -------------------------------------------------------------------------------- /SwiftServeDemo/SwiftServeDemo/SwiftServeDemo.xcdatamodeld/SwiftServeDemo.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /SwiftServeDemo/SwiftServeDemo/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // SwiftServeDemo 4 | // 5 | // Created by Anthony Picciano on 7/17/14. 6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | NSApplicationMain(C_ARGC, C_ARGV) 12 | -------------------------------------------------------------------------------- /SwiftServeDemo/SwiftServeDemoTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.picciano.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /SwiftServeDemo/SwiftServeDemoTests/SwiftServeDemoTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftServeDemoTests.swift 3 | // SwiftServeDemoTests 4 | // 5 | // Created by Anthony Picciano on 7/17/14. 6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class SwiftServeDemoTests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | // Put setup code here. This method is called before the invocation of each test method in the class. 16 | } 17 | 18 | override func tearDown() { 19 | // Put teardown code here. This method is called after the invocation of each test method in the class. 20 | super.tearDown() 21 | } 22 | 23 | func testExample() { 24 | // This is an example of a functional test case. 25 | XCTAssert(true, "Pass") 26 | } 27 | 28 | func testPerformanceExample() { 29 | // This is an example of a performance test case. 30 | self.measureBlock() { 31 | // Put the code you want to measure the time of here. 32 | } 33 | } 34 | 35 | } 36 | --------------------------------------------------------------------------------