Add workarounds to detect not mapped urls as NotLoggedIn errors in multi threaded envs
This commit is contained in:
parent
ce84113020
commit
6fff07e52a
7 changed files with 75 additions and 22 deletions
|
@ -439,6 +439,11 @@ public final class io/github/wulkanowy/sdk/scrapper/exception/VulcanClientError
|
|||
public class io/github/wulkanowy/sdk/scrapper/exception/VulcanException : io/github/wulkanowy/sdk/scrapper/exception/ScrapperException {
|
||||
}
|
||||
|
||||
public final class io/github/wulkanowy/sdk/scrapper/exception/VulcanServerError : io/github/wulkanowy/sdk/scrapper/exception/VulcanException {
|
||||
public final fun getDoc ()Lorg/jsoup/nodes/Document;
|
||||
public final fun getHttpCode ()I
|
||||
}
|
||||
|
||||
public final class io/github/wulkanowy/sdk/scrapper/grades/Grade {
|
||||
public static final field Companion Lio/github/wulkanowy/sdk/scrapper/grades/Grade$Companion;
|
||||
public field date Ljava/time/LocalDate;
|
||||
|
|
|
@ -9,6 +9,7 @@ import io.github.wulkanowy.sdk.scrapper.messages.Mailbox
|
|||
import io.github.wulkanowy.sdk.scrapper.messages.Recipient
|
||||
import io.github.wulkanowy.sdk.scrapper.messages.RecipientType
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.Request
|
||||
import org.jsoup.Jsoup
|
||||
import org.slf4j.LoggerFactory
|
||||
|
@ -201,7 +202,7 @@ internal fun String.md5(): String {
|
|||
return digest.toHexString()
|
||||
}
|
||||
|
||||
internal fun HttpUrl.mapModuleUrls(moduleHost: String, appVersion: String?): HttpUrl {
|
||||
internal fun HttpUrl.mapModuleUrl(moduleHost: String, appVersion: String?): HttpUrl {
|
||||
val pathSegmentIndex = getPathIndexByModuleHost(moduleHost)
|
||||
val pathKey = pathSegments.getOrNull(pathSegmentIndex)
|
||||
val mappedPath = Scrapper.endpointsMap[appVersion]
|
||||
|
@ -221,6 +222,20 @@ internal fun HttpUrl.mapModuleUrls(moduleHost: String, appVersion: String?): Htt
|
|||
}
|
||||
}
|
||||
|
||||
internal fun isAnyMappingAvailable(url: String): Boolean {
|
||||
val host = url.toHttpUrl().host
|
||||
val module = when {
|
||||
MessagesModuleHost in host -> MessagesModuleHost
|
||||
StudentPlusModuleHost in host -> StudentPlusModuleHost
|
||||
StudentModuleHost in host -> StudentModuleHost
|
||||
else -> null
|
||||
} ?: return false
|
||||
|
||||
return Scrapper.endpointsMap.keys.any { appVersion ->
|
||||
url.toHttpUrl().mapModuleUrl(module, appVersion).toString() !== url
|
||||
}
|
||||
}
|
||||
|
||||
internal fun getPathIndexByModuleHost(moduleHost: String): Int = when (moduleHost) {
|
||||
StudentPlusModuleHost -> 3
|
||||
StudentModuleHost, MessagesModuleHost -> 2
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
package io.github.wulkanowy.sdk.scrapper.exception
|
||||
|
||||
import org.jsoup.nodes.Document
|
||||
|
||||
class VulcanServerError internal constructor(
|
||||
message: String,
|
||||
val doc: Document,
|
||||
val httpCode: Int,
|
||||
) : VulcanException(message)
|
|
@ -11,12 +11,14 @@ import io.github.wulkanowy.sdk.scrapper.Scrapper.LoginType.ADFSLightScoped
|
|||
import io.github.wulkanowy.sdk.scrapper.Scrapper.LoginType.STANDARD
|
||||
import io.github.wulkanowy.sdk.scrapper.attachVToken
|
||||
import io.github.wulkanowy.sdk.scrapper.exception.VulcanClientError
|
||||
import io.github.wulkanowy.sdk.scrapper.exception.VulcanServerError
|
||||
import io.github.wulkanowy.sdk.scrapper.getModuleHeadersFromDocument
|
||||
import io.github.wulkanowy.sdk.scrapper.isAnyMappingAvailable
|
||||
import io.github.wulkanowy.sdk.scrapper.login.LoginResult
|
||||
import io.github.wulkanowy.sdk.scrapper.login.ModuleHeaders
|
||||
import io.github.wulkanowy.sdk.scrapper.login.NotLoggedInException
|
||||
import io.github.wulkanowy.sdk.scrapper.login.UrlGenerator
|
||||
import io.github.wulkanowy.sdk.scrapper.mapModuleUrls
|
||||
import io.github.wulkanowy.sdk.scrapper.mapModuleUrl
|
||||
import io.github.wulkanowy.sdk.scrapper.repository.AccountRepository.Companion.SELECTOR_ADFS
|
||||
import io.github.wulkanowy.sdk.scrapper.repository.AccountRepository.Companion.SELECTOR_ADFS_CARDS
|
||||
import io.github.wulkanowy.sdk.scrapper.repository.AccountRepository.Companion.SELECTOR_ADFS_LIGHT
|
||||
|
@ -71,8 +73,9 @@ internal class AutoLoginInterceptor(
|
|||
val response = try {
|
||||
chain.proceed(request.attachModuleHeaders())
|
||||
} catch (e: Throwable) {
|
||||
if (e is VulcanClientError) {
|
||||
checkHttpErrorResponse(e, url)
|
||||
when (e) {
|
||||
is VulcanClientError -> checkHttpErrorResponse(e, url)
|
||||
is VulcanServerError -> checkServerError(e, url)
|
||||
}
|
||||
throw e
|
||||
}
|
||||
|
@ -163,7 +166,7 @@ internal class AutoLoginInterceptor(
|
|||
}
|
||||
|
||||
val headers = headersByHost[moduleHost]
|
||||
val mappedUrl = url.mapModuleUrls(moduleHost, headers?.appVersion)
|
||||
val mappedUrl = url.mapModuleUrl(moduleHost, headers?.appVersion)
|
||||
|
||||
logger.info("X-V-AppVersion: ${headers?.appVersion}")
|
||||
|
||||
|
@ -214,18 +217,27 @@ internal class AutoLoginInterceptor(
|
|||
// new error style
|
||||
val isCodeMatch = response.code == HttpURLConnection.HTTP_OK
|
||||
val isJsonContent = bodyContent.startsWith("{")
|
||||
val isSubdomainMatch = "uonetplus-uczen" in url
|
||||
if (isCodeMatch && isJsonContent && isSubdomainMatch) {
|
||||
runCatching { json.decodeFromString<ApiResponse<Unit?>>(bodyContent) }
|
||||
.onFailure { logger.error("AutoLoginInterceptor: Can't deserialize new style error content body", it) }
|
||||
.onSuccess {
|
||||
it.feedback?.message?.let { errorMessage ->
|
||||
if ("Brak uprawnień" in errorMessage) {
|
||||
throw NotLoggedInException(errorMessage)
|
||||
}
|
||||
val isStudentModuleSubdomain = StudentModuleHost in url
|
||||
if (isCodeMatch && isJsonContent && isStudentModuleSubdomain) {
|
||||
checkResponseStudentModule(bodyContent)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkResponseStudentModule(bodyContent: String) {
|
||||
runCatching { json.decodeFromString<ApiResponse<Unit?>>(bodyContent) }
|
||||
.onFailure { logger.error("AutoLoginInterceptor: Can't deserialize new style error content body", it) }
|
||||
.onSuccess {
|
||||
it.feedback?.message?.let { errorMessage ->
|
||||
if ("Brak uprawnień" in errorMessage) {
|
||||
throw NotLoggedInException(errorMessage)
|
||||
}
|
||||
|
||||
// workaround - access resource before request mapping
|
||||
if ("was not found on controller" in errorMessage && headersByHost[StudentModuleHost] == null) {
|
||||
throw NotLoggedInException(errorMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkHttpErrorResponse(error: VulcanClientError, url: String) {
|
||||
|
@ -236,6 +248,17 @@ internal class AutoLoginInterceptor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun checkServerError(error: VulcanServerError, url: String) {
|
||||
val isCodeMatch = error.httpCode == HttpURLConnection.HTTP_OK
|
||||
val isSubdomainMatch = MessagesModuleHost in url || StudentModuleHost in url
|
||||
val isMappable = isAnyMappingAvailable(url)
|
||||
|
||||
// workaround - access resource before request mapping
|
||||
if (isCodeMatch && isSubdomainMatch && isMappable) {
|
||||
throw NotLoggedInException(error.message.orEmpty())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see [https://github.com/square/retrofit/issues/3110#issuecomment-536248102]
|
||||
*/
|
||||
|
|
|
@ -7,6 +7,7 @@ import io.github.wulkanowy.sdk.scrapper.exception.ScrapperException
|
|||
import io.github.wulkanowy.sdk.scrapper.exception.ServiceUnavailableException
|
||||
import io.github.wulkanowy.sdk.scrapper.exception.TemporarilyDisabledException
|
||||
import io.github.wulkanowy.sdk.scrapper.exception.VulcanException
|
||||
import io.github.wulkanowy.sdk.scrapper.exception.VulcanServerError
|
||||
import io.github.wulkanowy.sdk.scrapper.login.AccountPermissionException
|
||||
import io.github.wulkanowy.sdk.scrapper.login.InvalidSymbolException
|
||||
import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException
|
||||
|
@ -91,7 +92,7 @@ internal class ErrorInterceptor(
|
|||
}
|
||||
|
||||
when (doc.title()) {
|
||||
"Błąd" -> throw VulcanException(doc.body().text(), httpCode)
|
||||
"Błąd" -> throw VulcanServerError(doc.body().text(), doc, httpCode)
|
||||
"Błąd strony" -> throw VulcanException(doc.select(".errorMessage").text(), httpCode)
|
||||
"Logowanie" -> throw AccountPermissionException(
|
||||
buildString {
|
||||
|
|
|
@ -16,7 +16,7 @@ import io.github.wulkanowy.sdk.scrapper.login.InvalidSymbolException
|
|||
import io.github.wulkanowy.sdk.scrapper.login.LoginHelper
|
||||
import io.github.wulkanowy.sdk.scrapper.login.NotLoggedInException
|
||||
import io.github.wulkanowy.sdk.scrapper.login.UrlGenerator
|
||||
import io.github.wulkanowy.sdk.scrapper.mapModuleUrls
|
||||
import io.github.wulkanowy.sdk.scrapper.mapModuleUrl
|
||||
import io.github.wulkanowy.sdk.scrapper.register.Diary
|
||||
import io.github.wulkanowy.sdk.scrapper.register.HomePageResponse
|
||||
import io.github.wulkanowy.sdk.scrapper.register.RegisterStudent
|
||||
|
@ -251,7 +251,7 @@ internal class RegisterRepository(
|
|||
}
|
||||
val diaryUrl = (url.generate(UrlGenerator.Site.STUDENT) + "UczenDziennik.mvc/Get")
|
||||
.toHttpUrl()
|
||||
.mapModuleUrls(StudentModuleHost, appVersion)
|
||||
.mapModuleUrl(StudentModuleHost, appVersion)
|
||||
|
||||
return student
|
||||
.getSchoolInfo(url = diaryUrl.toString())
|
||||
|
@ -330,7 +330,7 @@ internal class RegisterRepository(
|
|||
}
|
||||
val cacheUrl = (url.generate(UrlGenerator.Site.STUDENT) + "UczenCache.mvc/Get")
|
||||
.toHttpUrl()
|
||||
.mapModuleUrls(StudentModuleHost, appVersion)
|
||||
.mapModuleUrl(StudentModuleHost, appVersion)
|
||||
|
||||
val userCache = student.getUserCache(
|
||||
url = cacheUrl.toString(),
|
||||
|
@ -347,11 +347,11 @@ internal class RegisterRepository(
|
|||
|
||||
val contextUrl = (baseStudentPlus + "api/Context").toHttpUrl()
|
||||
val contextVToken = contextUrl.getMatchedVToken(StudentPlusModuleHost, moduleHeaders)
|
||||
val mappedContextUrl = contextUrl.mapModuleUrls(StudentPlusModuleHost, moduleHeaders.appVersion)
|
||||
val mappedContextUrl = contextUrl.mapModuleUrl(StudentPlusModuleHost, moduleHeaders.appVersion)
|
||||
|
||||
val semestersUrl = (baseStudentPlus + "api/OkresyKlasyfikacyjne").toHttpUrl()
|
||||
val semestersVToken = semestersUrl.getMatchedVToken(StudentPlusModuleHost, moduleHeaders)
|
||||
val mappedSemestersUrl = semestersUrl.mapModuleUrls(StudentPlusModuleHost, moduleHeaders.appVersion)
|
||||
val mappedSemestersUrl = semestersUrl.mapModuleUrl(StudentPlusModuleHost, moduleHeaders.appVersion)
|
||||
|
||||
return studentPlus
|
||||
.getContextByUrl(vToken = contextVToken, url = mappedContextUrl.toString()).students
|
||||
|
|
|
@ -102,7 +102,6 @@ internal class ServiceManager(
|
|||
|
||||
private val interceptors: MutableList<Pair<Interceptor, Boolean>> = mutableListOf(
|
||||
HttpLoggingInterceptor().setLevel(logLevel) to true,
|
||||
ErrorInterceptor(cookieJarCabinet) to false,
|
||||
AutoLoginInterceptor(
|
||||
loginType = loginType,
|
||||
cookieJarCabinet = cookieJarCabinet,
|
||||
|
@ -113,6 +112,7 @@ internal class ServiceManager(
|
|||
headersByHost = headersByHost,
|
||||
loginLock = loginLock,
|
||||
) to false,
|
||||
ErrorInterceptor(cookieJarCabinet) to false,
|
||||
UserAgentInterceptor(androidVersion, buildTag, userAgentTemplate) to false,
|
||||
HttpErrorInterceptor() to false,
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue