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 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 final class io/github/wulkanowy/sdk/scrapper/grades/Grade {
|
||||||
public static final field Companion Lio/github/wulkanowy/sdk/scrapper/grades/Grade$Companion;
|
public static final field Companion Lio/github/wulkanowy/sdk/scrapper/grades/Grade$Companion;
|
||||||
public field date Ljava/time/LocalDate;
|
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.Recipient
|
||||||
import io.github.wulkanowy.sdk.scrapper.messages.RecipientType
|
import io.github.wulkanowy.sdk.scrapper.messages.RecipientType
|
||||||
import okhttp3.HttpUrl
|
import okhttp3.HttpUrl
|
||||||
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
@ -201,7 +202,7 @@ internal fun String.md5(): String {
|
||||||
return digest.toHexString()
|
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 pathSegmentIndex = getPathIndexByModuleHost(moduleHost)
|
||||||
val pathKey = pathSegments.getOrNull(pathSegmentIndex)
|
val pathKey = pathSegments.getOrNull(pathSegmentIndex)
|
||||||
val mappedPath = Scrapper.endpointsMap[appVersion]
|
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) {
|
internal fun getPathIndexByModuleHost(moduleHost: String): Int = when (moduleHost) {
|
||||||
StudentPlusModuleHost -> 3
|
StudentPlusModuleHost -> 3
|
||||||
StudentModuleHost, MessagesModuleHost -> 2
|
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.Scrapper.LoginType.STANDARD
|
||||||
import io.github.wulkanowy.sdk.scrapper.attachVToken
|
import io.github.wulkanowy.sdk.scrapper.attachVToken
|
||||||
import io.github.wulkanowy.sdk.scrapper.exception.VulcanClientError
|
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.getModuleHeadersFromDocument
|
||||||
|
import io.github.wulkanowy.sdk.scrapper.isAnyMappingAvailable
|
||||||
import io.github.wulkanowy.sdk.scrapper.login.LoginResult
|
import io.github.wulkanowy.sdk.scrapper.login.LoginResult
|
||||||
import io.github.wulkanowy.sdk.scrapper.login.ModuleHeaders
|
import io.github.wulkanowy.sdk.scrapper.login.ModuleHeaders
|
||||||
import io.github.wulkanowy.sdk.scrapper.login.NotLoggedInException
|
import io.github.wulkanowy.sdk.scrapper.login.NotLoggedInException
|
||||||
import io.github.wulkanowy.sdk.scrapper.login.UrlGenerator
|
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
|
||||||
import io.github.wulkanowy.sdk.scrapper.repository.AccountRepository.Companion.SELECTOR_ADFS_CARDS
|
import io.github.wulkanowy.sdk.scrapper.repository.AccountRepository.Companion.SELECTOR_ADFS_CARDS
|
||||||
import io.github.wulkanowy.sdk.scrapper.repository.AccountRepository.Companion.SELECTOR_ADFS_LIGHT
|
import io.github.wulkanowy.sdk.scrapper.repository.AccountRepository.Companion.SELECTOR_ADFS_LIGHT
|
||||||
|
@ -71,8 +73,9 @@ internal class AutoLoginInterceptor(
|
||||||
val response = try {
|
val response = try {
|
||||||
chain.proceed(request.attachModuleHeaders())
|
chain.proceed(request.attachModuleHeaders())
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
if (e is VulcanClientError) {
|
when (e) {
|
||||||
checkHttpErrorResponse(e, url)
|
is VulcanClientError -> checkHttpErrorResponse(e, url)
|
||||||
|
is VulcanServerError -> checkServerError(e, url)
|
||||||
}
|
}
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
|
@ -163,7 +166,7 @@ internal class AutoLoginInterceptor(
|
||||||
}
|
}
|
||||||
|
|
||||||
val headers = headersByHost[moduleHost]
|
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}")
|
logger.info("X-V-AppVersion: ${headers?.appVersion}")
|
||||||
|
|
||||||
|
@ -214,8 +217,13 @@ internal class AutoLoginInterceptor(
|
||||||
// new error style
|
// new error style
|
||||||
val isCodeMatch = response.code == HttpURLConnection.HTTP_OK
|
val isCodeMatch = response.code == HttpURLConnection.HTTP_OK
|
||||||
val isJsonContent = bodyContent.startsWith("{")
|
val isJsonContent = bodyContent.startsWith("{")
|
||||||
val isSubdomainMatch = "uonetplus-uczen" in url
|
val isStudentModuleSubdomain = StudentModuleHost in url
|
||||||
if (isCodeMatch && isJsonContent && isSubdomainMatch) {
|
if (isCodeMatch && isJsonContent && isStudentModuleSubdomain) {
|
||||||
|
checkResponseStudentModule(bodyContent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkResponseStudentModule(bodyContent: String) {
|
||||||
runCatching { json.decodeFromString<ApiResponse<Unit?>>(bodyContent) }
|
runCatching { json.decodeFromString<ApiResponse<Unit?>>(bodyContent) }
|
||||||
.onFailure { logger.error("AutoLoginInterceptor: Can't deserialize new style error content body", it) }
|
.onFailure { logger.error("AutoLoginInterceptor: Can't deserialize new style error content body", it) }
|
||||||
.onSuccess {
|
.onSuccess {
|
||||||
|
@ -223,6 +231,10 @@ internal class AutoLoginInterceptor(
|
||||||
if ("Brak uprawnień" in errorMessage) {
|
if ("Brak uprawnień" in errorMessage) {
|
||||||
throw NotLoggedInException(errorMessage)
|
throw NotLoggedInException(errorMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// workaround - access resource before request mapping
|
||||||
|
if ("was not found on controller" in errorMessage && headersByHost[StudentModuleHost] == null) {
|
||||||
|
throw NotLoggedInException(errorMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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]
|
* @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.ServiceUnavailableException
|
||||||
import io.github.wulkanowy.sdk.scrapper.exception.TemporarilyDisabledException
|
import io.github.wulkanowy.sdk.scrapper.exception.TemporarilyDisabledException
|
||||||
import io.github.wulkanowy.sdk.scrapper.exception.VulcanException
|
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.AccountPermissionException
|
||||||
import io.github.wulkanowy.sdk.scrapper.login.InvalidSymbolException
|
import io.github.wulkanowy.sdk.scrapper.login.InvalidSymbolException
|
||||||
import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException
|
import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException
|
||||||
|
@ -91,7 +92,7 @@ internal class ErrorInterceptor(
|
||||||
}
|
}
|
||||||
|
|
||||||
when (doc.title()) {
|
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)
|
"Błąd strony" -> throw VulcanException(doc.select(".errorMessage").text(), httpCode)
|
||||||
"Logowanie" -> throw AccountPermissionException(
|
"Logowanie" -> throw AccountPermissionException(
|
||||||
buildString {
|
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.LoginHelper
|
||||||
import io.github.wulkanowy.sdk.scrapper.login.NotLoggedInException
|
import io.github.wulkanowy.sdk.scrapper.login.NotLoggedInException
|
||||||
import io.github.wulkanowy.sdk.scrapper.login.UrlGenerator
|
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.Diary
|
||||||
import io.github.wulkanowy.sdk.scrapper.register.HomePageResponse
|
import io.github.wulkanowy.sdk.scrapper.register.HomePageResponse
|
||||||
import io.github.wulkanowy.sdk.scrapper.register.RegisterStudent
|
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")
|
val diaryUrl = (url.generate(UrlGenerator.Site.STUDENT) + "UczenDziennik.mvc/Get")
|
||||||
.toHttpUrl()
|
.toHttpUrl()
|
||||||
.mapModuleUrls(StudentModuleHost, appVersion)
|
.mapModuleUrl(StudentModuleHost, appVersion)
|
||||||
|
|
||||||
return student
|
return student
|
||||||
.getSchoolInfo(url = diaryUrl.toString())
|
.getSchoolInfo(url = diaryUrl.toString())
|
||||||
|
@ -330,7 +330,7 @@ internal class RegisterRepository(
|
||||||
}
|
}
|
||||||
val cacheUrl = (url.generate(UrlGenerator.Site.STUDENT) + "UczenCache.mvc/Get")
|
val cacheUrl = (url.generate(UrlGenerator.Site.STUDENT) + "UczenCache.mvc/Get")
|
||||||
.toHttpUrl()
|
.toHttpUrl()
|
||||||
.mapModuleUrls(StudentModuleHost, appVersion)
|
.mapModuleUrl(StudentModuleHost, appVersion)
|
||||||
|
|
||||||
val userCache = student.getUserCache(
|
val userCache = student.getUserCache(
|
||||||
url = cacheUrl.toString(),
|
url = cacheUrl.toString(),
|
||||||
|
@ -347,11 +347,11 @@ internal class RegisterRepository(
|
||||||
|
|
||||||
val contextUrl = (baseStudentPlus + "api/Context").toHttpUrl()
|
val contextUrl = (baseStudentPlus + "api/Context").toHttpUrl()
|
||||||
val contextVToken = contextUrl.getMatchedVToken(StudentPlusModuleHost, moduleHeaders)
|
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 semestersUrl = (baseStudentPlus + "api/OkresyKlasyfikacyjne").toHttpUrl()
|
||||||
val semestersVToken = semestersUrl.getMatchedVToken(StudentPlusModuleHost, moduleHeaders)
|
val semestersVToken = semestersUrl.getMatchedVToken(StudentPlusModuleHost, moduleHeaders)
|
||||||
val mappedSemestersUrl = semestersUrl.mapModuleUrls(StudentPlusModuleHost, moduleHeaders.appVersion)
|
val mappedSemestersUrl = semestersUrl.mapModuleUrl(StudentPlusModuleHost, moduleHeaders.appVersion)
|
||||||
|
|
||||||
return studentPlus
|
return studentPlus
|
||||||
.getContextByUrl(vToken = contextVToken, url = mappedContextUrl.toString()).students
|
.getContextByUrl(vToken = contextVToken, url = mappedContextUrl.toString()).students
|
||||||
|
|
|
@ -102,7 +102,6 @@ internal class ServiceManager(
|
||||||
|
|
||||||
private val interceptors: MutableList<Pair<Interceptor, Boolean>> = mutableListOf(
|
private val interceptors: MutableList<Pair<Interceptor, Boolean>> = mutableListOf(
|
||||||
HttpLoggingInterceptor().setLevel(logLevel) to true,
|
HttpLoggingInterceptor().setLevel(logLevel) to true,
|
||||||
ErrorInterceptor(cookieJarCabinet) to false,
|
|
||||||
AutoLoginInterceptor(
|
AutoLoginInterceptor(
|
||||||
loginType = loginType,
|
loginType = loginType,
|
||||||
cookieJarCabinet = cookieJarCabinet,
|
cookieJarCabinet = cookieJarCabinet,
|
||||||
|
@ -113,6 +112,7 @@ internal class ServiceManager(
|
||||||
headersByHost = headersByHost,
|
headersByHost = headersByHost,
|
||||||
loginLock = loginLock,
|
loginLock = loginLock,
|
||||||
) to false,
|
) to false,
|
||||||
|
ErrorInterceptor(cookieJarCabinet) to false,
|
||||||
UserAgentInterceptor(androidVersion, buildTag, userAgentTemplate) to false,
|
UserAgentInterceptor(androidVersion, buildTag, userAgentTemplate) to false,
|
||||||
HttpErrorInterceptor() to false,
|
HttpErrorInterceptor() to false,
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue