diff --git a/ts-core/ts-async-http/build.gradle.kts b/ts-core/ts-async-http/build.gradle.kts index 522cd35..b325add 100644 --- a/ts-core/ts-async-http/build.gradle.kts +++ b/ts-core/ts-async-http/build.gradle.kts @@ -10,9 +10,9 @@ dependencies { api(project(":ts-core:ts-buffer")) implementation(project(":ts-core:ts-xml")) api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.1") - api(group = "com.squareup.retrofit2", name = "converter-gson", version = "2.9.0") - // https://mvnrepository.com/artifact/com.squareup.retrofit2/retrofit - api(group = "com.squareup.retrofit2", name = "retrofit", version = "2.9.0") + compileOnly("com.squareup.okhttp3:okhttp:4.9.1") + //api(group = "com.squareup.retrofit2", name = "converter-gson", version = "2.9.0") + //api(group = "com.squareup.retrofit2", name = "retrofit", version = "2.9.0") // https://mvnrepository.com/artifact/org.jsoup/jsoup api(group = "org.jsoup", name = "jsoup", version = "1.14.2") diff --git a/ts-core/ts-async-http/src/main/kotlin/cn/tursom/http/BlockingCallAdapterFactory.kt b/ts-core/ts-async-http/src/main/kotlin/cn/tursom/http/BlockingCallAdapterFactory.kt deleted file mode 100644 index c2ad949..0000000 --- a/ts-core/ts-async-http/src/main/kotlin/cn/tursom/http/BlockingCallAdapterFactory.kt +++ /dev/null @@ -1,23 +0,0 @@ -package cn.tursom.http - -import retrofit2.Call -import retrofit2.CallAdapter -import retrofit2.Retrofit -import java.lang.reflect.Type -import java.util.concurrent.CompletableFuture - -object BlockingCallAdapterFactory : CallAdapter.Factory() { - override fun get( - returnType: Type, - annotations: Array<Annotation>, - retrofit: Retrofit - ): CallAdapter<out Any?, out Any?>? { - if (getRawType(returnType) == Call::class.java) return null - if (getRawType(returnType) == CompletableFuture::class.java) return null - if (annotations.any { it is retrofit2.SkipCallbackExecutor }) return null - return object : CallAdapter<Any?, Any?> { - override fun responseType(): Type = returnType - override fun adapt(call: Call<Any?>): Any? = call.execute().body() - } - } -} \ No newline at end of file diff --git a/ts-core/ts-async-http/src/main/kotlin/cn/tursom/http/HtmlConverterFactory.kt b/ts-core/ts-async-http/src/main/kotlin/cn/tursom/http/HtmlConverterFactory.kt deleted file mode 100644 index 4711920..0000000 --- a/ts-core/ts-async-http/src/main/kotlin/cn/tursom/http/HtmlConverterFactory.kt +++ /dev/null @@ -1,53 +0,0 @@ -package cn.tursom.http - -import cn.tursom.core.isInheritanceFrom -import okhttp3.MediaType -import okhttp3.RequestBody -import okhttp3.ResponseBody -import org.jsoup.Jsoup -import org.jsoup.nodes.Document -import org.jsoup.nodes.Node -import retrofit2.Converter -import retrofit2.Retrofit -import java.lang.reflect.Type - -object HtmlConverterFactory : Converter.Factory() { - override fun responseBodyConverter( - type: Type, - annotations: Array<Annotation>, - retrofit: Retrofit - ): Converter<ResponseBody, out Node>? { - return if (type is Class<*> && Document::class.java.isInheritanceFrom(type)) { - DocumentResponseBodyConverter(retrofit.baseUrl().uri().toString()) - } else { - null - } - } - - override fun requestBodyConverter( - type: Type, - parameterAnnotations: Array<Annotation>, - methodAnnotations: Array<Annotation>, - retrofit: Retrofit - ): Converter<in Node, RequestBody>? { - return if (type is Class<*> && type::class.java.isInheritanceFrom(Node::class.java)) { - NodeRequestBodyConverter - } else { - null - } - } - - class DocumentResponseBodyConverter( - private val baseUri: String - ) : Converter<ResponseBody, Document> { - override fun convert(value: ResponseBody): Document { - return Jsoup.parse(value.string(), baseUri) - } - } - - object NodeRequestBodyConverter : Converter<Node, RequestBody> { - override fun convert(value: Node): RequestBody { - return RequestBody.create(MediaType.parse("text/html; charset=utf-8"), value.outerHtml()) - } - } -} \ No newline at end of file diff --git a/ts-core/ts-async-http/src/main/kotlin/cn/tursom/http/HttpRequest.kt b/ts-core/ts-async-http/src/main/kotlin/cn/tursom/http/HttpRequest.kt deleted file mode 100644 index 55b157c..0000000 --- a/ts-core/ts-async-http/src/main/kotlin/cn/tursom/http/HttpRequest.kt +++ /dev/null @@ -1,175 +0,0 @@ -package cn.tursom.http - -import cn.tursom.core.buffer.ByteBuffer -import cn.tursom.core.buffer.impl.HeapByteBuffer -import java.io.InputStream -import java.net.HttpURLConnection -import java.net.URL -import java.net.URLConnection -import java.net.URLEncoder -import java.nio.charset.Charset -import java.util.zip.GZIPInputStream - -@Suppress("unused", "MemberVisibilityCanBePrivate") -object HttpRequest { - val defaultHeader = mapOf( - "accept" to "*/*", - "connection" to "Keep-Alive", - "user-agent" to "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)", - "Accept-Encoding" to "gzip, deflate, sdch, br" - ) - - fun URLConnection.getCharset(): Charset { - val contentType = getHeaderField("content-type") - return if (contentType == null) { - Charsets.UTF_8 - } else { - val startIndex = contentType.indexOf("charset=", ignoreCase = true) + 8 - if (startIndex < 8) { - Charsets.UTF_8 - } else { - var endIndex = contentType.indexOf(";", startIndex = startIndex, ignoreCase = true) - if (endIndex < 0) endIndex = contentType.length - if (startIndex == endIndex) { - Charsets.UTF_8 - } else { - Charset.forName(contentType.substring(startIndex, endIndex)) - } - } - } - } - - fun URLConnection.getRealInputStream(): InputStream { - return if (getHeaderField("content-encoding")?.contains("gzip", true) == true) { - GZIPInputStream(inputStream) - } else { - inputStream - } - } - - fun send( - method: String = "GET", - url: String, - headers: Map<String, String> = defaultHeader, - data: ByteBuffer? = null - ): HttpURLConnection { - val realUrl = URL(url) - val conn = realUrl.openConnection() as HttpURLConnection - headers.forEach { (key, value) -> - conn.setRequestProperty(key, value) - } - if (data != null) conn.doOutput = true - conn.doInput = true - conn.requestMethod = method - - data?.let { - conn.outputStream.use { out -> - data.writeTo(out) - out.flush() - } - } - - return conn - } - - fun send( - method: String = "GET", - url: String, - headers: Map<String, String> = defaultHeader, - data: ByteArray? - ) = send(method, url, headers, data?.let { HeapByteBuffer(data) }) - - fun getContextStream( - method: String = "GET", - url: String, - headers: Map<String, String> = defaultHeader, - data: ByteBuffer? = null - ): InputStream = send(method, url, headers, data).inputStream - - fun getContext( - method: String = "GET", - url: String, - headers: Map<String, String> = defaultHeader, - data: ByteBuffer? = null - ) = send(method, url, headers, data).getRealInputStream().readBytes() - - fun getContextStr( - method: String = "GET", - url: String, - headers: Map<String, String> = defaultHeader, - data: ByteBuffer? = null - ): String { - val conn = send(method, url, headers, data) - return conn.getRealInputStream().readBytes().toString(conn.getCharset()) - } - - fun doGet( - url: String, - param: String? = null, - headers: Map<String, String> = defaultHeader - ): String = getContextStr( - "GET", if (param != null) { - "$url?$param" - } else { - url - }, headers - ) - - infix operator fun get(url: String): String = doGet(url, null) - - fun doGet( - url: String, - param: Map<String, String>, - headers: Map<String, String> = defaultHeader - ): String { - val paramSB = StringBuilder() - return doGet(url, run { - param.forEach { - paramSB.append("${URLEncoder.encode(it.key, "UTF-8")}=${URLEncoder.encode(it.value, "UTF-8")}&") - } - if (paramSB.isNotEmpty()) paramSB.deleteCharAt(paramSB.lastIndex) - paramSB.toString() - }, headers) - } - - fun doPost( - url: String, - data: ByteArray, - headers: Map<String, String> = defaultHeader - ): String = getContextStr("POST", url, headers, HeapByteBuffer(data)) - - fun doPost( - url: String, - param: Map<String, String>, - headers: Map<String, String> = defaultHeader - ): String { - val sb = StringBuilder() - param.forEach { (key, value) -> - sb.append("${URLEncoder.encode(key, "utf-8")}=${URLEncoder.encode(value, "utf-8")}&") - } - if (sb.isNotEmpty()) sb.deleteCharAt(sb.lastIndex) - return doPost(url, sb.toString().toByteArray(), headers) - } - - fun doHead( - url: String, - param: String, - headers: Map<String, String> = defaultHeader - ): Map<String, List<String>> = send("HEAD", "$url?$param", headers).headerFields - - fun doHead( - url: String, - param: Map<String, String>, - headers: Map<String, String> = defaultHeader - ): Map<String, List<String>> { - val paramSB = StringBuilder() - return doHead(url, run { - param.forEach { - paramSB.append("${URLEncoder.encode(it.key, "UTF-8")}=${URLEncoder.encode(it.value, "UTF-8")}&") - } - if (paramSB.isNotEmpty()) paramSB.deleteCharAt(paramSB.lastIndex) - paramSB.toString() - }, headers) - } - -} \ No newline at end of file diff --git a/ts-core/ts-async-http/src/main/kotlin/cn/tursom/http/StringConverterFactory.kt b/ts-core/ts-async-http/src/main/kotlin/cn/tursom/http/StringConverterFactory.kt deleted file mode 100644 index 2baa5a4..0000000 --- a/ts-core/ts-async-http/src/main/kotlin/cn/tursom/http/StringConverterFactory.kt +++ /dev/null @@ -1,47 +0,0 @@ -package cn.tursom.http - -import okhttp3.MediaType -import okhttp3.RequestBody -import okhttp3.ResponseBody -import retrofit2.Converter -import retrofit2.Retrofit -import java.lang.reflect.Type - -object StringConverterFactory : Converter.Factory() { - override fun responseBodyConverter( - type: Type, - annotations: Array<Annotation>, - retrofit: Retrofit - ): Converter<ResponseBody, *>? { - return if (type == String::class.java) { - StringResponseBodyConverter - } else { - null - } - } - - override fun requestBodyConverter( - type: Type, - parameterAnnotations: Array<Annotation>, - methodAnnotations: Array<Annotation>, - retrofit: Retrofit - ): Converter<*, RequestBody>? { - return if (type == String::class.java) { - StringRequestBodyConverter - } else { - null - } - } - - object StringResponseBodyConverter : Converter<ResponseBody, String> { - override fun convert(value: ResponseBody): String? { - return value.string() - } - } - - object StringRequestBodyConverter : Converter<String, RequestBody> { - override fun convert(value: String): RequestBody { - return RequestBody.create(MediaType.parse("text/plain; charset=utf-8"), value) - } - } -} \ No newline at end of file diff --git a/ts-core/ts-async-http/src/main/kotlin/cn/tursom/http/XmlConverterFactory.kt b/ts-core/ts-async-http/src/main/kotlin/cn/tursom/http/XmlConverterFactory.kt deleted file mode 100644 index 6de7522..0000000 --- a/ts-core/ts-async-http/src/main/kotlin/cn/tursom/http/XmlConverterFactory.kt +++ /dev/null @@ -1,51 +0,0 @@ -package cn.tursom.http - -import cn.tursom.core.isInheritanceFrom -import cn.tursom.core.xml.Xml -import okhttp3.MediaType -import okhttp3.RequestBody -import okhttp3.ResponseBody -import org.dom4j.Document -import org.dom4j.Node -import retrofit2.Converter -import retrofit2.Retrofit -import java.lang.reflect.Type - -object XmlConverterFactory : Converter.Factory() { - override fun responseBodyConverter( - type: Type, - annotations: Array<Annotation>, - retrofit: Retrofit - ): Converter<ResponseBody, out Node>? { - return if (type is Class<*> && Document::class.java.isInheritanceFrom(type)) { - DocumentResponseBodyConverter - } else { - null - } - } - - override fun requestBodyConverter( - type: Type, - parameterAnnotations: Array<Annotation>, - methodAnnotations: Array<Annotation>, - retrofit: Retrofit - ): Converter<in Node, RequestBody>? { - return if (type is Class<*> && type.isInheritanceFrom(Node::class.java)) { - NodeRequestBodyConverter - } else { - null - } - } - - object DocumentResponseBodyConverter : Converter<ResponseBody, Document> { - override fun convert(value: ResponseBody): Document { - return Xml.saxReader.read(value.string().reader()) - } - } - - object NodeRequestBodyConverter : Converter<Node, RequestBody> { - override fun convert(value: Node): RequestBody { - return RequestBody.create(MediaType.parse("text/xml; charset=utf-8"), value.asXML()) - } - } -} \ No newline at end of file diff --git a/ts-core/ts-async-http/src/main/kotlin/cn/tursom/utils/AsyncHttpRequest.kt b/ts-core/ts-async-http/src/main/kotlin/cn/tursom/http/client/AsyncHttpRequest.kt similarity index 89% rename from ts-core/ts-async-http/src/main/kotlin/cn/tursom/utils/AsyncHttpRequest.kt rename to ts-core/ts-async-http/src/main/kotlin/cn/tursom/http/client/AsyncHttpRequest.kt index 2f5f4b5..e6a615f 100644 --- a/ts-core/ts-async-http/src/main/kotlin/cn/tursom/utils/AsyncHttpRequest.kt +++ b/ts-core/ts-async-http/src/main/kotlin/cn/tursom/http/client/AsyncHttpRequest.kt @@ -1,6 +1,7 @@ -package cn.tursom.utils +package cn.tursom.http.client import okhttp3.* +import okhttp3.MediaType.Companion.toMediaTypeOrNull import java.io.File import java.io.IOException import java.net.InetSocketAddress @@ -11,7 +12,6 @@ import kotlin.coroutines.resume import kotlin.coroutines.resumeWithException import kotlin.coroutines.suspendCoroutine - @Suppress("unused", "MemberVisibilityCanBePrivate") object AsyncHttpRequest { val defaultClient: OkHttpClient = OkHttpClient().newBuilder() @@ -106,7 +106,7 @@ object AsyncHttpRequest { client: OkHttpClient = defaultClient ) = post( url, - RequestBody.create(MediaType.parse("text/plain;charset=utf-8"), body), + RequestBody.create("text/plain;charset=utf-8".toMediaTypeOrNull(), body), headers, client ) @@ -118,7 +118,7 @@ object AsyncHttpRequest { client: OkHttpClient = defaultClient ) = post( url, - RequestBody.create(MediaType.parse("application/octet-stream"), body), + RequestBody.create("application/octet-stream".toMediaTypeOrNull(), body), headers, client ) @@ -130,7 +130,7 @@ object AsyncHttpRequest { client: OkHttpClient = defaultClient ) = post( url, - RequestBody.create(MediaType.parse("application/octet-stream"), body), + RequestBody.create("application/octet-stream".toMediaTypeOrNull(), body), headers, client ) @@ -151,7 +151,7 @@ object AsyncHttpRequest { client: OkHttpClient ): String { val response = get(url, param, headers, client) - return response.body()!!.string() + return response.body!!.string() } @Suppress("BlockingMethodInNonBlockingContext") @@ -160,7 +160,7 @@ object AsyncHttpRequest { body: RequestBody, headers: Map<String, String>? = null, client: OkHttpClient - ): String = post(url, body, headers, client).body()!!.string() + ): String = post(url, body, headers, client).body!!.string() suspend fun postStr( url: String, @@ -196,7 +196,7 @@ object AsyncHttpRequest { client: OkHttpClient ): String = postStr( url, - RequestBody.create(MediaType.parse("text/plain;charset=utf-8"), body), + RequestBody.create("text/plain;charset=utf-8".toMediaTypeOrNull(), body), headers, client ) @@ -215,7 +215,7 @@ object AsyncHttpRequest { client: OkHttpClient ): String = postStr( url, - RequestBody.create(MediaType.parse("application/octet-stream"), body), + RequestBody.create("application/octet-stream".toMediaTypeOrNull(), body), headers, client ) @@ -237,7 +237,7 @@ object AsyncHttpRequest { param: Map<String, String>? = null, headers: Map<String, String>? = null, client: OkHttpClient - ): ByteArray = get(url, param, headers, client).body()!!.bytes() + ): ByteArray = get(url, param, headers, client).body!!.bytes() @Suppress("BlockingMethodInNonBlockingContext") @@ -246,7 +246,7 @@ object AsyncHttpRequest { body: RequestBody, headers: Map<String, String>? = null, client: OkHttpClient - ): ByteArray = post(url, body, headers, client).body()!!.bytes() + ): ByteArray = post(url, body, headers, client).body!!.bytes() suspend fun postByteArray( @@ -291,7 +291,7 @@ object AsyncHttpRequest { client: OkHttpClient ): ByteArray = postByteArray( url, - RequestBody.create(MediaType.parse("text/plain;charset=utf-8"), body), + RequestBody.create("text/plain;charset=utf-8".toMediaTypeOrNull(), body), headers, client ) @@ -314,8 +314,8 @@ object AsyncHttpRequest { client: OkHttpClient ): ByteArray = postByteArray( url, - RequestBody.create(MediaType.parse("application/octet-stream"), body), + RequestBody.create("application/octet-stream".toMediaTypeOrNull(), body), headers, client ) -} +} \ No newline at end of file diff --git a/ts-core/ts-ws-client/build.gradle.kts b/ts-core/ts-ws-client/build.gradle.kts index 52bb39f..525eced 100644 --- a/ts-core/ts-ws-client/build.gradle.kts +++ b/ts-core/ts-ws-client/build.gradle.kts @@ -9,7 +9,7 @@ dependencies { api(project(":ts-core:ts-buffer")) api(project(":ts-core:ts-log")) compileOnly(project(":ts-socket")) - api(group = "io.netty", name = "netty-all", version = "4.1.67.Final") + api(group = "io.netty", name = "netty-all", version = nettyVersion) } diff --git a/ts-web/ts-web-netty/src/main/kotlin/cn/tursom/web/netty/NettyHttpContent.kt b/ts-web/ts-web-netty/src/main/kotlin/cn/tursom/web/netty/NettyHttpContent.kt index 56ee1d3..a467a60 100644 --- a/ts-web/ts-web-netty/src/main/kotlin/cn/tursom/web/netty/NettyHttpContent.kt +++ b/ts-web/ts-web-netty/src/main/kotlin/cn/tursom/web/netty/NettyHttpContent.kt @@ -150,8 +150,10 @@ open class NettyHttpContent( if (log.traceEnabled) { log?.trace("write {}", message) } - getResponseBodyBuf().addComponent(Unpooled.wrappedBuffer(message.toByteArray())) - //responseBody.write(message.toByteArray()) + val compositeByteBuf = getResponseBodyBuf() + val bytes = message.toByteArray() + compositeByteBuf.addComponent(Unpooled.wrappedBuffer(bytes)) + compositeByteBuf.writerIndex(compositeByteBuf.writerIndex() + bytes.size) } override fun write(byte: Byte) { diff --git a/ts-web/ts-web-netty/src/main/kotlin/cn/tursom/web/netty/TimeoutHandler.kt b/ts-web/ts-web-netty/src/main/kotlin/cn/tursom/web/netty/TimeoutHandler.kt new file mode 100644 index 0000000..8d207b1 --- /dev/null +++ b/ts-web/ts-web-netty/src/main/kotlin/cn/tursom/web/netty/TimeoutHandler.kt @@ -0,0 +1,17 @@ +package cn.tursom.web.netty + +import io.netty.channel.ChannelHandler +import io.netty.channel.ChannelHandlerContext +import io.netty.handler.timeout.TimeoutException + +@ChannelHandler.Sharable +object TimeoutHandler : ChannelHandler { + override fun handlerAdded(ctx: ChannelHandlerContext?) {} + override fun handlerRemoved(ctx: ChannelHandlerContext?) {} + override fun exceptionCaught(ctx: ChannelHandlerContext, cause: Throwable?) { + when (cause) { + is TimeoutException -> ctx.close() + else -> ctx.fireExceptionCaught(cause) + } + } +} \ No newline at end of file