main.swift 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. /*
  2. * Copyright IBM Corporation 2018
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. import Foundation
  17. import Kitura
  18. import LoggerAPI
  19. import HeliumLogger
  20. import KituraStencil
  21. import Stencil
  22. import TechEmpowerCommon
  23. import KueryPostgresRaw
  24. Log.logger = HeliumLogger(.info)
  25. // Stencil stuff
  26. let ext = Extension()
  27. // Stencil does not yet support automatic HTML escaping:
  28. // https://github.com/kylef/Stencil/pull/80
  29. //
  30. ext.registerFilter("htmlencode") { (value: Any?) in
  31. if let value = value as? String {
  32. return value
  33. .replacingOccurrences(of: "&", with: "&")
  34. .replacingOccurrences(of: "<", with: "&lt;")
  35. .replacingOccurrences(of: ">", with: "&gt;")
  36. .replacingOccurrences(of: "'", with: "&apos;")
  37. .replacingOccurrences(of: "\"", with: "&quot;")
  38. }
  39. return value
  40. }
  41. let router = Router()
  42. router.add(templateEngine: StencilTemplateEngine(extension: ext))
  43. //
  44. // TechEmpower test 2: Single database query (raw, no ORM)
  45. //
  46. router.get("/db") {
  47. request, response, next in
  48. response.headers["Server"] = "Kitura"
  49. getRandomRow_Raw { (row, err) in
  50. guard let row = row else {
  51. guard let err = err else {
  52. Log.error("Unknown Error")
  53. try? response.status(.badRequest).send("Unknown error").end()
  54. return
  55. }
  56. Log.error("\(err)")
  57. try? response.status(.badRequest).send("Error: \(err)").end()
  58. return
  59. }
  60. try? response.status(.OK).send(json: row.asDictionary()).end()
  61. }
  62. }
  63. //
  64. // TechEmpower test 3: Multiple database queries (raw, no ORM)
  65. // Get param provides number of queries: /queries?queries=N
  66. //
  67. router.get("/queries") {
  68. request, response, next in
  69. response.headers["Server"] = "Kitura"
  70. let queriesParam = request.queryParameters["queries"] ?? "1"
  71. let numQueries = max(1, min(Int(queriesParam) ?? 1, 500)) // Snap to range of 1-500 as per test spec
  72. getRandomRows_Raw(count: numQueries) { (rows, err) in
  73. if let rows = rows {
  74. try? response.status(.OK).send(json: rows).end()
  75. } else if let err = err {
  76. try? response.status(.badRequest).send("Error: \(err)").end()
  77. } else {
  78. fatalError("Unexpected: rows and err both nil")
  79. }
  80. }
  81. }
  82. router.get("/queriesParallel") {
  83. request, response, next in
  84. response.headers["Server"] = "Kitura"
  85. let queriesParam = request.queryParameters["queries"] ?? "1"
  86. let numQueries = max(1, min(Int(queriesParam) ?? 1, 500)) // Snap to range of 1-500 as per test spec
  87. var results: [[String:Int]] = []
  88. // Used to protect result array from concurrent modification
  89. let updateLock = DispatchSemaphore(value: 1)
  90. // Execute each query. Each callback will append its result to `results`
  91. for _ in 1...numQueries {
  92. getRandomRow_Raw { (row, err) in
  93. guard let row = row else {
  94. guard let err = err else {
  95. Log.error("Unknown Error")
  96. try? response.status(.badRequest).send("Unknown error").end()
  97. return
  98. }
  99. Log.error("\(err)")
  100. try? response.status(.badRequest).send("Error: \(err)").end()
  101. return
  102. }
  103. updateLock.wait()
  104. results.append(row.asDictionary())
  105. if results.count == numQueries {
  106. // Return JSON representation of array of results
  107. try? response.status(.OK).send(json: results).end()
  108. }
  109. updateLock.signal()
  110. }
  111. }
  112. }
  113. //
  114. // TechEmpower test 4: fortunes (raw, no ORM)
  115. //
  116. router.get("/fortunes") {
  117. request, response, next in
  118. response.headers["Server"] = "Kitura"
  119. response.headers["Content-Type"] = "text/html; charset=UTF-8"
  120. getFortunes { (fortunes, err) in
  121. guard var fortunes = fortunes else {
  122. guard let err = err else {
  123. Log.error("Unknown Error")
  124. try? response.status(.badRequest).send("Unknown error").end()
  125. return
  126. }
  127. Log.error("\(err)")
  128. try? response.status(.badRequest).send("Error: \(err)").end()
  129. return
  130. }
  131. fortunes.append(Fortune(id: 0, message: "Additional fortune added at request time."))
  132. do {
  133. try response.render("fortunes.stencil", context: ["fortunes": fortunes.sorted()]).end()
  134. } catch {
  135. print("Error: \(error)")
  136. }
  137. }
  138. }
  139. //
  140. // TechEmpower test 5: updates (raw, no ORM)
  141. //
  142. router.get("/updates") {
  143. request, response, next in
  144. response.headers["Server"] = "Kitura"
  145. let queriesParam = request.queryParameters["queries"] ?? "1"
  146. let numQueries = max(1, min(Int(queriesParam) ?? 1, 500)) // Snap to range of 1-500 as per test spec
  147. updateRandomRows_Raw(count: numQueries) { (rows, err) in
  148. if let rows = rows {
  149. try? response.status(.OK).send(json: rows).end()
  150. } else if let err = err {
  151. try? response.status(.badRequest).send("Error: \(err)").end()
  152. } else {
  153. fatalError("Unexpected: rows and err both nil")
  154. }
  155. }
  156. }
  157. router.get("/updatesParallel") {
  158. request, response, next in
  159. response.headers["Server"] = "Kitura"
  160. let queriesParam = request.queryParameters["queries"] ?? "1"
  161. let numQueries = max(1, min(Int(queriesParam) ?? 1, 500)) // Snap to range of 1-500 as per test spec
  162. var results: [[String:Int]] = []
  163. // Used to protect result array from concurrent modification
  164. let updateLock = DispatchSemaphore(value: 1)
  165. // Execute each query. Each callback will append its result to `results`
  166. for _ in 1...numQueries {
  167. getRandomRow_Raw { (row, err) in
  168. guard let row = row else {
  169. guard let err = err else {
  170. Log.error("Unknown Error")
  171. try? response.status(.badRequest).send("Unknown error").end()
  172. return
  173. }
  174. Log.error("\(err)")
  175. try? response.status(.badRequest).send("Error: \(err)").end()
  176. return
  177. }
  178. // Execute inner callback for updating the row
  179. updateRow_Raw(id: row.id) { (err) in
  180. if let err = err {
  181. try? response.status(.badRequest).send("Error: \(err)").end()
  182. return
  183. }
  184. updateLock.wait()
  185. results.append(row.asDictionary())
  186. if results.count == numQueries {
  187. // Return JSON representation of array of results
  188. try? response.status(.OK).send(json: results).end()
  189. }
  190. updateLock.signal()
  191. }
  192. }
  193. }
  194. }
  195. Kitura.addHTTPServer(onPort: 8080, with: router)
  196. Kitura.run()