httpserver

This commit is contained in:
lushunming 2025-07-21 10:58:51 +08:00
parent a1f8601481
commit 078f8a701e
5 changed files with 68 additions and 67 deletions

View File

@ -87,7 +87,8 @@ dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1"
// https://mvnrepository.com/artifact/com.sun.net.httpserver/http // https://mvnrepository.com/artifact/com.sun.net.httpserver/http
implementation("com.sun.net.httpserver:http:20070405") // implementation("com.sun.net.httpserver:http:20070405")
implementation("com.hibegin:simplewebserver:0.3.175")
//implementation 'wang.harlon.quickjs:wrapper-java:1.0.0' //implementation 'wang.harlon.quickjs:wrapper-java:1.0.0'
// implementation(ext: 'aar', name: 'quickjs', group: 'fongmi', version: 'release') // implementation(ext: 'aar', name: 'quickjs', group: 'fongmi', version: 'release')

View File

@ -1,97 +1,85 @@
package com.github.catvod.utils package com.github.catvod.utils
import com.github.catvod.crawler.SpiderDebug import com.github.catvod.crawler.SpiderDebug
import com.github.catvod.net.OkHttp import com.github.catvod.net.OkHttp
import com.google.gson.Gson import com.google.gson.Gson
import com.sun.net.httpserver.HttpExchange import com.hibegin.http.server.SimpleWebServer
import com.sun.net.httpserver.HttpServer import com.hibegin.http.server.WebServerBuilder
import com.hibegin.http.server.api.HttpRequest
import com.hibegin.http.server.api.HttpResponse
import com.hibegin.http.server.config.ServerConfig
import com.hibegin.http.server.util.StatusCodeUtil
import com.hibegin.http.server.web.Controller
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.Headers
import java.io.BufferedOutputStream
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.OutputStreamWriter
import java.net.InetSocketAddress
import java.nio.charset.Charset import java.nio.charset.Charset
object ProxyServer { object ProxyServer {
private val THREAD_NUM = Runtime.getRuntime().availableProcessors() * 2 private val THREAD_NUM = Runtime.getRuntime().availableProcessors() * 2
private const val partSize = 1024 * 1024 * 1 private const val partSize = 1024 * 1024 * 1
private var port = 0 private var port = 12345
private var httpServer: HttpServer? = null private var httpServer: SimpleWebServer? = null
private val infos = mutableMapOf<String, MutableMap<String, MutableList<String>>>(); private val infos = mutableMapOf<String, MutableMap<String, MutableList<String>>>();
fun stop() { fun stop() {
httpServer?.stop(1_000) httpServer?.destroy()
} }
fun start() { fun start() {
try { try {
httpServer = HttpServer.create(InetSocketAddress(port), 100); val serverConfig = ServerConfig()
httpServer?.createContext("/") { httpExchange -> serverConfig.port = port
run { serverConfig.router.addMapper("/proxy", ProxyController::class.java)
httpExchange.sendResponseHeaders(200, "server running ".length.toLong()); val builder = WebServerBuilder.Builder().serverConfig(serverConfig).build()
val os = httpExchange.responseBody builder.startWithThread()
val writer = OutputStreamWriter(os, Charset.defaultCharset()) httpServer = builder.webServer
writer.write("server running ")
writer.close()
os.close()
httpExchange.close()
}
}
httpServer?.createContext("/proxy") { httpExchange ->
run {
val params = queryToMap(httpExchange.requestURI.query)
val url = Util.base64Decode(params?.get("url"))
val header: Map<String, String> = Gson().fromJson<Map<String, String>>(
Util.base64Decode(params?.get("headers")), MutableMap::class.java
)
CoroutineScope(Dispatchers.IO).launch {
proxyAsync(
url, header, httpExchange
)
}
}
}
httpServer?.executor = null;
httpServer?.start();
} catch (e: Exception) { } catch (e: Exception) {
SpiderDebug.log("start server e:" + e.message) SpiderDebug.log("start server e:" + e.message)
e.printStackTrace() e.printStackTrace()
httpServer?.stop(1000) httpServer?.destroy()
} }
port = httpServer?.address?.port!! SpiderDebug.log("Server start on " + port)
SpiderDebug.log("ktorServer start on " + port)
} }
class ProxyController : Controller() {
fun index() {
val url = Util.base64Decode(getRequest().getParaToStr("url"))
val header: Map<String, String> = Gson().fromJson<Map<String, String>>(
Util.base64Decode(getRequest().getParaToStr("headers")), MutableMap::class.java
)
CoroutineScope(Dispatchers.IO).launch {
proxyAsync(
url, header, getRequest(), getResponse()
)
}
}
}
private suspend fun proxyAsync( private suspend fun proxyAsync(
url: String, headers: Map<String, String>, httpExchange: HttpExchange url: String, headers: Map<String, String>, request: HttpRequest, response: HttpResponse
) { ) {
val channels = List(THREAD_NUM) { Channel<ByteArray>() } val channels = List(THREAD_NUM) { Channel<ByteArray>() }
val outputStream = httpExchange.responseBody var outputStream = ByteArrayOutputStream()
val bufferedOutputStream = BufferedOutputStream(outputStream)
try { try {
SpiderDebug.log("--proxyMultiThread: THREAD_NUM: $THREAD_NUM") SpiderDebug.log("--proxyMultiThread: THREAD_NUM: $THREAD_NUM")
var rangeHeader = httpExchange.requestHeaders.getFirst("Range") var rangeHeader = request.getHeader("Range")
//没有range头 //没有range头
if (rangeHeader.isNullOrEmpty()) { if (rangeHeader.isNullOrEmpty()) {
// 处理初始请求 // 处理初始请求
@ -118,15 +106,22 @@ object ProxyServer {
SpiderDebug.log("contentLength: $contentLength") SpiderDebug.log("contentLength: $contentLength")
val finalEndPoint = if (endPoint == -1L) contentLength - 1 else endPoint val finalEndPoint = if (endPoint == -1L) contentLength - 1 else endPoint
httpExchange.responseHeaders.apply {
set("Connection", "keep-alive")
set("Content-Length", (finalEndPoint - startPoint + 1).toString())
set("Content-Range", "bytes $startPoint-$finalEndPoint/$contentLength")
set("Content-Type", info["Content-Type"]?.get(0))
}
httpExchange.sendResponseHeaders(206, 0)
// 使用流式响应 response.addHeader("Connection", "keep-alive")
response.addHeader("Content-Length", (finalEndPoint - startPoint + 1).toString())
response.addHeader("Content-Range", "bytes $startPoint-$finalEndPoint/$contentLength")
response.addHeader("Content-Type", info["Content-Type"]?.get(0))
val statusCode = 206
val sb = StringBuilder();
sb.append(request.httpVersion.value).append(" ").append(statusCode).append(" ")
.append(StatusCodeUtil.getStatusCodeDesc(statusCode)).append("\r\n")
for ((key, value) in response.header) {
sb.append(key).append(": ").append(value).append("\r\n")
}
sb.append("\r\n")
outputStream.write(sb.toString().toByteArray(Charset.defaultCharset()))
outputStream.flush()
response.send(outputStream, false)
var currentStart = startPoint var currentStart = startPoint
@ -141,7 +136,9 @@ object ProxyServer {
for (i in 0 until THREAD_NUM) { for (i in 0 until THREAD_NUM) {
if (currentStart > finalEndPoint) break if (currentStart > finalEndPoint) {
break
}
val chunkStart = currentStart val chunkStart = currentStart
val chunkEnd = minOf(currentStart + partSize - 1, finalEndPoint) val chunkEnd = minOf(currentStart + partSize - 1, finalEndPoint)
producerJob += CoroutineScope(Dispatchers.IO).launch { producerJob += CoroutineScope(Dispatchers.IO).launch {
@ -156,24 +153,27 @@ object ProxyServer {
val data = channels[index].receive() val data = channels[index].receive()
SpiderDebug.log("Received chunk: ${data.size} bytes") SpiderDebug.log("Received chunk: ${data.size} bytes")
outputStream = ByteArrayOutputStream();
outputStream.write(data);
outputStream.flush()
response.send(outputStream, false)
// bufferedOutputStream.close()
bufferedOutputStream.write(data)
bufferedOutputStream.flush()
} }
} }
response.send(ByteArrayOutputStream(), true)
} catch (e: Exception) { } catch (e: Exception) {
SpiderDebug.log("error: ${e.message}") SpiderDebug.log("error: ${e.message}")
outputStream.write("error: ${e.message}".toByteArray()) e.printStackTrace()
} finally { } finally {
channels.forEach { it.close() } channels.forEach { it.close() }
bufferedOutputStream.close()
outputStream.close()
httpExchange.close()
} }
} }

Binary file not shown.

View File

@ -1 +1 @@
2b55efe5a17ba216363ab2bef3b69d05 ba2f976b1ed8413cb45d6798af4b5b55

View File

@ -1,5 +1,5 @@
{ {
"spider": "https://gh.llkk.cc/https://raw.githubusercontent.com/lushunming/AndroidCatVodSpider/multiThreadNew/jar/custom_spider.jar;md5;2b55efe5a17ba216363ab2bef3b69d05", "spider": "https://gh.llkk.cc/https://raw.githubusercontent.com/lushunming/AndroidCatVodSpider/multiThreadNew/jar/custom_spider.jar;md5;ba2f976b1ed8413cb45d6798af4b5b55",
"lives": [ "lives": [
{ {
"name": "电视直播", "name": "电视直播",