AsyncRoutedHttpHandler 添加对协程的支持

This commit is contained in:
tursom 2019-12-14 17:16:53 +08:00
parent 024dd8c7cf
commit 597b94f12d
3 changed files with 107 additions and 68 deletions

View File

@ -16,6 +16,7 @@ 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 kotlin.reflect.KCallable
/** /**
* 自动添加路径映射的处理器 * 自动添加路径映射的处理器
@ -97,44 +98,54 @@ open class RoutedHttpHandler(
} }
protected fun insertMapping(obj: Any, method: Method) { protected fun insertMapping(obj: Any, method: Method) {
val mapping = obj::class.java.getAnnotation(Mapping::class.java)?.route ?: arrayOf("")
method.annotations.forEach { annotation -> method.annotations.forEach { annotation ->
log?.info("method route {} annotation {}", method, annotation) log?.info("method route {} annotation {}", method, annotation)
val (routes, router) = getRoutes(annotation) ?: return@forEach val (routes, router) = getRoutes(annotation) ?: return@forEach
log?.info("method route {} mapped to {}", method, routes) log?.info("method route {} mapped to {}", method, routes)
routes.forEach { route -> routes.forEach { route ->
router[safeRoute(route)] = if (method.parameterTypes.isEmpty()) { if (mapping.isEmpty()) {
obj to when { addRouter(obj, method, route, router)
method.getAnnotation(Html::class.java) != null -> { content -> } else mapping.forEach {
method(obj)?.let { result -> finishHtml(result, content) } val base = safeRoute(it)
} addRouter(obj, method, base + route, router)
method.getAnnotation(Text::class.java) != null -> { content -> }
method(obj)?.let { result -> finishText(result, content) } }
} }
method.getAnnotation(Json::class.java) != null -> { content -> }
method(obj)?.let { result -> finishJson(result, content) }
} fun addRouter(obj: Any, method: Method, route: String, router: Router<Pair<Any?, (HttpContent) -> Unit>>) {
else -> { content -> router[safeRoute(route)] = if (method.parameterTypes.isEmpty()) {
method(obj)?.let { result -> autoReturn(result, content) } obj to when {
} method.getAnnotation(Html::class.java) != null -> { content ->
} method(obj)?.let { result -> finishHtml(result, content) }
} else obj to when (method.returnType) { }
Void::class.java -> { content -> method(obj, content) } method.getAnnotation(Text::class.java) != null -> { content ->
Void.TYPE -> { content -> method(obj, content) } method(obj)?.let { result -> finishText(result, content) }
Unit::class.java -> { content -> method(obj, content) } }
else -> when { method.getAnnotation(Json::class.java) != null -> { content ->
method.getAnnotation(Html::class.java) != null -> { content -> method(obj)?.let { result -> finishJson(result, content) }
method(obj, content)?.let { result -> finishHtml(result, content) } }
} else -> { content ->
method.getAnnotation(Text::class.java) != null -> { content -> method(obj)?.let { result -> autoReturn(result, content) }
method(obj, content)?.let { result -> finishText(result, content) } }
} }
method.getAnnotation(Json::class.java) != null -> { content -> } else obj to when (method.returnType) {
method(obj, content)?.let { result -> finishJson(result, content) } Void::class.java -> { content -> method(obj, content) }
} Void.TYPE -> { content -> method(obj, content) }
else -> { content -> Unit::class.java -> { content -> method(obj, content) }
method(obj, content)?.let { result -> autoReturn(result, content) } else -> when {
} method.getAnnotation(Html::class.java) != null -> { content ->
} method(obj, content)?.let { result -> finishHtml(result, content) }
}
method.getAnnotation(Text::class.java) != null -> { content ->
method(obj, content)?.let { result -> finishText(result, content) }
}
method.getAnnotation(Json::class.java) != null -> { content ->
method(obj, content)?.let { result -> finishJson(result, content) }
}
else -> { content ->
method(obj, content)?.let { result -> autoReturn(result, content) }
} }
} }
} }
@ -231,15 +242,25 @@ open class RoutedHttpHandler(
null null
} }
fun <T> T.repeatUntil(state: (T) -> Boolean, block: (T) -> T): T {
var result = this
while (state(result)) {
result = block(result)
}
return result
}
fun safeRoute(route: String) = ( fun safeRoute(route: String) = (
if (route.startsWith('/')) route else "/$route").let { if (route.startsWith('/')) route else "/$route").let {
if (it.endsWith('/')) it.dropLast(1) else it if (it.endsWith('/')) it.dropLast(1) else it
} }.repeatUntil({ it.contains("//") }) { it.replace("//", "/") }
fun autoReturn(result: Any, content: HttpContent) { fun autoReturn(result: Any, content: HttpContent) {
log?.debug("{}: autoReturn: {}", content.clientIp, result) log?.debug("{}: autoReturn: {}", content.clientIp, result)
when (result) { when (result) {
is String -> content.finishText(result.toByteArray()) is String -> content.finishText(result.toByteArray())
is StringBuilder -> content.finishText(result.toString().toByteArray())
is StringBuffer -> content.finishText(result.toString().toByteArray())
is ByteArray -> content.finishText(result) is ByteArray -> content.finishText(result)
is File -> { is File -> {
content.autoContextType(result.name) content.autoContextType(result.name)

View File

@ -0,0 +1,7 @@
dependencies {
implementation project(":web")
api project(":json")
api group: 'org.slf4j', name: 'slf4j-api', version: '1.7.29'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1'
compile group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: '1.3.61'
}

View File

@ -80,51 +80,62 @@ open class AsyncRoutedHttpHandler(
} }
} }
@Suppress("UNCHECKED_CAST")
protected fun insertMapping(obj: Any, method: KCallable<*>) { protected fun insertMapping(obj: Any, method: KCallable<*>) {
val mapping = obj::class.java.getAnnotation(Mapping::class.java)?.route ?: arrayOf("")
method.annotations.forEach { annotation -> method.annotations.forEach { annotation ->
log?.info("method route {} annotation {}", method, annotation) log?.info("method route {} annotation {}", method, annotation)
val (routes, router) = getAsyncRoutes(annotation) ?: return@forEach val (routes, router) = getAsyncRoutes(annotation) ?: return@forEach
log?.info("method route {} mapped to {}", method, routes) log?.info("method route {} mapped to {}", method, routes)
routes.forEach { route -> routes.forEach { route ->
router[safeRoute(route)] = if (method.parameters.size == 1) { if (mapping.isEmpty()) {
obj to when { addRouter(obj, method, route, router)
method.findAnnotation<Html>() != null -> { content -> } else mapping.forEach {
(method as suspend Any.() -> Any?)(obj)?.let { result -> finishHtml(result, content) } val base = safeRoute(it)
} addRouter(obj, method, base + route, router)
method.findAnnotation<Text>() != null -> { content ->
(method as suspend Any.() -> Any?)(obj)?.let { result -> finishText(result, content) }
}
method.findAnnotation<Json>() != null -> { content ->
(method as suspend Any.() -> Any?)(obj)?.let { result -> finishJson(result, content) }
}
else -> { content ->
(method as suspend Any.() -> Any?)(obj)?.let { result -> autoReturn(result, content) }
}
}
} else obj to when (method.returnType) {
Void::class.java -> method as suspend (HttpContent) -> Unit
Void.TYPE -> method as suspend (HttpContent) -> Unit
Unit::class.java -> method as suspend (HttpContent) -> Unit
else -> when {
method.findAnnotation<Html>() != null -> { content ->
(method as suspend Any.(HttpContent) -> Any?)(obj, content)?.let { result -> finishHtml(result, content) }
}
method.findAnnotation<Text>() != null -> { content ->
(method as suspend Any.(HttpContent) -> Any?)(obj, content)?.let { result -> finishText(result, content) }
}
method.findAnnotation<Json>() != null -> { content ->
(method as suspend Any.(HttpContent) -> Any?)(obj, content)?.let { result -> finishJson(result, content) }
}
else -> { content ->
(method as suspend Any.(HttpContent) -> Any?)(obj, content)?.let { result -> autoReturn(result, content) }
}
}
} }
} }
} }
} }
@Suppress("UNCHECKED_CAST")
fun addRouter(obj: Any, method: KCallable<*>, route: String, router: Router<Pair<Any?, suspend (HttpContent) -> Unit>>) {
router[safeRoute(route)] = if (method.parameters.size == 1) {
obj to when {
method.findAnnotation<Html>() != null -> { content ->
(method as suspend Any.() -> Any?)(obj)?.let { result -> finishHtml(result, content) }
}
method.findAnnotation<Text>() != null -> { content ->
(method as suspend Any.() -> Any?)(obj)?.let { result -> finishText(result, content) }
}
method.findAnnotation<Json>() != null -> { content ->
(method as suspend Any.() -> Any?)(obj)?.let { result -> finishJson(result, content) }
}
else -> { content ->
(method as suspend Any.() -> Any?)(obj)?.let { result -> autoReturn(result, content) }
}
}
} else obj to when (method.returnType) {
Void::class.java -> method as suspend (HttpContent) -> Unit
Void.TYPE -> method as suspend (HttpContent) -> Unit
Unit::class.java -> method as suspend (HttpContent) -> Unit
else -> when {
method.findAnnotation<Html>() != null -> { content ->
(method as suspend Any.(HttpContent) -> Any?)(obj, content)?.let { result -> finishHtml(result, content) }
}
method.findAnnotation<Text>() != null -> { content ->
(method as suspend Any.(HttpContent) -> Any?)(obj, content)?.let { result -> finishText(result, content) }
}
method.findAnnotation<Json>() != null -> { content ->
(method as suspend Any.(HttpContent) -> Any?)(obj, content)?.let { result -> finishJson(result, content) }
}
else -> { content ->
(method as suspend Any.(HttpContent) -> Any?)(obj, content)?.let { result -> autoReturn(result, content) }
}
}
}
}
protected fun getAsyncRoutes(annotation: Annotation) = when (annotation) { protected fun getAsyncRoutes(annotation: Annotation) = when (annotation) {
is Mapping -> { is Mapping -> {
annotation.route to getAsyncRouter(annotation.method) annotation.route to getAsyncRouter(annotation.method)