mirror of
https://github.com/tursom/TursomServer.git
synced 2025-02-04 00:30:23 +08:00
增强web
This commit is contained in:
parent
e4c6b5657e
commit
85291d9f59
@ -1,14 +0,0 @@
|
|||||||
package cn.tursom.web.router.suspend
|
|
||||||
|
|
||||||
interface ISuspendRouter<T> {
|
|
||||||
suspend fun setSubRoute(route: String, value: T?, onDestroy: ((oldValue: T) -> Unit)? = null)
|
|
||||||
suspend fun delRoute(route: String, onDestroy: ((oldValue: T) -> Unit)? = null)
|
|
||||||
|
|
||||||
suspend fun set(
|
|
||||||
route: String,
|
|
||||||
onDestroy: ((oldValue: T) -> Unit)? = null,
|
|
||||||
value: T?
|
|
||||||
) = setSubRoute(route, value, onDestroy)
|
|
||||||
|
|
||||||
suspend fun get(route: String): Pair<T?, List<Pair<String, String>>>
|
|
||||||
}
|
|
@ -1,145 +0,0 @@
|
|||||||
package cn.tursom.web.router.suspend.impl
|
|
||||||
|
|
||||||
import cn.tursom.web.router.suspend.ISuspendRouter
|
|
||||||
import cn.tursom.web.router.suspend.impl.node.ISuspendColonStarNode
|
|
||||||
import cn.tursom.web.router.suspend.impl.node.SuspendAnyColonStarNode
|
|
||||||
import cn.tursom.web.router.suspend.impl.node.SuspendPlaceholderColonStarNode
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import java.util.concurrent.Executors
|
|
||||||
|
|
||||||
@Suppress("unused", "unused", "MemberVisibilityCanBePrivate", "UNUSED_PARAMETER")
|
|
||||||
class SuspendColonStarRouter<T> : ISuspendRouter<T> {
|
|
||||||
private val rootNode = cn.tursom.web.router.suspend.impl.node.SuspendColonStarNode<T>(listOf(""), 0, null)
|
|
||||||
private val threadPool = Executors.newSingleThreadExecutor()
|
|
||||||
|
|
||||||
@Volatile
|
|
||||||
private var _lastChangeTime: Long = System.currentTimeMillis()
|
|
||||||
val lashChangeTime
|
|
||||||
get() = _lastChangeTime
|
|
||||||
@Volatile
|
|
||||||
private var strBuf: String = ""
|
|
||||||
@Volatile
|
|
||||||
private var strBufTime: Long = 0
|
|
||||||
|
|
||||||
val root: ISuspendColonStarNode<T> = rootNode
|
|
||||||
|
|
||||||
override suspend fun setSubRoute(
|
|
||||||
route: String,
|
|
||||||
value: T?,
|
|
||||||
onDestroy: ((oldValue: T) -> Unit)?
|
|
||||||
) {
|
|
||||||
val routeList = route.split('?')[0].split('/').filter { it.isNotEmpty() }
|
|
||||||
var routeNode = rootNode
|
|
||||||
var r: String
|
|
||||||
var index = 0
|
|
||||||
while (index < routeList.size) {
|
|
||||||
r = routeList[index]
|
|
||||||
routeNode = when {
|
|
||||||
r.isEmpty() -> routeNode
|
|
||||||
|
|
||||||
r == "*" -> routeNode.wildSubRouter ?: run {
|
|
||||||
val node = SuspendAnyColonStarNode<T>(routeList, index, null)
|
|
||||||
routeNode.wildSubRouter = node
|
|
||||||
index = routeList.size - 1
|
|
||||||
node
|
|
||||||
}
|
|
||||||
|
|
||||||
r[0] == ':' -> {
|
|
||||||
val matchLength = SuspendPlaceholderColonStarNode.matchLength(routeList, index)
|
|
||||||
val node = routeNode.getPlaceholderRouter(matchLength) ?: suspend {
|
|
||||||
routeNode.addNode(routeList, index, null)
|
|
||||||
routeNode.getPlaceholderRouter(matchLength)!!
|
|
||||||
}()
|
|
||||||
index += node.size - 1
|
|
||||||
node
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> routeNode.subRouterMap[r] ?: {
|
|
||||||
val node = cn.tursom.web.router.suspend.impl.node.SuspendColonStarNode<T>(routeList, index, null)
|
|
||||||
routeNode.subRouterMap[r] = node
|
|
||||||
node
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
index++
|
|
||||||
}
|
|
||||||
val oldValue = routeNode.value
|
|
||||||
if (oldValue != null) onDestroy?.invoke(oldValue)
|
|
||||||
routeNode.value = value
|
|
||||||
routeNode.routeList = routeList
|
|
||||||
routeNode.index = index - 1
|
|
||||||
_lastChangeTime = System.currentTimeMillis()
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun delRoute(route: String, onDestroy: ((oldValue: T) -> Unit)?) {
|
|
||||||
this.set(route, null, onDestroy)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun set(
|
|
||||||
route: String,
|
|
||||||
value: T?,
|
|
||||||
onDestroy: ((oldValue: T) -> Unit)? = null
|
|
||||||
) = setSubRoute(route, value, onDestroy)
|
|
||||||
|
|
||||||
override suspend fun get(route: String): Pair<T?, List<Pair<String, String>>> {
|
|
||||||
val list = ArrayList<Pair<String, String>>()
|
|
||||||
val endIndex = route.indexOf('?')
|
|
||||||
return rootNode.get(
|
|
||||||
(if (endIndex < 0) route else route.substring(0, endIndex))
|
|
||||||
.split('/').filter { it.isNotEmpty() },
|
|
||||||
list
|
|
||||||
)?.value to list
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun toString(node: cn.tursom.web.router.suspend.impl.node.SuspendColonStarNode<T>, stringBuilder: StringBuilder, indentation: String) {
|
|
||||||
if (
|
|
||||||
node.value == null &&
|
|
||||||
node.subRouterMap.isEmpty() &&
|
|
||||||
node.placeholderRouterListEmpty &&
|
|
||||||
node.wildSubRouter == null
|
|
||||||
) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (indentation.isNotEmpty()) {
|
|
||||||
stringBuilder.append(indentation)
|
|
||||||
stringBuilder.append("- ")
|
|
||||||
}
|
|
||||||
stringBuilder.append("${node.lastRoute}${if (node.value != null) " ${node.value}" else ""}\n")
|
|
||||||
|
|
||||||
if (node is SuspendAnyColonStarNode) return
|
|
||||||
|
|
||||||
val subIndentation = if (indentation.isEmpty()) "|" else "$indentation |"
|
|
||||||
|
|
||||||
node.subRouterMap.forEach { (_, u) ->
|
|
||||||
toString(u, stringBuilder, subIndentation)
|
|
||||||
}
|
|
||||||
node.forEachPlaceholderRouter {
|
|
||||||
toString(it, stringBuilder, subIndentation)
|
|
||||||
}
|
|
||||||
toString(node.wildSubRouter ?: return, stringBuilder, subIndentation)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun suspendToString(): String {
|
|
||||||
if (strBufTime < _lastChangeTime) {
|
|
||||||
val stringBuilder = StringBuilder()
|
|
||||||
toString(rootNode, stringBuilder, "")
|
|
||||||
strBuf = stringBuilder.toString()
|
|
||||||
strBufTime = System.currentTimeMillis()
|
|
||||||
}
|
|
||||||
return strBuf
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
if (strBufTime < _lastChangeTime) {
|
|
||||||
val stringBuilder = StringBuilder()
|
|
||||||
runBlocking {
|
|
||||||
toString(rootNode, stringBuilder, "")
|
|
||||||
}
|
|
||||||
strBuf = stringBuilder.toString()
|
|
||||||
strBufTime = System.currentTimeMillis()
|
|
||||||
}
|
|
||||||
return strBuf
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
|||||||
package cn.tursom.web.router.suspend.impl.node
|
|
||||||
|
|
||||||
interface ISuspendColonStarNode<T> {
|
|
||||||
val value: T?
|
|
||||||
val lastRoute: String
|
|
||||||
val fullRoute: String
|
|
||||||
val empty: Boolean
|
|
||||||
|
|
||||||
suspend fun forEach(action: suspend (node: ISuspendColonStarNode<T>) -> Unit)
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
package cn.tursom.web.router.suspend.impl.node
|
|
||||||
|
|
||||||
import java.util.AbstractList
|
|
||||||
|
|
||||||
internal class SuspendAnyColonStarNode<T>(
|
|
||||||
route: List<String>,
|
|
||||||
index: Int,
|
|
||||||
value: T? = null
|
|
||||||
) : SuspendColonStarNode<T>(route, index, value) {
|
|
||||||
private val subNode = this to 1
|
|
||||||
override fun match(route: List<String>, startIndex: Int) = true to 1
|
|
||||||
override suspend fun get(route: List<String>, startIndex: Int, routeList: AbstractList<Pair<String, String>>) = subNode
|
|
||||||
}
|
|
@ -1,223 +0,0 @@
|
|||||||
package cn.tursom.web.router.suspend.impl.node
|
|
||||||
|
|
||||||
import cn.tursom.utils.asynclock.AsyncReadFirstRWLock
|
|
||||||
import cn.tursom.core.binarySearch
|
|
||||||
|
|
||||||
@Suppress("MemberVisibilityCanBePrivate")
|
|
||||||
internal open class SuspendColonStarNode<T>(
|
|
||||||
var routeList: List<String>,
|
|
||||||
var index: Int,
|
|
||||||
override var value: T? = null
|
|
||||||
) : ISuspendColonStarNode<T> {
|
|
||||||
val route: String = routeList[index]
|
|
||||||
var wildSubRouter: SuspendAnyColonStarNode<T>? = null
|
|
||||||
|
|
||||||
private val placeholderRouterListLock = AsyncReadFirstRWLock()
|
|
||||||
protected open val placeholderRouterList: ArrayList<SuspendPlaceholderColonStarNode<T>>? = ArrayList(0)
|
|
||||||
|
|
||||||
private val subRouterMapLock = AsyncReadFirstRWLock()
|
|
||||||
val subRouterMap = HashMap<String, cn.tursom.web.router.suspend.impl.node.SuspendColonStarNode<T>>(0)
|
|
||||||
|
|
||||||
override val lastRoute
|
|
||||||
get() = "/$route"
|
|
||||||
|
|
||||||
override val fullRoute: String by lazy {
|
|
||||||
val stringBuilder = StringBuilder("")
|
|
||||||
for (i in 0..index) {
|
|
||||||
val s = routeList[i]
|
|
||||||
if (s.isNotEmpty()) stringBuilder.append("/$s")
|
|
||||||
}
|
|
||||||
stringBuilder.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
val placeholderRouterListEmpty
|
|
||||||
get() = placeholderRouterList?.isEmpty() ?: true
|
|
||||||
|
|
||||||
override val empty: Boolean
|
|
||||||
get() = value == null &&
|
|
||||||
subRouterMap.isEmpty() &&
|
|
||||||
placeholderRouterListEmpty &&
|
|
||||||
wildSubRouter == null
|
|
||||||
|
|
||||||
override suspend fun forEach(action: suspend (node: ISuspendColonStarNode<T>) -> Unit) {
|
|
||||||
placeholderRouterListLock.doRead {
|
|
||||||
placeholderRouterList?.forEach { action(it) }
|
|
||||||
}
|
|
||||||
subRouterMapLock.doRead {
|
|
||||||
subRouterMap.forEach { (_, u) -> action(u) }
|
|
||||||
}
|
|
||||||
wildSubRouter?.let { action(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun forEachPlaceholderRouter(block: suspend (SuspendPlaceholderColonStarNode<T>) -> Unit) {
|
|
||||||
placeholderRouterListLock.doRead { placeholderRouterList?.forEach { block(it) } }
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun getPlaceholderRouter(length: Int): SuspendPlaceholderColonStarNode<T>? {
|
|
||||||
return placeholderRouterListLock.doRead { placeholderRouterList!!.binarySearch { it.size - length } }
|
|
||||||
}
|
|
||||||
|
|
||||||
open fun match(
|
|
||||||
route: List<String>,
|
|
||||||
startIndex: Int
|
|
||||||
): Pair<Boolean, Int> = (route.size > startIndex && route[startIndex] == this.route) to 1
|
|
||||||
|
|
||||||
suspend fun addNode(route: List<String>, startIndex: Int, value: T? = null): Int {
|
|
||||||
val r = route[startIndex]
|
|
||||||
return when {
|
|
||||||
r.isEmpty() -> return addNode(route, startIndex + 1)
|
|
||||||
r == "*" -> {
|
|
||||||
wildSubRouter = SuspendAnyColonStarNode(route, startIndex, null)
|
|
||||||
1
|
|
||||||
}
|
|
||||||
r[0] == ':' -> {
|
|
||||||
val node: SuspendPlaceholderColonStarNode<T> = SuspendPlaceholderColonStarNode(
|
|
||||||
route,
|
|
||||||
startIndex,
|
|
||||||
value = value
|
|
||||||
)
|
|
||||||
// 必须保证 placeholderRouterList 存在,而且还不能有这个长度的节点
|
|
||||||
if (placeholderRouterListLock.doRead { placeholderRouterList!!.binarySearch { it.size - node.size } } != null) {
|
|
||||||
throw Exception()
|
|
||||||
}
|
|
||||||
placeholderRouterListLock.doWrite {
|
|
||||||
placeholderRouterList?.add(node)
|
|
||||||
placeholderRouterList?.sortBy { it.size }
|
|
||||||
}
|
|
||||||
node.size
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
subRouterMap[r] = SuspendColonStarNode(route, startIndex, value)
|
|
||||||
1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun get(route: List<String>, startIndex: Int = 0): Pair<cn.tursom.web.router.suspend.impl.node.SuspendColonStarNode<T>?, Int> {
|
|
||||||
val r = route[startIndex]
|
|
||||||
if (r.isEmpty()) return this to 1
|
|
||||||
|
|
||||||
val value = subRouterMap[r]
|
|
||||||
if (value != null) return value to 1
|
|
||||||
|
|
||||||
val matchLength = route.size - startIndex
|
|
||||||
val exactRoute = placeholderRouterList?.let { list ->
|
|
||||||
list.binarySearch { matchLength - it.size }
|
|
||||||
}
|
|
||||||
if (exactRoute != null) return exactRoute to matchLength
|
|
||||||
|
|
||||||
placeholderRouterList?.let { list ->
|
|
||||||
list.forEach {
|
|
||||||
val subRoute = it.getRoute(route, startIndex + it.size)
|
|
||||||
if (subRoute != null) return subRoute to route.size - startIndex
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return wildSubRouter to 1
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getRoute(route: List<String>, startIndex: Int = 0): cn.tursom.web.router.suspend.impl.node.SuspendColonStarNode<T>? {
|
|
||||||
var index = startIndex
|
|
||||||
var routeNode = this
|
|
||||||
while (index < route.size) {
|
|
||||||
val (node, size) = routeNode[route, index]
|
|
||||||
routeNode = node ?: return null
|
|
||||||
index += size
|
|
||||||
}
|
|
||||||
return routeNode
|
|
||||||
}
|
|
||||||
|
|
||||||
open suspend fun get(
|
|
||||||
route: List<String>,
|
|
||||||
startIndex: Int = 0,
|
|
||||||
routeList: java.util.AbstractList<Pair<String, String>>
|
|
||||||
): Pair<cn.tursom.web.router.suspend.impl.node.SuspendColonStarNode<T>?, Int> {
|
|
||||||
val r = route[startIndex]
|
|
||||||
if (r.isEmpty()) {
|
|
||||||
return this to 1
|
|
||||||
}
|
|
||||||
|
|
||||||
val value = subRouterMapLock.doRead { subRouterMap[r] }
|
|
||||||
if (value != null) return value to 1
|
|
||||||
|
|
||||||
val matchLength = route.size - startIndex
|
|
||||||
val exactRoute = placeholderRouterListLock.doRead {
|
|
||||||
placeholderRouterList?.binarySearch { matchLength - it.size }
|
|
||||||
}
|
|
||||||
if (exactRoute != null) {
|
|
||||||
exactRoute.routeList.forEachIndexed { index, s ->
|
|
||||||
if (s.isNotEmpty() && s[0] == ':') routeList.add(s.substring(1) to route[index])
|
|
||||||
}
|
|
||||||
return exactRoute to matchLength
|
|
||||||
}
|
|
||||||
|
|
||||||
if (placeholderRouterList != null) {
|
|
||||||
val list = ArrayList<Pair<String, String>>()
|
|
||||||
val detected = placeholderRouterListLock.doRead {
|
|
||||||
placeholderRouterList?.let { routerList ->
|
|
||||||
routerList.forEach {
|
|
||||||
list.clear()
|
|
||||||
val subRoute = it.getRoute(route, startIndex + it.size, list)
|
|
||||||
if (subRoute != null) {
|
|
||||||
subRoute.routeList.forEachIndexed { index, s ->
|
|
||||||
if (s.isNotEmpty()) when {
|
|
||||||
s == "*" -> for (i in index until route.size) {
|
|
||||||
routeList.add("*" to route[i])
|
|
||||||
}
|
|
||||||
s[0] == ':' -> routeList.add(s.substring(1) to route[index])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var listIndex = 0
|
|
||||||
var routeIndex = 0
|
|
||||||
while (listIndex < list.size && routeIndex <= index) {
|
|
||||||
val s = this.routeList[routeIndex++]
|
|
||||||
if (s.isNotEmpty() && s[0] == ':') {
|
|
||||||
routeList.add(s to list[listIndex++].second)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return@doRead subRoute to route.size - startIndex
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
null
|
|
||||||
}
|
|
||||||
if (detected != null) return detected
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i in startIndex until route.size)
|
|
||||||
routeList.add("*" to route[i])
|
|
||||||
return wildSubRouter to 1
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun get(
|
|
||||||
route: List<String>,
|
|
||||||
routeList: java.util.AbstractList<Pair<String, String>>
|
|
||||||
) = getRoute(route, 0, routeList)
|
|
||||||
|
|
||||||
suspend fun getRoute(
|
|
||||||
route: List<String>,
|
|
||||||
startIndex: Int = 0,
|
|
||||||
routeList: java.util.AbstractList<Pair<String, String>>
|
|
||||||
): cn.tursom.web.router.suspend.impl.node.SuspendColonStarNode<T>? {
|
|
||||||
var index = startIndex
|
|
||||||
var routeNode = this
|
|
||||||
while (index < route.size) {
|
|
||||||
val (node, size) = routeNode.get(route, index, routeList)
|
|
||||||
routeNode = node ?: return null
|
|
||||||
index += size
|
|
||||||
}
|
|
||||||
return routeNode
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
val stringBuilder = StringBuilder("/")
|
|
||||||
for (i in 0..index) {
|
|
||||||
val s = routeList[i]
|
|
||||||
if (s.isNotEmpty()) stringBuilder.append("$s/")
|
|
||||||
}
|
|
||||||
if (value != null) {
|
|
||||||
stringBuilder.append(" $value")
|
|
||||||
}
|
|
||||||
return stringBuilder.toString()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,53 +0,0 @@
|
|||||||
package cn.tursom.web.router.suspend.impl.node
|
|
||||||
|
|
||||||
internal class SuspendPlaceholderColonStarNode<T>(
|
|
||||||
route: List<String>,
|
|
||||||
private val startIndex: Int = 0,
|
|
||||||
endIndex: Int = startIndex + route.matchLength(startIndex),
|
|
||||||
value: T? = null
|
|
||||||
) : SuspendColonStarNode<T>(route, endIndex - 1, value) {
|
|
||||||
override val placeholderRouterList: ArrayList<SuspendPlaceholderColonStarNode<T>>?
|
|
||||||
get() = null
|
|
||||||
|
|
||||||
val size: Int = route.matchLength(startIndex, endIndex)
|
|
||||||
|
|
||||||
override fun match(
|
|
||||||
route: List<String>,
|
|
||||||
startIndex: Int
|
|
||||||
): Pair<Boolean, Int> =
|
|
||||||
(size == route.matchLength(startIndex)) to size
|
|
||||||
|
|
||||||
override val lastRoute: String
|
|
||||||
get() {
|
|
||||||
val sb = StringBuilder()
|
|
||||||
for (i in startIndex..index) {
|
|
||||||
sb.append("/")
|
|
||||||
sb.append(routeList[i])
|
|
||||||
}
|
|
||||||
return sb.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmStatic
|
|
||||||
private fun List<String>.matchLength(startIndex: Int, endIndex: Int = size): Int {
|
|
||||||
var length = 0
|
|
||||||
for (i in startIndex until endIndex) {
|
|
||||||
if (this[i].isEmpty()) continue
|
|
||||||
else if (this[i][0] != ':') return length
|
|
||||||
else length++
|
|
||||||
}
|
|
||||||
return length
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun matchLength(route: List<String>, startIndex: Int): Int {
|
|
||||||
var length = 0
|
|
||||||
for (i in startIndex until route.size) {
|
|
||||||
if (route[i].isEmpty()) continue
|
|
||||||
else if (route[i][0] != ':') return length
|
|
||||||
else length++
|
|
||||||
}
|
|
||||||
return length
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
3
web/src/main/kotlin/cn/tursom/web/router/BlockHandler.kt
Normal file
3
web/src/main/kotlin/cn/tursom/web/router/BlockHandler.kt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
package cn.tursom.web.router
|
||||||
|
|
||||||
|
annotation class BlockHandler
|
@ -13,10 +13,16 @@ import cn.tursom.web.result.Json
|
|||||||
import cn.tursom.web.result.Text
|
import cn.tursom.web.result.Text
|
||||||
import cn.tursom.web.router.impl.SimpleRouter
|
import cn.tursom.web.router.impl.SimpleRouter
|
||||||
import cn.tursom.web.utils.Chunked
|
import cn.tursom.web.utils.Chunked
|
||||||
|
import cn.tursom.web.utils.ContextType
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.RandomAccessFile
|
import java.io.RandomAccessFile
|
||||||
import java.lang.reflect.Method
|
import java.lang.reflect.Method
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue
|
||||||
|
import java.util.concurrent.ThreadFactory
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 自动添加路径映射的处理器
|
* 自动添加路径映射的处理器
|
||||||
@ -28,10 +34,20 @@ import java.lang.reflect.Method
|
|||||||
@Suppress("MemberVisibilityCanBePrivate", "unused")
|
@Suppress("MemberVisibilityCanBePrivate", "unused")
|
||||||
open class RoutedHttpHandler(
|
open class RoutedHttpHandler(
|
||||||
target: Any? = null,
|
target: Any? = null,
|
||||||
val routerMaker: () -> Router<Pair<Any?, (HttpContent) -> Unit>> = { SimpleRouter() }
|
val routerMaker: () -> Router<Pair<Any?, (HttpContent) -> Any?>> = { SimpleRouter() }
|
||||||
) : HttpHandler<HttpContent, ExceptionContent> {
|
) : HttpHandler<HttpContent, ExceptionContent> {
|
||||||
protected val router: Router<Pair<Any?, (HttpContent) -> Unit>> = routerMaker()
|
protected val router: Router<Pair<Any?, (HttpContent) -> Any?>> = routerMaker()
|
||||||
protected val routerMap: HashMap<String, Router<Pair<Any?, (HttpContent) -> Unit>>> = HashMap()
|
protected val routerMap: HashMap<String, Router<Pair<Any?, (HttpContent) -> Any?>>> = HashMap()
|
||||||
|
private val threadNumber = AtomicInteger(0)
|
||||||
|
val workerThread = ThreadPoolExecutor(
|
||||||
|
Runtime.getRuntime().availableProcessors() * 4,
|
||||||
|
Runtime.getRuntime().availableProcessors() * 4,
|
||||||
|
0L, TimeUnit.MILLISECONDS,
|
||||||
|
LinkedBlockingQueue<Runnable>(),
|
||||||
|
ThreadFactory {
|
||||||
|
Thread(it, "TreeDiagramWorker-${threadNumber.incrementAndGet()}")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@Suppress("LeakingThis")
|
@Suppress("LeakingThis")
|
||||||
@ -47,7 +63,7 @@ open class RoutedHttpHandler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun handle(content: HttpContent, handler: ((HttpContent) -> Unit)?) {
|
open fun handle(content: HttpContent, handler: ((HttpContent) -> Any?)?) {
|
||||||
if (handler != null) {
|
if (handler != null) {
|
||||||
handler(content)
|
handler(content)
|
||||||
} else {
|
} else {
|
||||||
@ -73,17 +89,17 @@ open class RoutedHttpHandler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addRouter(route: String, handler: (HttpContent) -> Unit) = addRouter(route, null, handler)
|
fun addRouter(route: String, handler: (HttpContent) -> Any?) = addRouter(route, null, handler)
|
||||||
fun addRouter(route: String, obj: Any?, handler: (HttpContent) -> Unit) {
|
fun addRouter(route: String, obj: Any?, handler: (HttpContent) -> Any?) {
|
||||||
router[safeRoute(route)] = obj to handler
|
router[safeRoute(route)] = obj to handler
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addRouter(method: String, route: String, handler: (HttpContent) -> Unit) = addRouter(method, route, null, handler)
|
fun addRouter(method: String, route: String, handler: (HttpContent) -> Any?) = addRouter(method, route, null, handler)
|
||||||
fun addRouter(method: String, route: String, obj: Any?, handler: (HttpContent) -> Unit) {
|
fun addRouter(method: String, route: String, obj: Any?, handler: (HttpContent) -> Any?) {
|
||||||
getRouter(method)[safeRoute(route)] = obj to handler
|
getRouter(method)[safeRoute(route)] = obj to handler
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getHandler(content: MutableHttpContent, method: String, route: String): ((HttpContent) -> Unit)? {
|
fun getHandler(content: MutableHttpContent, method: String, route: String): ((HttpContent) -> Any?)? {
|
||||||
val safeRoute = safeRoute(route)
|
val safeRoute = safeRoute(route)
|
||||||
val router = getHandler(method, safeRoute)
|
val router = getHandler(method, safeRoute)
|
||||||
if (router.first != null) {
|
if (router.first != null) {
|
||||||
@ -94,7 +110,7 @@ open class RoutedHttpHandler(
|
|||||||
return router.first?.second ?: this.router[safeRoute].first?.second
|
return router.first?.second ?: this.router[safeRoute].first?.second
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getHandler(method: String, route: String): Pair<Pair<Any?, (HttpContent) -> Unit>?, List<Pair<String, String>>> {
|
fun getHandler(method: String, route: String): Pair<Pair<Any?, (HttpContent) -> Any?>?, List<Pair<String, String>>> {
|
||||||
val safeRoute = safeRoute(route)
|
val safeRoute = safeRoute(route)
|
||||||
val router = getRouter(method)[safeRoute]
|
val router = getRouter(method)[safeRoute]
|
||||||
return if (router.first != null) router else this.router[safeRoute]
|
return if (router.first != null) router else this.router[safeRoute]
|
||||||
@ -115,9 +131,9 @@ open class RoutedHttpHandler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addRouter(obj: Any, method: Method, route: String, router: Router<Pair<Any?, (HttpContent) -> Unit>>) {
|
fun addRouter(obj: Any, method: Method, route: String, router: Router<Pair<Any?, (HttpContent) -> Any?>>) {
|
||||||
router[safeRoute(route)] = if (method.parameterTypes.isEmpty()) {
|
router[safeRoute(route)] = obj to (if (method.parameterTypes.isEmpty()) {
|
||||||
obj to when {
|
when {
|
||||||
method.getAnnotation(Html::class.java) != null -> { content ->
|
method.getAnnotation(Html::class.java) != null -> { content ->
|
||||||
method(obj)?.let { result -> finishHtml(result, content) }
|
method(obj)?.let { result -> finishHtml(result, content) }
|
||||||
}
|
}
|
||||||
@ -128,10 +144,10 @@ open class RoutedHttpHandler(
|
|||||||
method(obj)?.let { result -> finishJson(result, content) }
|
method(obj)?.let { result -> finishJson(result, content) }
|
||||||
}
|
}
|
||||||
else -> { content ->
|
else -> { content ->
|
||||||
method(obj)?.let { result -> autoReturn(result, content) }
|
method(obj)?.let { result -> autoReturn(method, result, content) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else obj to when (method.returnType) {
|
} else when (method.returnType) {
|
||||||
Void::class.java -> { content -> method(obj, content) }
|
Void::class.java -> { content -> method(obj, content) }
|
||||||
Void.TYPE -> { content -> method(obj, content) }
|
Void.TYPE -> { content -> method(obj, content) }
|
||||||
Unit::class.java -> { content -> method(obj, content) }
|
Unit::class.java -> { content -> method(obj, content) }
|
||||||
@ -145,10 +161,15 @@ open class RoutedHttpHandler(
|
|||||||
method.getAnnotation(Json::class.java) != null -> { content ->
|
method.getAnnotation(Json::class.java) != null -> { content ->
|
||||||
method(obj, content)?.let { result -> finishJson(result, content) }
|
method(obj, content)?.let { result -> finishJson(result, content) }
|
||||||
}
|
}
|
||||||
else -> { content ->
|
else -> { content: HttpContent ->
|
||||||
method(obj, content)?.let { result -> autoReturn(result, content) }
|
method(obj, content)?.let { result -> autoReturn(method, result, content) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}).let {
|
||||||
|
if (method.getAnnotation(BlockHandler::class.java) != null) { content ->
|
||||||
|
workerThread.execute { it(content) }
|
||||||
|
} else
|
||||||
|
it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,7 +206,7 @@ open class RoutedHttpHandler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun deleteMapping(obj: Any, route: String, router: Router<Pair<Any?, (HttpContent) -> Unit>>) {
|
protected fun deleteMapping(obj: Any, route: String, router: Router<Pair<Any?, (HttpContent) -> Any?>>) {
|
||||||
val handler = router[safeRoute(route)].first
|
val handler = router[safeRoute(route)].first
|
||||||
if (handler?.first == obj) {
|
if (handler?.first == obj) {
|
||||||
router.delRoute(safeRoute(route))
|
router.delRoute(safeRoute(route))
|
||||||
@ -226,7 +247,7 @@ open class RoutedHttpHandler(
|
|||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun getRouter(method: String): Router<Pair<Any?, (HttpContent) -> Unit>> = when {
|
protected fun getRouter(method: String): Router<Pair<Any?, (HttpContent) -> Any?>> = when {
|
||||||
method.isEmpty() -> router
|
method.isEmpty() -> router
|
||||||
else -> {
|
else -> {
|
||||||
val upperCaseMethod = method.toUpperCase()
|
val upperCaseMethod = method.toUpperCase()
|
||||||
@ -267,6 +288,13 @@ open class RoutedHttpHandler(
|
|||||||
if (it.endsWith('/')) it.dropLast(1) else it
|
if (it.endsWith('/')) it.dropLast(1) else it
|
||||||
}.repeatUntil({ it.contains("//") }) { it.replace(slashRegex, "/") }
|
}.repeatUntil({ it.contains("//") }) { it.replace(slashRegex, "/") }
|
||||||
|
|
||||||
|
fun autoReturn(method: Method, result: Any?, content: HttpContent) {
|
||||||
|
method.getAnnotation(ContextType::class.java)?.let {
|
||||||
|
content.autoContextType(it.type)
|
||||||
|
}
|
||||||
|
autoReturn(result, content)
|
||||||
|
}
|
||||||
|
|
||||||
fun autoReturn(result: Any?, content: HttpContent) {
|
fun autoReturn(result: Any?, content: HttpContent) {
|
||||||
log?.debug("{}: autoReturn: {}", content.clientIp, result)
|
log?.debug("{}: autoReturn: {}", content.clientIp, result)
|
||||||
result ?: return
|
result ?: return
|
||||||
@ -294,6 +322,12 @@ open class RoutedHttpHandler(
|
|||||||
is ByteBuffer -> content.finishHtml(result)
|
is ByteBuffer -> content.finishHtml(result)
|
||||||
is ByteArray -> content.finishHtml(result)
|
is ByteArray -> content.finishHtml(result)
|
||||||
is String -> content.finishHtml(result.toByteArray())
|
is String -> content.finishHtml(result.toByteArray())
|
||||||
|
is StringBuffer -> content.finishHtml(result.toString().toByteArray())
|
||||||
|
is StringBuilder -> content.finishHtml(result.toString().toByteArray())
|
||||||
|
is Chunked -> {
|
||||||
|
content.responseHtml()
|
||||||
|
content.finishChunked(result)
|
||||||
|
}
|
||||||
else -> content.finishHtml(result.toString().toByteArray())
|
else -> content.finishHtml(result.toString().toByteArray())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -305,6 +339,12 @@ open class RoutedHttpHandler(
|
|||||||
is ByteBuffer -> content.finishText(result)
|
is ByteBuffer -> content.finishText(result)
|
||||||
is ByteArray -> content.finishText(result)
|
is ByteArray -> content.finishText(result)
|
||||||
is String -> content.finishText(result.toByteArray())
|
is String -> content.finishText(result.toByteArray())
|
||||||
|
is StringBuffer -> content.finishHtml(result.toString().toByteArray())
|
||||||
|
is StringBuilder -> content.finishHtml(result.toString().toByteArray())
|
||||||
|
is Chunked -> {
|
||||||
|
content.responseText()
|
||||||
|
content.finishChunked(result)
|
||||||
|
}
|
||||||
else -> content.finishText(result.toString().toByteArray())
|
else -> content.finishText(result.toString().toByteArray())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -316,7 +356,13 @@ open class RoutedHttpHandler(
|
|||||||
is ByteBuffer -> content.finishJson(result)
|
is ByteBuffer -> content.finishJson(result)
|
||||||
is ByteArray -> content.finishJson(result)
|
is ByteArray -> content.finishJson(result)
|
||||||
is String -> content.finishJson("\"$result\"".toByteArray())
|
is String -> content.finishJson("\"$result\"".toByteArray())
|
||||||
is Byte, Short, Int, Long, Float, Double, Boolean -> content.finishJson(result.toString().toByteArray())
|
is StringBuffer -> content.finishHtml(result.toString().toByteArray())
|
||||||
|
is java.lang.StringBuilder -> content.finishHtml(result.toString().toByteArray())
|
||||||
|
is Number, Boolean -> content.finishJson(result.toString().toByteArray())
|
||||||
|
is Chunked -> {
|
||||||
|
content.responseJson()
|
||||||
|
content.finishChunked(result)
|
||||||
|
}
|
||||||
else -> {
|
else -> {
|
||||||
val json = json?.toJson(result)
|
val json = json?.toJson(result)
|
||||||
log?.debug("{}: finishJson: generate json: {}", content.clientIp, json)
|
log?.debug("{}: finishJson: generate json: {}", content.clientIp, json)
|
||||||
|
3
web/src/main/kotlin/cn/tursom/web/utils/ContextType.kt
Normal file
3
web/src/main/kotlin/cn/tursom/web/utils/ContextType.kt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
package cn.tursom.web.utils
|
||||||
|
|
||||||
|
annotation class ContextType(val type: String)
|
@ -7,9 +7,11 @@ import cn.tursom.web.result.Html
|
|||||||
import cn.tursom.web.result.Json
|
import cn.tursom.web.result.Json
|
||||||
import cn.tursom.web.result.Text
|
import cn.tursom.web.result.Text
|
||||||
import cn.tursom.web.router.impl.SimpleRouter
|
import cn.tursom.web.router.impl.SimpleRouter
|
||||||
|
import cn.tursom.web.utils.ContextType
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
import java.lang.reflect.Method
|
||||||
import kotlin.reflect.KCallable
|
import kotlin.reflect.KCallable
|
||||||
import kotlin.reflect.full.findAnnotation
|
import kotlin.reflect.full.findAnnotation
|
||||||
import kotlin.reflect.jvm.jvmErasure
|
import kotlin.reflect.jvm.jvmErasure
|
||||||
@ -17,7 +19,7 @@ import kotlin.reflect.jvm.jvmErasure
|
|||||||
@Suppress("ProtectedInFinal", "unused", "MemberVisibilityCanBePrivate")
|
@Suppress("ProtectedInFinal", "unused", "MemberVisibilityCanBePrivate")
|
||||||
open class AsyncRoutedHttpHandler(
|
open class AsyncRoutedHttpHandler(
|
||||||
target: Any? = null,
|
target: Any? = null,
|
||||||
routerMaker: () -> Router<Pair<Any?, (HttpContent) -> Unit>> = { SimpleRouter() },
|
routerMaker: () -> Router<Pair<Any?, (HttpContent) -> Any?>> = { SimpleRouter() },
|
||||||
val asyncRouterMaker: () -> Router<Pair<Any?, suspend (HttpContent) -> Unit>> = { SimpleRouter() }
|
val asyncRouterMaker: () -> Router<Pair<Any?, suspend (HttpContent) -> Unit>> = { SimpleRouter() }
|
||||||
) : RoutedHttpHandler(target, routerMaker) {
|
) : RoutedHttpHandler(target, routerMaker) {
|
||||||
protected val asyncRouter: Router<Pair<Any?, suspend (HttpContent) -> Unit>> = asyncRouterMaker()
|
protected val asyncRouter: Router<Pair<Any?, suspend (HttpContent) -> Unit>> = asyncRouterMaker()
|
||||||
@ -113,7 +115,7 @@ open class AsyncRoutedHttpHandler(
|
|||||||
(method as suspend Any.() -> Any?)(obj)?.let { result -> finishJson(result, content) }
|
(method as suspend Any.() -> Any?)(obj)?.let { result -> finishJson(result, content) }
|
||||||
}
|
}
|
||||||
else -> { content ->
|
else -> { content ->
|
||||||
(method as suspend Any.() -> Any?)(obj)?.let { result -> autoReturn(result, content) }
|
(method as suspend Any.() -> Any?)(obj)?.let { result -> autoReturn(method, result, content) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else obj to when (method.returnType.jvmErasure.java) {
|
} else obj to when (method.returnType.jvmErasure.java) {
|
||||||
@ -131,7 +133,7 @@ open class AsyncRoutedHttpHandler(
|
|||||||
(method as suspend Any.(HttpContent) -> Any?)(obj, content)?.let { result -> finishJson(result, content) }
|
(method as suspend Any.(HttpContent) -> Any?)(obj, content)?.let { result -> finishJson(result, content) }
|
||||||
}
|
}
|
||||||
else -> { content ->
|
else -> { content ->
|
||||||
(method as suspend Any.(HttpContent) -> Any?)(obj, content)?.let { result -> autoReturn(result, content) }
|
(method as suspend Any.(HttpContent) -> Any?)(obj, content)?.let { result -> autoReturn(method, result, content) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -226,5 +228,12 @@ open class AsyncRoutedHttpHandler(
|
|||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun autoReturn(method: KCallable<*>, result: Any?, content: HttpContent) {
|
||||||
|
method.findAnnotation<ContextType>()?.let {
|
||||||
|
content.autoContextType(it.type)
|
||||||
|
}
|
||||||
|
autoReturn(result, content)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user