HttpServer.kt 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. package io.legato.kazusa.web
  2. import android.graphics.Bitmap
  3. import fi.iki.elonen.NanoHTTPD
  4. import io.legato.kazusa.api.ReturnData
  5. import io.legato.kazusa.api.controller.BookController
  6. import io.legato.kazusa.api.controller.BookSourceController
  7. import io.legato.kazusa.api.controller.ReplaceRuleController
  8. import io.legato.kazusa.api.controller.RssSourceController
  9. import io.legato.kazusa.help.coroutine.Coroutine
  10. import io.legato.kazusa.service.WebService
  11. import io.legato.kazusa.utils.GSON
  12. import io.legato.kazusa.utils.LogUtils
  13. import io.legato.kazusa.utils.stackTraceStr
  14. import io.legato.kazusa.web.utils.AssetsWeb
  15. import kotlinx.coroutines.runBlocking
  16. import okio.Pipe
  17. import okio.buffer
  18. import java.io.ByteArrayInputStream
  19. import java.io.ByteArrayOutputStream
  20. class HttpServer(port: Int) : NanoHTTPD(port) {
  21. private val assetsWeb = AssetsWeb("web")
  22. override fun serve(session: IHTTPSession): Response {
  23. WebService.serve()
  24. var returnData: ReturnData? = null
  25. val ct = ContentType(session.headers["content-type"]).tryUTF8()
  26. session.headers["content-type"] = ct.contentTypeHeader
  27. var uri = session.uri
  28. val startAt = System.currentTimeMillis()
  29. LogUtils.d(TAG) {
  30. "${session.method.name} - $uri - ${session.queryParameterString} - Start($startAt)"
  31. }
  32. try {
  33. when (session.method) {
  34. Method.OPTIONS -> {
  35. val response = newFixedLengthResponse("")
  36. response.addHeader("Access-Control-Allow-Methods", "POST")
  37. response.addHeader("Access-Control-Allow-Headers", "content-type")
  38. response.addHeader("Access-Control-Allow-Origin", session.headers["origin"])
  39. //response.addHeader("Access-Control-Max-Age", "3600");
  40. return response
  41. }
  42. Method.POST -> {
  43. val files = HashMap<String, String>()
  44. session.parseBody(files)
  45. val postData = files["postData"]
  46. returnData = runBlocking {
  47. when (uri) {
  48. "/saveBookSource" -> BookSourceController.saveSource(postData)
  49. "/saveBookSources" -> BookSourceController.saveSources(postData)
  50. "/deleteBookSources" -> BookSourceController.deleteSources(postData)
  51. "/saveBook" -> BookController.saveBook(postData)
  52. "/deleteBook" -> BookController.deleteBook(postData)
  53. "/saveBookProgress" -> BookController.saveBookProgress(postData)
  54. "/addLocalBook" -> BookController.addLocalBook(session.parameters, files)
  55. "/saveReadConfig" -> BookController.saveWebReadConfig(postData)
  56. "/saveRssSource" -> RssSourceController.saveSource(postData)
  57. "/saveRssSources" -> RssSourceController.saveSources(postData)
  58. "/deleteRssSources" -> RssSourceController.deleteSources(postData)
  59. "/saveReplaceRule" -> ReplaceRuleController.saveRule(postData)
  60. "/deleteReplaceRule" -> ReplaceRuleController.delete(postData)
  61. "/testReplaceRule" -> ReplaceRuleController.testRule(postData)
  62. else -> null
  63. }
  64. }
  65. }
  66. Method.GET -> {
  67. val parameters = session.parameters
  68. returnData = when (uri) {
  69. "/getBookSource" -> BookSourceController.getSource(parameters)
  70. "/getBookSources" -> BookSourceController.sources
  71. "/getBookshelf" -> BookController.bookshelf
  72. "/getChapterList" -> BookController.getChapterList(parameters)
  73. "/refreshToc" -> BookController.refreshToc(parameters)
  74. "/getBookContent" -> BookController.getBookContent(parameters)
  75. "/cover" -> BookController.getCover(parameters)
  76. "/image" -> BookController.getImg(parameters)
  77. "/getReadConfig" -> BookController.getWebReadConfig()
  78. "/getRssSource" -> RssSourceController.getSource(parameters)
  79. "/getRssSources" -> RssSourceController.sources
  80. "/getReplaceRules" -> ReplaceRuleController.allRules
  81. else -> null
  82. }
  83. }
  84. else -> Unit
  85. }
  86. if (returnData == null) {
  87. if (uri.endsWith("/"))
  88. uri += "index.html"
  89. return assetsWeb.getResponse(uri)
  90. }
  91. val response = if (returnData.data is Bitmap) {
  92. val outputStream = ByteArrayOutputStream()
  93. (returnData.data as Bitmap).compress(Bitmap.CompressFormat.PNG, 100, outputStream)
  94. val byteArray = outputStream.toByteArray()
  95. outputStream.close()
  96. val inputStream = ByteArrayInputStream(byteArray)
  97. newFixedLengthResponse(
  98. Response.Status.OK,
  99. "image/png",
  100. inputStream,
  101. byteArray.size.toLong()
  102. )
  103. } else {
  104. val data = returnData.data
  105. if (data is List<*> && data.size > 3000) {
  106. val pipe = Pipe(16 * 1024)
  107. Coroutine.async {
  108. pipe.sink.buffer().outputStream().bufferedWriter(Charsets.UTF_8).use {
  109. GSON.toJson(returnData, it)
  110. }
  111. }
  112. newChunkedResponse(
  113. Response.Status.OK,
  114. "application/json",
  115. pipe.source.buffer().inputStream()
  116. )
  117. } else {
  118. newFixedLengthResponse(GSON.toJson(returnData))
  119. }
  120. }
  121. response.addHeader("Access-Control-Allow-Methods", "GET, POST")
  122. response.addHeader("Access-Control-Allow-Origin", session.headers["origin"])
  123. LogUtils.d(TAG) {
  124. "${session.method.name} - $uri - ${session.queryParameterString} - End($startAt)"
  125. }
  126. return response
  127. } catch (e: Exception) {
  128. LogUtils.d(TAG) {
  129. "${session.method.name} - $uri - ${session.queryParameterString} - Error End($startAt)\n$e\n${e.stackTraceStr}"
  130. }
  131. return newFixedLengthResponse(e.message)
  132. }
  133. }
  134. companion object {
  135. private const val TAG = "HttpServer"
  136. }
  137. }