Migrate more to cronet directly (#247)

* Convert GalleryListParser to parse a DirectByteBuffer

* Convert FavoritesParser to parse a DirectByteBuffer

* Limit buffer size

---------

Co-authored-by: Lokesh Gidra <lokeshgidra@google.com>
This commit is contained in:
Vladimir DostoyevskiyandLokesh Gidra authored and GitHub committed 2023-11-12 19:29:31 +08:00
1 parent 70f2bfb760
commit 3bad1d06f4
8 files changed
+81 -57

No files matched your search

@@ -116,9 +116,9 @@ private fun rethrowExactly(code: Int, body: String, e: Throwable): Nothing {
throw e
}
val httpContentPool = DirectByteBufferPool(16, 0x20000)
val httpContentPool = DirectByteBufferPool(8, 0x80000)
suspend inline fun <T> fetchCompat(url: String, referer: String? = null, crossinline parser: (ByteBuffer) -> T): T {
suspend inline fun <T> fetchCompat(url: String, referer: String? = null, crossinline parser: suspend (ByteBuffer) -> T): T {
return if (isCronetSupported) {
cronetRequest(url, referer).execute {
httpContentPool.useInstance { buffer ->
@@ -213,8 +213,7 @@ object EhEngine {
return ehRequest(url, referer).executeAndParsingWith(GalleryPageParser::parse)
}
suspend fun getGalleryList(url: String) = ehRequest(url, EhUrl.referer)
.executeAndParsingWith(GalleryListParser::parse)
suspend fun getGalleryList(url: String) = fetchCompat(url, EhUrl.referer) { GalleryListParser.parse(it) }
.apply { fillGalleryList(galleryInfoList, url, true) }
.takeUnless { it.galleryInfoList.isEmpty() } ?: GalleryListParser.emptyResult
@@ -230,8 +229,7 @@ object EhEngine {
GalleryDetailParser.parsePreviewList(this) to GalleryDetailParser.parsePreviewPages(this)
}
suspend fun getFavorites(url: String) = ehRequest(url, EhUrl.referer)
.executeAndParsingWith(FavoritesParser::parse)
suspend fun getFavorites(url: String) = fetchCompat(url, EhUrl.referer, FavoritesParser::parse)
.apply { fillGalleryList(galleryInfoList, url) }
suspend fun signIn(username: String, password: String): String {
@@ -356,7 +354,13 @@ object EhEngine {
add("ddact", catStr)
gidArray.forEach { add("modifygids[]", it.toString()) }
}
}.executeAndParsingWith(FavoritesParser::parse).apply { fillGalleryList(galleryInfoList, url) }
}.executeAndParsingWith {
httpContentPool.useInstance {
it.put(ByteBuffer.wrap(toByteArray()))
it.flip()
FavoritesParser.parse(it)
}
}.apply { fillGalleryList(galleryInfoList, url) }
}
suspend fun getGalleryPageApi(
@@ -475,7 +479,13 @@ object EhEngine {
if (osc) addFormDataPart("fs_covers", "on")
addFormDataPart("f_sfile", "File Search")
}
}.executeAndParsingWith(GalleryListParser::parse).apply { fillGalleryList(galleryInfoList, EhUrl.imageSearchUrl) }
}.executeAndParsingWith {
httpContentPool.useInstance {
it.put(ByteBuffer.wrap(toByteArray()))
it.flip()
GalleryListParser.parse(it)
}
}.apply { fillGalleryList(galleryInfoList, EhUrl.imageSearchUrl) }
private suspend fun fillGalleryList(list: MutableList<BaseGalleryInfo>, url: String, filter: Boolean = false) {
// Filter title and uploader
@@ -2,16 +2,16 @@ package com.hippo.ehviewer.client.parser
import com.hippo.ehviewer.client.exception.NotLoggedInException
import com.hippo.ehviewer.client.exception.ParseException
import java.nio.ByteBuffer
import moe.tarsin.coroutines.runSuspendCatching
object FavoritesParser {
suspend fun parse(body: String): Result {
if (body.contains("This page requires you to log on.</p>")) {
throw NotLoggedInException()
}
suspend fun parse(body: ByteBuffer): Result {
val catArray = arrayOfNulls<String>(10)
val countArray = runCatching {
parseFav(body, catArray).also { check(it.isNotEmpty()) }
val countArray = runSuspendCatching {
parseFav(body, favCat = catArray).also { check(it.isNotEmpty()) }
}.getOrElse {
if (it is RuntimeException && it.message == "Not logged in!") throw NotLoggedInException()
throw ParseException("Parse favorites error", it)
}
val result = GalleryListParser.parse(body)
@@ -29,4 +29,4 @@ object FavoritesParser {
}
}
private external fun parseFav(body: String, favCat: Array<String?>): IntArray
private external fun parseFav(body: ByteBuffer, limit: Int = body.limit(), favCat: Array<String?>): IntArray
@@ -8,25 +8,29 @@ import com.hippo.ehviewer.client.data.GalleryInfo.Companion.LOCAL_FAVORITED
import com.hippo.ehviewer.client.data.GalleryInfo.Companion.NOT_FAVORITED
import com.hippo.ehviewer.client.exception.EhException
import com.hippo.ehviewer.client.exception.ParseException
import java.nio.ByteBuffer
import moe.tarsin.coroutines.runSuspendCatching
object GalleryListParser {
suspend fun parse(body: String) = runCatching {
parseGalleryInfoList(body).apply {
galleryInfoList.onEach {
if (it.favoriteSlot == NOT_FAVORITED && EhDB.containLocalFavorites(it.gid)) {
it.favoriteSlot = LOCAL_FAVORITED
}
it.generateSLang()
suspend fun parse(body: ByteBuffer) = runSuspendCatching {
parseGalleryInfoList(body)
}.onFailure {
if (it is RuntimeException) {
if (it.message == "No watched tags!") {
throw EhException(R.string.gallery_list_empty_hit_subscription)
}
if (it.message == "No hits found!") {
throw EhException(R.string.gallery_list_empty_hit)
}
}
}.getOrElse {
if (body.contains("<p>You do not have any watched tags")) {
throw EhException(R.string.gallery_list_empty_hit_subscription)
}
if (body.contains("No hits found</p>")) {
throw EhException(R.string.gallery_list_empty_hit)
}
throw ParseException("Can't parse gallery list", it)
}.getOrThrow().apply {
galleryInfoList.onEach {
if (it.favoriteSlot == NOT_FAVORITED && EhDB.containLocalFavorites(it.gid)) {
it.favoriteSlot = LOCAL_FAVORITED
}
it.generateSLang()
}
}
val emptyResult = GalleryListResult(null, null, arrayListOf())
@@ -38,4 +42,4 @@ class GalleryListResult @Keep constructor(
val galleryInfoList: ArrayList<BaseGalleryInfo>,
)
private external fun parseGalleryInfoList(e: String): GalleryListResult
private external fun parseGalleryInfoList(body: ByteBuffer, size: Int = body.limit()): GalleryListResult
+5 -15
View File
@@ -13,13 +13,13 @@ extern crate regex_lite;
extern crate tl;
use android_logger::Config;
use jnix::jni::objects::{JByteBuffer, JString};
use jnix::jni::objects::JByteBuffer;
use jnix::jni::sys::{jint, JavaVM, JNI_VERSION_1_6};
use jnix::JnixEnv;
use log::LevelFilter;
use std::ffi::c_void;
use std::ptr::slice_from_raw_parts;
use std::str::from_utf8;
use std::str::from_utf8_unchecked;
use tl::{Bytes, Node, NodeHandle, Parser, VDom};
#[macro_export]
@@ -62,28 +62,18 @@ where
handle.get(parser)
}
fn parse_jni_string<F, R>(env: &mut JnixEnv, str: &JString, mut f: F) -> Option<R>
where
F: FnMut(&VDom, &Parser, &JnixEnv) -> Option<R>,
{
let html = env.get_string(*str).ok()?;
let dom = tl::parse(html.to_str().ok()?, tl::ParserOptions::default()).ok()?;
let parser = dom.parser();
f(&dom, parser, env)
}
fn parse_bytebuffer<F, R>(env: &mut JnixEnv, str: JByteBuffer, limit: jint, mut f: F) -> Option<R>
where
F: FnMut(&VDom, &Parser, &JnixEnv) -> Option<R>,
F: FnMut(&VDom, &Parser, &JnixEnv, &str) -> Option<R>,
{
let ptr = env.get_direct_buffer_address(str).ok()?;
let html = unsafe {
let buff = slice_from_raw_parts(ptr, limit as usize);
from_utf8(&*buff).ok()?
from_utf8_unchecked(&*buff)
};
let dom = tl::parse(html, tl::ParserOptions::default()).ok()?;
let parser = dom.parser();
f(&dom, parser, env)
f(&dom, parser, env, html)
}
fn get_node_handle_attr<'a>(
+14 -5
View File
@@ -1,20 +1,29 @@
use apply::Also;
use catch_panic::catch_panic;
use jni_fn::jni_fn;
use jnix::jni::objects::{JClass, JString};
use jnix::jni::sys::{jintArray, jobjectArray};
use jnix::jni::objects::{JByteBuffer, JClass};
use jnix::jni::sys::{jint, jintArray, jobjectArray};
use jnix::jni::JNIEnv;
use jnix::JnixEnv;
use parse_jni_string;
use parse_bytebuffer;
use quick_xml::escape::unescape;
#[no_mangle]
#[catch_panic(default = "std::ptr::null_mut()")]
#[allow(non_snake_case)]
#[jni_fn("com.hippo.ehviewer.client.parser.FavoritesParserKt")]
pub fn parseFav(env: JNIEnv, _class: JClass, input: JString, str: jobjectArray) -> jintArray {
pub fn parseFav(
env: JNIEnv,
_class: JClass,
input: JByteBuffer,
limit: jint,
str: jobjectArray,
) -> jintArray {
let mut env = JnixEnv { env };
let vec = parse_jni_string(&mut env, &input, |dom, parser, env| {
let vec = parse_bytebuffer(&mut env, input, limit, |dom, parser, env, html| {
if html.contains("This page requires you to log on.</p>") {
panic!("Not logged in!")
}
let vec: Vec<i32> = dom
.get_elements_by_class_name("fp")
.enumerate()
+1 -1
View File
@@ -22,7 +22,7 @@ pub struct Limits {
#[jni_fn("com.hippo.ehviewer.client.parser.HomeParserKt")]
pub fn parseLimit(env: JNIEnv, _class: JClass, input: JByteBuffer, limit: jint) -> jobject {
let mut env = JnixEnv { env };
parse_bytebuffer(&mut env, input, limit, |dom, parser, _env| {
parse_bytebuffer(&mut env, input, limit, |dom, parser, _, _| {
let iter = get_vdom_first_element_by_class_name(dom, "homebox")?
.as_tag()?
.query_selector(parser, "strong")?;
+16 -5
View File
@@ -1,11 +1,11 @@
use catch_panic::catch_panic;
use jni_fn::jni_fn;
use jnix::jni::objects::{JClass, JString};
use jnix::jni::sys::jobject;
use jnix::jni::objects::{JByteBuffer, JClass};
use jnix::jni::sys::{jint, jobject};
use jnix::jni::JNIEnv;
use jnix::{IntoJava, JnixEnv};
use jnix_macros::IntoJava;
use parse_jni_string;
use parse_bytebuffer;
use quick_xml::escape::unescape;
use std::borrow::Cow;
use std::ops::Index;
@@ -197,9 +197,20 @@ fn parse_gallery_info(node: &Node, parser: &Parser) -> Option<BaseGalleryInfo> {
#[catch_panic(default = "std::ptr::null_mut()")]
#[allow(non_snake_case)]
#[jni_fn("com.hippo.ehviewer.client.parser.GalleryListParserKt")]
pub fn parseGalleryInfoList(env: JNIEnv, _class: JClass, input: JString) -> jobject {
pub fn parseGalleryInfoList(
env: JNIEnv,
_class: JClass,
buffer: JByteBuffer,
limit: jint,
) -> jobject {
let mut env = JnixEnv { env };
parse_jni_string(&mut env, &input, |dom, parser, _env| {
parse_bytebuffer(&mut env, buffer, limit, |dom, parser, _, str| {
if str.contains("<p>You do not have any watched tags") {
panic!("No watched tags!")
}
if str.contains("No hits found</p>") {
panic!("No hits found!")
}
let itg = get_vdom_first_element_by_class_name(dom, "itg")?;
let children = itg.children()?;
let iter = children.top().iter();
+1 -1
View File
@@ -27,7 +27,7 @@ pub struct Torrent {
#[jni_fn("com.hippo.ehviewer.client.parser.TorrentParserKt")]
pub fn parseTorrent(env: JNIEnv, _class: JClass, buffer: JByteBuffer, limit: jint) -> jobject {
let mut env = JnixEnv { env };
parse_bytebuffer(&mut env, buffer, limit, |dom, parser, _env| {
parse_bytebuffer(&mut env, buffer, limit, |dom, parser, _, _| {
Some(dom.query_selector("table")?.filter_map(|e| {
let html = e.get(parser)?.inner_html(parser);
if html.contains("Expunged") {