refactor: 优化 HttpService 代码,修复类型问题和改进代码质量

main
mojo 1 month ago
parent efe559c6d2
commit fbc899a824

@ -19,7 +19,6 @@ import com.example.utils.toJsonString
import com.example.utils.toJsonString1 import com.example.utils.toJsonString1
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.net.URI import java.net.URI
@ -34,136 +33,220 @@ class HttpService(
companion object { companion object {
private const val MAX_REDIRECT_COUNT = 30 private const val MAX_REDIRECT_COUNT = 30
val pattern = Regex("^[a-zA-Z0-9]+://.*") private val HTTP_REDIRECT_PATTERN = Regex("^[a-zA-Z0-9]+://.*")
private val HTTP_STATUS_SUCCESS = 100 until 300
private val HTTP_STATUS_REDIRECT = 300 until 400
} }
// ==================== 主执行方法 ====================
override suspend fun execute(onFinish: (List<ActionExec>) -> Unit) = override suspend fun execute(onFinish: (List<ActionExec>) -> Unit) =
withContext(Dispatchers.Default) { withContext(Dispatchers.Default) {
val actionExecList = mutableListOf<ActionExec>() val actionExecList = mutableListOf<ActionExec>()
var currentStep = taskConfig.currentStep val currentStep = taskConfig.currentStep
try { try {
LogUtils.info("action delay: ${action.delay} s, it's async: ${action.async}") handleActionDelay()
val actionRequest = action.request ?: throw NullPointerException("request is null")
amendActionRequest(actionRequest)
val httpRequest = actionRequest.buildHttpRequest()
val result = if (action.async) {
handleAsyncRequest(httpRequest, actionExecList)
} else {
handleSyncRequest(httpRequest, actionExecList)
}
updateTaskStep(result, currentStep, httpRequest)
} catch (e: Exception) {
LogUtils.error(throwable = e)
handleException(e, actionExecList, currentStep)
}
LogUtils.info("finish action: ${action.request?.url}")
onFinish(actionExecList)
}
// ==================== 请求处理 ====================
private suspend fun handleActionDelay() {
if (action.delay > 0) { if (action.delay > 0) {
LogUtils.info("action delay: ${action.delay} s, it's async: ${action.async}")
delay(action.delay.toDuration(DurationUnit.SECONDS)) delay(action.delay.toDuration(DurationUnit.SECONDS))
} }
val actionRequest = action.request ?: throw NullPointerException("request is null") }
amendActionRequest(actionRequest)
var httpRequest = actionRequest.buildHttpRequest() private data class RequestResult(
var httpResponse: Response? = null val proceedTask: Boolean,
var proceedTask = false val httpResponse: Response?
var stepCallHttpCount = 1 )
if (action.async) {
private fun handleAsyncRequest(
httpRequest: Request,
actionExecList: MutableList<ActionExec>
): RequestResult {
httpRequest.makeAsyncRequest() httpRequest.makeAsyncRequest()
val actionExec = httpRequest.genActionExec(null, stepCallHttpCount) val actionExec = httpRequest.genActionExec(null, 1)
actionExec.respCode = ASYNC_EXEC_CODE actionExec.respCode = ASYNC_EXEC_CODE
actionExecList += actionExec actionExecList += actionExec
proceedTask = true return RequestResult(proceedTask = true, httpResponse = null)
} else { }
httpResponse = httpRequest.call()
val actionExec = httpRequest.genActionExec(httpResponse, stepCallHttpCount) private suspend fun handleSyncRequest(
httpRequest: Request,
actionExecList: MutableList<ActionExec>
): RequestResult {
val request = httpRequest
val response = request.call()
val stepCallHttpCount = 1
val actionExec = request.genActionExec(response, stepCallHttpCount)
actionExecList += actionExec actionExecList += actionExec
when (httpResponse.code) {
in 100 until 300 -> { return when (response.code) {
proceedTask = true in HTTP_STATUS_SUCCESS -> {
RequestResult(proceedTask = true, httpResponse = response)
} }
in 300 until 400 -> { in HTTP_STATUS_REDIRECT -> {
var redirectUrl: String? = handleRedirects(request, response, actionExecList).let { redirectResult ->
httpResponse.headers.get( RequestResult(
HttpClient.Params.REQUEST_HEADER_LOCATION, proceedTask = redirectResult.proceedTask,
ignoreCase = true httpResponse = redirectResult.response
)?.firstOrNull()
while ((isActive && httpResponse != null &&
httpResponse.code in (300 until 400) &&
stepCallHttpCount <= MAX_REDIRECT_COUNT &&
!redirectUrl.isNullOrBlank()) &&
redirectUrl.isHttpRedirect()
) {
runCatching {
stepCallHttpCount++
httpRequest = httpRequest.buildRedirectHttpRequest(
redirectUrl!!,
httpRequest.url
)
httpResponse = httpRequest.call()
if (httpResponse!!.code !in 300 until 400) {
if (!httpResponse?.headers.isNullOrEmpty()) {
httpResponse?.headers =
httpResponse!!.headers.toMutableMap().apply {
put(
HttpClient.Params.REQUEST_HEADER_REFERER,
listOf(
httpRequest.headers.getOrDefault(
HttpClient.Params.REQUEST_HEADER_REFERER,
ignoreCase = true,
""
)
)
) )
} }
} }
else -> {
RequestResult(proceedTask = action.skipError, httpResponse = response)
}
}
} }
redirectUrl = private data class RedirectResult(
httpResponse?.headers?.get( val proceedTask: Boolean,
HttpClient.Params.REQUEST_HEADER_LOCATION, val response: Response?
ignoreCase = true )
)?.firstOrNull()
private suspend fun handleRedirects(
initialRequest: Request,
initialResponse: Response,
actionExecList: MutableList<ActionExec>
): RedirectResult {
var request = initialRequest
var response = initialResponse
var stepCallHttpCount = 1
var redirectUrl = getRedirectUrl(response)
while (shouldContinueRedirect(response, redirectUrl, stepCallHttpCount)) {
stepCallHttpCount++
runCatching {
request = request.buildRedirectHttpRequest(redirectUrl!!, request.url)
response = request.call()
if (response.code !in HTTP_STATUS_REDIRECT) {
updateResponseReferer(request, response)
}
redirectUrl = getRedirectUrl(response)
LogUtils.info("redirectUrl: $redirectUrl") LogUtils.info("redirectUrl: $redirectUrl")
proceedTask = true
httpRequest.genActionExec(httpResponse, stepCallHttpCount) request.genActionExec(response, stepCallHttpCount)
}.onFailure { e -> }.onFailure { e ->
LogUtils.error(throwable = e) LogUtils.error(throwable = e)
proceedTask = action.skipError val errorExec = request.genActionExec(null, stepCallHttpCount).apply {
actionExecList += httpRequest.genActionExec( respCode = HttpClient.ErrorCode.ERROR_CODE_HTTP_BUILD_CONNECTION_FAILED
null, stepCallHttpCount
).apply {
respCode =
HttpClient.ErrorCode.ERROR_CODE_HTTP_BUILD_CONNECTION_FAILED
} }
}.onSuccess { actionExecList += errorExec
actionExecList += it return RedirectResult(proceedTask = action.skipError, response = null)
}.onSuccess { actionExec ->
actionExecList += actionExec
} }
} }
return RedirectResult(proceedTask = true, response = response)
} }
else -> { private fun shouldContinueRedirect(
proceedTask = action.skipError response: Response?,
redirectUrl: String?,
stepCallHttpCount: Int
): Boolean {
return response != null &&
response.code in HTTP_STATUS_REDIRECT &&
stepCallHttpCount <= MAX_REDIRECT_COUNT &&
!redirectUrl.isNullOrBlank() &&
redirectUrl.isHttpRedirect()
}
private fun getRedirectUrl(response: Response?): String? {
return response?.headers?.get(
HttpClient.Params.REQUEST_HEADER_LOCATION,
ignoreCase = true
)?.firstOrNull()
}
private fun updateResponseReferer(request: Request, response: Response) {
if (response.headers.isNotEmpty()) {
response.headers = response.headers.toMutableMap().apply {
put(
HttpClient.Params.REQUEST_HEADER_REFERER,
listOf(
request.headers.getOrDefault(
HttpClient.Params.REQUEST_HEADER_REFERER,
ignoreCase = true,
""
)
)
)
} }
} }
} }
if (proceedTask) { private fun updateTaskStep(
httpResponse?.apply { result: RequestResult,
extractResponseVariableToCache(action, httpRequest, httpResponse) currentStep: Int,
httpRequest: Request
) {
if (result.proceedTask) {
result.httpResponse?.let { response ->
extractResponseVariableToCache(action, httpRequest, response)
val nextStep = action.next.httpGetNextStepIndex( val nextStep = action.next.httpGetNextStepIndex(
httpRequest, httpResponse, currentStep httpRequest, response, currentStep
) )
taskConfig.currentStep = nextStep taskConfig.currentStep = nextStep
} ?: let { } ?: run {
taskConfig.currentStep = ++currentStep taskConfig.currentStep = currentStep + 1
} }
} else { } else {
taskConfig.currentStep = Int.MAX_VALUE taskConfig.currentStep = Int.MAX_VALUE
} }
} catch (e: Exception) { }
LogUtils.error(throwable = e)
private fun handleException(
e: Exception,
actionExecList: MutableList<ActionExec>,
currentStep: Int
) {
val actionExec = genExceptionActionExec( val actionExec = genExceptionActionExec(
action, ERROR_CODE_HTTP_ACTION_EXEC_FAILED, Log.getStackTraceString(e) action, ERROR_CODE_HTTP_ACTION_EXEC_FAILED, Log.getStackTraceString(e)
) )
actionExecList += actionExec actionExecList += actionExec
if (action.skipError) {
taskConfig.currentStep = ++currentStep taskConfig.currentStep = if (action.skipError) {
currentStep + 1
} else { } else {
taskConfig.currentStep = Int.MAX_VALUE Int.MAX_VALUE
}
} }
LogUtils.info("finish action: ${action.request?.url}")
onFinish(actionExecList)
} }
// ==================== ActionExec 生成 ====================
private fun Request.genActionExec( private fun Request.genActionExec(
httpResponse: Response?, redirectCount: Int httpResponse: Response?,
redirectCount: Int
): ActionExec { ): ActionExec {
val actionExec = ActionExec( val actionExec = ActionExec(
step = taskConfig.currentStep, step = taskConfig.currentStep,
@ -179,66 +262,69 @@ class HttpService(
} }
httpResponse?.let { response -> httpResponse?.let { response ->
if (response.headers.isNotEmpty()) { saveCookiesFromResponse(response, url)
kotlin.runCatching { fillResponseData(actionExec, response)
}
return actionExec
}
private fun saveCookiesFromResponse(response: Response, url:String) {
if (response.headers.isEmpty()) return
runCatching {
URL(url).apply { URL(url).apply {
URI(protocol, host, path, query, null).let { uri -> val uri = URI(protocol, host, path, query, null)
taskConfig.cookieManager.put(uri, response.headers) taskConfig.cookieManager.put(uri, response.headers)
} }
}
}.onFailure { }.onFailure {
LogUtils.error(throwable = it) LogUtils.error(throwable = it)
} }
}
private fun fillResponseData(actionExec: ActionExec, response: Response) {
if (response.headers.isNotEmpty()) {
actionExec.respHeader = response.headers.toJsonString() actionExec.respHeader = response.headers.toJsonString()
} }
actionExec.respCode = response.code actionExec.respCode = response.code
if (response.data.isNotEmpty()) { if (response.data.isNotEmpty()) {
actionExec.respData = String(response.data) actionExec.respData = String(response.data)
} }
actionExec.cost = httpResponse.endTime - httpResponse.startTime
} actionExec.cost = response.endTime - response.startTime
return actionExec
} }
// ==================== 异步请求处理 ====================
private fun Request.makeAsyncRequest() = scope.launch { private fun Request.makeAsyncRequest() = scope.launch {
var request = copy()
var response = request.call()
var stepCallHttpCount = 0 var stepCallHttpCount = 0
var asyncRequest: Request = copy() var redirectUrl = getRedirectUrl(response)
var asyncResponse = asyncRequest.call()
var locationList: MutableList<String> while (shouldContinueRedirect(response, redirectUrl, stepCallHttpCount)) {
var redirectUrl: String? = null
if (asyncResponse.code in 300 until 400) {
locationList =
asyncResponse.headers.get(
HttpClient.Params.REQUEST_HEADER_LOCATION,
ignoreCase = true
)?.toMutableList()
?: mutableListOf()
redirectUrl = locationList.firstOrNull()
}
while (asyncResponse.code in 300 until 400 && stepCallHttpCount <= MAX_REDIRECT_COUNT && !redirectUrl.isNullOrBlank() && redirectUrl.isHttpRedirect()) {
kotlin.runCatching {
stepCallHttpCount++ stepCallHttpCount++
asyncRequest =
asyncRequest.buildRedirectHttpRequest(redirectUrl!!, asyncRequest.url) runCatching {
asyncResponse = asyncRequest.call() request = request.buildRedirectHttpRequest(redirectUrl!!, request.url)
locationList = response = request.call()
asyncResponse.headers.get( redirectUrl = getRedirectUrl(response)
HttpClient.Params.REQUEST_HEADER_LOCATION,
ignoreCase = true
)?.toMutableList()
?: mutableListOf()
redirectUrl = locationList.firstOrNull()
}.onFailure { }.onFailure {
LogUtils.error(throwable = it) LogUtils.error(throwable = it)
return@launch
} }
} }
} }
private fun String.isHttpRedirect(): Boolean = pattern.find(this)?.let { // ==================== URL 处理 ====================
return@isHttpRedirect this.startsWith("http")
private fun String.isHttpRedirect(): Boolean {
return HTTP_REDIRECT_PATTERN.find(this)?.let {
startsWith("http", ignoreCase = true)
} ?: true } ?: true
}
private fun HttpActionRequest.buildHttpRequest(): Request = Request( private fun HttpActionRequest.buildHttpRequest(): Request = Request(
url = url, url = url,
@ -247,193 +333,223 @@ class HttpService(
headers = headers.nameValueToMap() headers = headers.nameValueToMap()
) )
private fun genWholeResponse(httpRequest: Request, httpResponse: Response?): String { private fun genWholeResponse(httpRequest: Request, httpResponse: Response?): String {
return """ val responseData = httpResponse?.data?.takeIf { it.isNotEmpty() }?.let {
[${httpRequest.url}]${if (httpResponse?.data?.isNotEmpty() == true) String(httpResponse.data) else ""}[${ String(it)
httpResponse?.headers?.let { } ?: ""
if (it.isEmpty()) ""
else it.toJsonString() val responseHeaders = httpResponse?.headers?.takeIf { it.isNotEmpty() }?.toJsonString() ?: ""
}
}] return "[${httpRequest.url}]$responseData[$responseHeaders]"
""".trimIndent()
} }
// ==================== 步骤控制 ====================
private fun List<Next>.httpGetNextStepIndex( private fun List<Next>.httpGetNextStepIndex(
httpRequest: Request, httpResponse: Response?, currentStep: Int httpRequest: Request,
httpResponse: Response?,
currentStep: Int
): Int { ): Int {
val wholeResponse = genWholeResponse(httpRequest, httpResponse) val wholeResponse = genWholeResponse(httpRequest, httpResponse)
return getNextStepIndex(wholeResponse, currentStep) return getNextStepIndex(wholeResponse, currentStep)
} }
// ==================== 变量提取 ====================
private fun extractResponseVariableToCache( private fun extractResponseVariableToCache(
action: BaseAction.HttpAction, httpRequest: Request, httpResponse: Response? action: BaseAction.HttpAction,
httpRequest: Request,
httpResponse: Response
) { ) {
action.response?.let { actionResponse -> val actionResponse = action.response ?: return
httpResponse?.headers?.let { responseHeaders -> val responseHeaders = httpResponse.headers
extractCookieVariableToCache(actionResponse.cookies, responseHeaders) extractCookieVariableToCache(actionResponse.cookies, responseHeaders)
extractHeaderVariableToCache(actionResponse.headers, responseHeaders) extractHeaderVariableToCache(actionResponse.headers, responseHeaders)
extractBodyVariableToCache( extractBodyVariableToCache(
action, genWholeResponse(httpRequest, httpResponse), httpResponse.data action,
genWholeResponse(httpRequest, httpResponse),
httpResponse.data
) )
} }
}
}
private fun extractCookieVariableToCache( private fun extractCookieVariableToCache(
cookies: List<NameVariable>, responseHeaders: Map<String, List<String>> cookies: List<NameVariable>,
responseHeaders: Map<String, List<String>>
) { ) {
if (cookies.isEmpty()) return if (cookies.isEmpty()) return
runCatching { runCatching {
val cookieList = val cookieList = responseHeaders.get(
responseHeaders.get(HttpClient.Params.RESPONSE_HEADER_SET_COOKIE, ignoreCase = true) HttpClient.Params.RESPONSE_HEADER_SET_COOKIE,
if (cookieList.isNullOrEmpty()) return ignoreCase = true
cookies.map { nameVariable -> ) ?: return
cookieList.map { cookie ->
val cookieValues = cookie.split(";") cookies.forEach { nameVariable ->
cookieValues.map { cookieValue -> cookieList.forEach { cookie ->
val keyPair = cookieValue.split("=", limit = 2) parseCookieValue(cookie, nameVariable.name)?.let { value ->
val key = keyPair.first().trim()
val value = keyPair.getOrElse(1) { "" }.trim()
if (key == nameVariable.name) {
taskConfig.variableCache[nameVariable.variable] = value taskConfig.variableCache[nameVariable.variable] = value
} }
} }
} }
}
}.onFailure { }.onFailure {
LogUtils.error(throwable = it) LogUtils.error(throwable = it)
} }
} }
private fun parseCookieValue(cookie: String, targetName: String): String? {
val cookieValues = cookie.split(";")
cookieValues.forEach { cookieValue ->
val keyPair = cookieValue.split("=", limit = 2)
val key = keyPair.first().trim()
val value = keyPair.getOrElse(1) { "" }.trim()
if (key == targetName) {
return value
}
}
return null
}
private fun extractHeaderVariableToCache( private fun extractHeaderVariableToCache(
headers: List<NameVariable>, responseHeaders: Map<String, List<String>> headers: List<NameVariable>,
responseHeaders: Map<String, List<String>>
) { ) {
if (headers.isEmpty() || responseHeaders.isEmpty()) return if (headers.isEmpty() || responseHeaders.isEmpty()) return
headers.map { nameVariable ->
responseHeaders[nameVariable.name]?.firstOrNull()?.apply {
taskConfig.variableCache[nameVariable.variable] = this
}
headers.forEach { nameVariable ->
responseHeaders[nameVariable.name]?.firstOrNull()?.let { value ->
taskConfig.variableCache[nameVariable.variable] = value
}
} }
} }
// ==================== 请求构建 ====================
private fun amendActionRequest(actionRequest: HttpActionRequest) { private fun amendActionRequest(actionRequest: HttpActionRequest) {
// 替换变量
actionRequest.headers.replaceVariableData() actionRequest.headers.replaceVariableData()
actionRequest.cookies.replaceVariableData() actionRequest.cookies.replaceVariableData()
actionRequest.params.replaceVariableData() actionRequest.params.replaceVariableData()
actionRequest.headers.amendBaseHeader(true).apply {
actionRequest.headers = this.toMutableList()
}
if (actionRequest.data.isNotBlank()) { if (actionRequest.data.isNotBlank()) {
actionRequest.data = actionRequest.data.toVariableData() actionRequest.data = actionRequest.data.toVariableData()
} }
actionRequest.url = when (actionRequest.method) { // 添加基础 Header
HttpMethod.Get -> { actionRequest.headers = actionRequest.headers.amendBaseHeader(true).toMutableList()
buildGetUrl(actionRequest)
}
else -> { // 处理 URL
actionRequest.url.toVariableData() actionRequest.url = when (actionRequest.method) {
} HttpMethod.Get -> buildGetUrl(actionRequest)
else -> actionRequest.url.toVariableData()
} }
if (actionRequest.url.startsWith("https", ignoreCase = true)) { // 添加 Sec-CH-UA Header (HTTPS)
actionRequest.headers.addSecChUa().apply { if (actionRequest.url.isHttps()) {
actionRequest.headers = this actionRequest.headers = actionRequest.headers.addSecChUa()
}
} }
// 处理 Cookie
actionRequest.amendCookie() actionRequest.amendCookie()
if (HttpMethod.Get != actionRequest.method) {
val sendData: ByteArray = actionRequest.genPostData() // 处理 POST 数据
if (sendData.isNotEmpty()) actionRequest.data = String(sendData) if (actionRequest.method != HttpMethod.Get) {
val sendData = actionRequest.genPostData()
if (sendData.isNotEmpty()) {
actionRequest.data = String(sendData)
}
} }
} }
private fun buildGetUrl(actionRequest: HttpActionRequest): String { private fun buildGetUrl(actionRequest: HttpActionRequest): String {
return actionRequest.url.toVariableData().let { u -> val baseUrl = actionRequest.url.toVariableData()
Uri.parse(u).buildUpon().apply { return baseUrl.toUri().buildUpon().apply {
actionRequest.params.forEach { nameValue -> actionRequest.params.forEach { nameValue ->
this.appendQueryParameter(nameValue.name, nameValue.value) appendQueryParameter(nameValue.name, nameValue.value)
}
} }
}.build().toString() }.build().toString()
} }
private fun HttpActionRequest.amendCookie() { private fun HttpActionRequest.amendCookie() {
val cookies: MutableSet<NameValue> = mutableSetOf() val cookies = mutableSetOf<NameValue>()
cookieFromCookieManager(url).apply {
if (autoCookie && isNotEmpty()) { // 从 CookieManager 获取 Cookie
cookies += this if (autoCookie) {
} cookies += cookieFromCookieManager(url)
} }
this.cookies.apply {
if (isNotEmpty()) { // 添加手动指定的 Cookie覆盖同名 Cookie
forEach { this.cookies.forEach {
cookies.remove(it) cookies.remove(it)
cookies += it cookies += it
} }
}
} // 构建 Cookie Header
if (cookies.isNotEmpty()) { if (cookies.isNotEmpty()) {
headers += cookies.toList().buildCookie() headers += cookies.toList().buildCookie()
} }
} }
private fun HttpActionRequest.genPostData(): ByteArray { private fun HttpActionRequest.genPostData(): ByteArray {
var sendData: ByteArray = byteArrayOf() // 优先使用 paramsform-data
if (params.isNotEmpty()) { if (params.isNotEmpty()) {
sendData = params.joinToString("&") { return params.joinToString("&") {
"${it.name.urlEncode()}=${it.value.urlEncode()}" "${it.name.urlEncode()}=${it.value.urlEncode()}"
}.toByteArray() }.toByteArray()
} }
// 否则使用 dataraw data
if (data.isNotBlank()) { if (data.isNotBlank()) {
sendData = data.toByteArray(Charsets.UTF_8) return data.toByteArray(Charsets.UTF_8)
} }
return sendData
return byteArrayOf()
} }
private fun Request.buildRedirectHttpRequest(redirectUrl: String, originUrl: String): Request { // ==================== 重定向请求构建 ====================
val headers = this.genBaseHeaderMap().toMutableMap()
headers[HttpClient.Params.REQUEST_HEADER_REFERER] = originUrl
private fun Request.buildRedirectHttpRequest(
redirectUrl: String,
originUrl: String
): Request {
val headers = genBaseHeaderMap().toMutableMap()
headers[HttpClient.Params.REQUEST_HEADER_REFERER] = originUrl
val request = Request( val normalizedUrl = URL(URL(originUrl), redirectUrl.replace(" ", "%20")).toString()
url = URL(URL(originUrl), redirectUrl.replace(" ", "%20")).toString(),
method = HttpMethod.Get,
headers = headers
)
val cookies = cookieFromCookieManager(request.url) // 添加 Cookie
val cookies = cookieFromCookieManager(normalizedUrl)
if (cookies.isNotEmpty()) { if (cookies.isNotEmpty()) {
cookies.toList().buildCookie().let { cookie -> cookies.toList().buildCookie().let { cookie ->
headers.put(cookie.name, cookie.value) headers[cookie.name] = cookie.value
} }
} }
if (request.url.isHttps()) { // 添加 Sec-CH-UA Header (HTTPS)
val secChUa = mutableListOf<NameValue>().addSecChUa() if (normalizedUrl.isHttps()) {
secChUa.forEach { mutableListOf<NameValue>().addSecChUa().forEach {
headers[it.name] = it.value headers[it.name] = it.value
} }
} }
return request return Request(
url = normalizedUrl,
method = HttpMethod.Get,
headers = headers
)
} }
} }
// ==================== Map 扩展函数 ====================
fun <V> Map<String, V>.get(key: String, ignoreCase: Boolean): V? { fun <V> Map<String, V>.get(key: String, ignoreCase: Boolean): V? {
return this.entries.firstOrNull { return entries.firstOrNull {
it.key.equals(key, ignoreCase = ignoreCase) it.key.equals(key, ignoreCase = ignoreCase)
}?.value }?.value
} }
fun <V> Map<String, V>.getOrDefault(key: String, ignoreCase:Boolean, defaultValue:V): V = fun <V> Map<String, V>.getOrDefault(key: String, ignoreCase:Boolean, defaultValue:V): V =
this.get(key, ignoreCase) ?: defaultValue this.get(key, ignoreCase) ?: defaultValue
fun String.toUri():Uri = Uri.parse(this)
Loading…
Cancel
Save