fetch太慢去除历史提交记录
This commit is contained in:
commit
51c16a1efc
1294 files changed
+194322
No files matched your search
@@ -0,0 +1,102 @@
|
||||
package io.legado.app.web
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import com.google.gson.Gson
|
||||
import fi.iki.elonen.NanoHTTPD
|
||||
import io.legado.app.api.ReturnData
|
||||
import io.legado.app.api.controller.BookController
|
||||
import io.legado.app.api.controller.BookSourceController
|
||||
import io.legado.app.api.controller.RssSourceController
|
||||
import io.legado.app.web.utils.AssetsWeb
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.util.*
|
||||
|
||||
|
||||
class HttpServer(port: Int) : NanoHTTPD(port) {
|
||||
private val assetsWeb = AssetsWeb("web")
|
||||
|
||||
|
||||
override fun serve(session: IHTTPSession): Response {
|
||||
var returnData: ReturnData? = null
|
||||
val ct = ContentType(session.headers["content-type"]).tryUTF8()
|
||||
session.headers["content-type"] = ct.contentTypeHeader
|
||||
var uri = session.uri
|
||||
|
||||
try {
|
||||
when (session.method) {
|
||||
Method.OPTIONS -> {
|
||||
val response = newFixedLengthResponse("")
|
||||
response.addHeader("Access-Control-Allow-Methods", "POST")
|
||||
response.addHeader("Access-Control-Allow-Headers", "content-type")
|
||||
response.addHeader("Access-Control-Allow-Origin", session.headers["origin"])
|
||||
//response.addHeader("Access-Control-Max-Age", "3600");
|
||||
return response
|
||||
}
|
||||
Method.POST -> {
|
||||
val files = HashMap<String, String>()
|
||||
session.parseBody(files)
|
||||
val postData = files["postData"]
|
||||
|
||||
returnData = when (uri) {
|
||||
"/saveBookSource" -> BookSourceController.saveSource(postData)
|
||||
"/saveBookSources" -> BookSourceController.saveSources(postData)
|
||||
"/deleteBookSources" -> BookSourceController.deleteSources(postData)
|
||||
"/saveBook" -> BookController.saveBook(postData)
|
||||
"/addLocalBook" -> BookController.addLocalBook(session.parameters)
|
||||
"/saveRssSource" -> RssSourceController.saveSource(postData)
|
||||
"/saveRssSources" -> RssSourceController.saveSources(postData)
|
||||
"/deleteRssSources" -> RssSourceController.deleteSources(postData)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
Method.GET -> {
|
||||
val parameters = session.parameters
|
||||
|
||||
returnData = when (uri) {
|
||||
"/getBookSource" -> BookSourceController.getSource(parameters)
|
||||
"/getBookSources" -> BookSourceController.sources
|
||||
"/getBookshelf" -> BookController.bookshelf
|
||||
"/getChapterList" -> BookController.getChapterList(parameters)
|
||||
"/refreshToc" -> BookController.refreshToc(parameters)
|
||||
"/getBookContent" -> BookController.getBookContent(parameters)
|
||||
"/cover" -> BookController.getCover(parameters)
|
||||
"/getRssSource" -> RssSourceController.getSource(parameters)
|
||||
"/getRssSources" -> RssSourceController.sources
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
|
||||
if (returnData == null) {
|
||||
if (uri.endsWith("/"))
|
||||
uri += "index.html"
|
||||
return assetsWeb.getResponse(uri)
|
||||
}
|
||||
|
||||
val response = if (returnData.data is Bitmap) {
|
||||
val outputStream = ByteArrayOutputStream()
|
||||
(returnData.data as Bitmap).compress(Bitmap.CompressFormat.PNG, 100, outputStream)
|
||||
val byteArray = outputStream.toByteArray()
|
||||
outputStream.close()
|
||||
val inputStream = ByteArrayInputStream(byteArray)
|
||||
newFixedLengthResponse(
|
||||
Response.Status.OK,
|
||||
"image/png",
|
||||
inputStream,
|
||||
byteArray.size.toLong()
|
||||
)
|
||||
} else {
|
||||
newFixedLengthResponse(Gson().toJson(returnData))
|
||||
}
|
||||
response.addHeader("Access-Control-Allow-Methods", "GET, POST")
|
||||
response.addHeader("Access-Control-Allow-Origin", session.headers["origin"])
|
||||
return response
|
||||
} catch (e: Exception) {
|
||||
return newFixedLengthResponse(e.message)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
# web服务
|
||||
|
||||
* controller 数据操作
|
||||
* HttpServer http服务
|
||||
* WebSocketServer 持续通讯服务
|
||||
@@ -0,0 +1,20 @@
|
||||
package io.legado.app.web
|
||||
|
||||
import fi.iki.elonen.NanoWSD
|
||||
import io.legado.app.web.socket.BookSourceDebugWebSocket
|
||||
import io.legado.app.web.socket.RssSourceDebugWebSocket
|
||||
|
||||
class WebSocketServer(port: Int) : NanoWSD(port) {
|
||||
|
||||
override fun openWebSocket(handshake: IHTTPSession): WebSocket? {
|
||||
return when (handshake.uri) {
|
||||
"/bookSourceDebug" -> {
|
||||
BookSourceDebugWebSocket(handshake)
|
||||
}
|
||||
"/rssSourceDebug" -> {
|
||||
RssSourceDebugWebSocket(handshake)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package io.legado.app.web.socket
|
||||
|
||||
|
||||
import fi.iki.elonen.NanoHTTPD
|
||||
import fi.iki.elonen.NanoWSD
|
||||
import io.legado.app.R
|
||||
import io.legado.app.data.appDb
|
||||
import io.legado.app.model.Debug
|
||||
import io.legado.app.utils.*
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import splitties.init.appCtx
|
||||
import timber.log.Timber
|
||||
import java.io.IOException
|
||||
|
||||
|
||||
class BookSourceDebugWebSocket(handshakeRequest: NanoHTTPD.IHTTPSession) :
|
||||
NanoWSD.WebSocket(handshakeRequest),
|
||||
CoroutineScope by MainScope(),
|
||||
Debug.Callback {
|
||||
|
||||
private val notPrintState = arrayOf(10, 20, 30, 40)
|
||||
|
||||
override fun onOpen() {
|
||||
launch(IO) {
|
||||
kotlin.runCatching {
|
||||
while (isOpen) {
|
||||
ping("ping".toByteArray())
|
||||
delay(30000)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onClose(
|
||||
code: NanoWSD.WebSocketFrame.CloseCode,
|
||||
reason: String,
|
||||
initiatedByRemote: Boolean
|
||||
) {
|
||||
cancel()
|
||||
Debug.cancelDebug(true)
|
||||
}
|
||||
|
||||
override fun onMessage(message: NanoWSD.WebSocketFrame) {
|
||||
launch(IO) {
|
||||
kotlin.runCatching {
|
||||
if (!message.textPayload.isJson()) {
|
||||
send("数据必须为Json格式")
|
||||
close(NanoWSD.WebSocketFrame.CloseCode.NormalClosure, "调试结束", false)
|
||||
return@launch
|
||||
}
|
||||
val debugBean = GSON.fromJsonObject<Map<String, String>>(message.textPayload)
|
||||
if (debugBean != null) {
|
||||
val tag = debugBean["tag"]
|
||||
val key = debugBean["key"]
|
||||
if (tag.isNullOrBlank() || key.isNullOrBlank()) {
|
||||
send(appCtx.getString(R.string.cannot_empty))
|
||||
close(NanoWSD.WebSocketFrame.CloseCode.NormalClosure, "调试结束", false)
|
||||
return@launch
|
||||
}
|
||||
appDb.bookSourceDao.getBookSource(tag)?.let {
|
||||
Debug.callback = this@BookSourceDebugWebSocket
|
||||
Debug.startDebug(this, it, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPong(pong: NanoWSD.WebSocketFrame) {
|
||||
|
||||
}
|
||||
|
||||
override fun onException(exception: IOException) {
|
||||
Debug.cancelDebug(true)
|
||||
}
|
||||
|
||||
override fun printLog(state: Int, msg: String) {
|
||||
if (state in notPrintState) {
|
||||
return
|
||||
}
|
||||
runOnIO {
|
||||
runCatching {
|
||||
send(msg)
|
||||
if (state == -1 || state == 1000) {
|
||||
Debug.cancelDebug(true)
|
||||
close(NanoWSD.WebSocketFrame.CloseCode.NormalClosure, "调试结束", false)
|
||||
}
|
||||
}.onFailure {
|
||||
Timber.e(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package io.legado.app.web.socket
|
||||
|
||||
|
||||
import fi.iki.elonen.NanoHTTPD
|
||||
import fi.iki.elonen.NanoWSD
|
||||
import io.legado.app.R
|
||||
import io.legado.app.data.appDb
|
||||
import io.legado.app.model.Debug
|
||||
import io.legado.app.utils.*
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import splitties.init.appCtx
|
||||
import timber.log.Timber
|
||||
import java.io.IOException
|
||||
|
||||
|
||||
class RssSourceDebugWebSocket(handshakeRequest: NanoHTTPD.IHTTPSession) :
|
||||
NanoWSD.WebSocket(handshakeRequest),
|
||||
CoroutineScope by MainScope(),
|
||||
Debug.Callback {
|
||||
|
||||
private val notPrintState = arrayOf(10, 20, 30, 40)
|
||||
|
||||
override fun onOpen() {
|
||||
launch(IO) {
|
||||
kotlin.runCatching {
|
||||
while (isOpen) {
|
||||
ping("ping".toByteArray())
|
||||
delay(30000)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onClose(
|
||||
code: NanoWSD.WebSocketFrame.CloseCode,
|
||||
reason: String,
|
||||
initiatedByRemote: Boolean
|
||||
) {
|
||||
cancel()
|
||||
Debug.cancelDebug(true)
|
||||
}
|
||||
|
||||
override fun onMessage(message: NanoWSD.WebSocketFrame) {
|
||||
launch(IO) {
|
||||
kotlin.runCatching {
|
||||
if (!message.textPayload.isJson()) {
|
||||
send("数据必须为Json格式")
|
||||
close(NanoWSD.WebSocketFrame.CloseCode.NormalClosure, "调试结束", false)
|
||||
return@launch
|
||||
}
|
||||
val debugBean = GSON.fromJsonObject<Map<String, String>>(message.textPayload)
|
||||
if (debugBean != null) {
|
||||
val tag = debugBean["tag"]
|
||||
if (tag.isNullOrBlank()) {
|
||||
send(appCtx.getString(R.string.cannot_empty))
|
||||
close(NanoWSD.WebSocketFrame.CloseCode.NormalClosure, "调试结束", false)
|
||||
return@launch
|
||||
}
|
||||
appDb.rssSourceDao.getByKey(tag)?.let {
|
||||
Debug.callback = this@RssSourceDebugWebSocket
|
||||
Debug.startDebug(this, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPong(pong: NanoWSD.WebSocketFrame) {
|
||||
|
||||
}
|
||||
|
||||
override fun onException(exception: IOException) {
|
||||
Debug.cancelDebug(true)
|
||||
}
|
||||
|
||||
override fun printLog(state: Int, msg: String) {
|
||||
if (state in notPrintState) {
|
||||
return
|
||||
}
|
||||
runOnIO {
|
||||
runCatching {
|
||||
send(msg)
|
||||
if (state == -1 || state == 1000) {
|
||||
Debug.cancelDebug(true)
|
||||
close(NanoWSD.WebSocketFrame.CloseCode.NormalClosure, "调试结束", false)
|
||||
}
|
||||
}.onFailure {
|
||||
Timber.e(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package io.legado.app.web.utils
|
||||
|
||||
import android.content.res.AssetManager
|
||||
import android.text.TextUtils
|
||||
import fi.iki.elonen.NanoHTTPD
|
||||
import splitties.init.appCtx
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
||||
|
||||
class AssetsWeb(rootPath: String) {
|
||||
private val assetManager: AssetManager = appCtx.assets
|
||||
private var rootPath = "web"
|
||||
|
||||
init {
|
||||
if (!TextUtils.isEmpty(rootPath)) {
|
||||
this.rootPath = rootPath
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun getResponse(path: String): NanoHTTPD.Response {
|
||||
var path1 = path
|
||||
path1 = (rootPath + path1).replace("/+".toRegex(), File.separator)
|
||||
val inputStream = assetManager.open(path1)
|
||||
return NanoHTTPD.newChunkedResponse(
|
||||
NanoHTTPD.Response.Status.OK,
|
||||
getMimeType(path1),
|
||||
inputStream
|
||||
)
|
||||
}
|
||||
|
||||
private fun getMimeType(path: String): String {
|
||||
val suffix = path.substring(path.lastIndexOf("."))
|
||||
return when {
|
||||
suffix.equals(".html", ignoreCase = true)
|
||||
|| suffix.equals(".htm", ignoreCase = true) -> "text/html"
|
||||
suffix.equals(".js", ignoreCase = true) -> "text/javascript"
|
||||
suffix.equals(".css", ignoreCase = true) -> "text/css"
|
||||
suffix.equals(".ico", ignoreCase = true) -> "image/x-icon"
|
||||
else -> "text/html"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user