增强web

This commit is contained in:
tursom 2019-12-15 01:12:02 +08:00
parent e4c6b5657e
commit 85291d9f59
10 changed files with 84 additions and 481 deletions

View File

@ -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>>>
}

View File

@ -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
}
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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()
}
}

View File

@ -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
}
}
}

View File

@ -0,0 +1,3 @@
package cn.tursom.web.router
annotation class BlockHandler

View File

@ -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)

View File

@ -0,0 +1,3 @@
package cn.tursom.web.utils
annotation class ContextType(val type: String)

View File

@ -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)
}
} }
} }