前言
前一段時(shí)間恕汇,在公司內(nèi)部進(jìn)行了一次QUIC協(xié)議的演講。當(dāng)時(shí)因?yàn)闀r(shí)間有限徙赢,沒有仔細(xì)的討論Cronet 的源碼細(xì)節(jié)赂弓,僅僅只是介紹了QUIC的協(xié)議細(xì)節(jié)。本文就從Cronet
源碼出發(fā)孽鸡,聊聊QUIC的一些實(shí)現(xiàn)蹂午,進(jìn)而看看QUIC對比Http2的優(yōu)勢,解決了什么問題彬碱?
網(wǎng)上搜了不少Q(mào)UIC解析文章豆胸,不是太老就是粗略聊聊原理,沒有幾個(gè)真的深入源碼層面來證明說法是否正確堡妒,本文將主要根據(jù)QUIC最新的源碼來聊聊整個(gè)協(xié)議的設(shè)計(jì)配乱,以及這樣做的優(yōu)勢
正文
Http 發(fā)展史
在聊QUIC之前,我們需要對QUIC有一個(gè)初步的了解皮迟。QUIC本質(zhì)上是在Http之上進(jìn)一步的發(fā)展搬泥,而不是憑空出現(xiàn)的,而是為了解決Http之前的痛點(diǎn)誕生的伏尼。為此我們需要先了解http的發(fā)展忿檩,以及每一代比起上一代都解決了什么問題。
下圖是一個(gè)Http的發(fā)展進(jìn)程:
在這個(gè)發(fā)展史中可以看到在Http2.0正式推出之前爆阶,Google就開始實(shí)驗(yàn)QUIC協(xié)議燥透。并在2018年在QUIC基礎(chǔ)上進(jìn)一步的發(fā)展出Http3協(xié)議。在2021年正式發(fā)布出QUIC協(xié)議辨图。
能看到Http 1到SPDY協(xié)議中間間隔10年時(shí)間班套,究竟是為什么在Http 2.0正式發(fā)布之前,就開始了Http3前身QUIC進(jìn)行實(shí)驗(yàn)了故河?那必然是很早就被Google發(fā)現(xiàn)了Http 2.0協(xié)議的有根本性的缺陷吱韭,無法被彌補(bǔ),需要立即實(shí)驗(yàn)下一代協(xié)議鱼的。
再來看看如今Http 2.0的全網(wǎng)使用情況:
能看到目前Http 2.0全網(wǎng)使用率從發(fā)展到2022年2月份還是50%的使用率理盆,而在5月份就是驟降了5%。實(shí)際上都轉(zhuǎn)去了QUIC協(xié)議凑阶,如今已盡占比接近25%了猿规。那究竟有什么魅力,導(dǎo)致這么多開發(fā)者青睞QUIC協(xié)議呢宙橱?
帶著疑問姨俩,我們來簡單回顧一下每一代Http協(xié)議實(shí)現(xiàn)的功能蘸拔,以及缺點(diǎn)。
Http 1
在Http 1中奠定了Http協(xié)議的基本語義:
- 由請求行/狀態(tài)行环葵,body和header 構(gòu)成 Http請求
Http的缺點(diǎn)分為如下幾點(diǎn):
1.header 編碼效率低:特別是Rest 架構(gòu)都伪,往往無狀態(tài)容易,沒有對Header進(jìn)行編碼壓縮
-
2.多路復(fù)用成本過高
- 慢啟動(dòng)
- 一旦網(wǎng)絡(luò)發(fā)生異動(dòng)就需要重建連接积担,無論如何都需要3次握手陨晶,緩慢
3.一旦長連接建立了就無法中斷
4.Http 應(yīng)用層不支持流控
為了解決這些問題,就誕生出了Http 2.0協(xié)議帝璧。
Http 2.0
Http 2.0在Http 1.0基礎(chǔ)上實(shí)現(xiàn)了如下的功能:
-
1.多路復(fù)用
- 連接先誉,Stream級(jí)別流控
- 帶權(quán)重,依賴優(yōu)先級(jí)
- Stream Reset
- 應(yīng)用層數(shù)據(jù)交換單位細(xì)化到Frame(幀)
2.HPack 頭部編碼
3.服務(wù)器消息推送
關(guān)于Http 2.0詳細(xì)的設(shè)計(jì)的烁,可以閱讀我之前寫的Okhttp源碼解析中的Http2Connection
相關(guān)的源碼解析褐耳。里面有詳細(xì)剖析Okhttp是如何進(jìn)行Frame江湖,以及HPack是如何壓縮的渴庆,還有流是如何控制的铃芦。
Http 2.0的缺點(diǎn)如下:
- 1.隊(duì)頭阻塞
因?yàn)镠ttp 2.0中使用的是多路復(fù)用的流模型,一個(gè)tcp鏈接的發(fā)送數(shù)據(jù)過程中可能會(huì)把一個(gè)個(gè)請求分割成多個(gè)流發(fā)送到服務(wù)器襟雷。刃滓,因?yàn)門cp的tls加密是一個(gè)Record的加密,也就是接近10stream大小進(jìn)行加密耸弄。如果其中在某一個(gè)流丟失了咧虎,整一串都會(huì)解密失敗。
這就是Http 2.0最為嚴(yán)重的隊(duì)頭阻塞問題计呈。
- 2.建立連接速度緩慢砰诵,能看到整個(gè)過程都需要一個(gè)十分冗長的過程,三次握手捌显,tls密鑰交換等等茁彭。可以簡單看看https的建立鏈接過程:
- 3.基于TCP四元組確定一個(gè)鏈接扶歪,在移動(dòng)互聯(lián)網(wǎng)中表現(xiàn)不佳理肺。因?yàn)橐苿?dòng)設(shè)備經(jīng)常移動(dòng),可能在公交地鐵等地方击罪,出現(xiàn)了基站變換哲嘲,Wi-Fi變化等狀態(tài)贪薪。導(dǎo)致四元組發(fā)聲變化媳禁,而需要重新建立鏈接。
QUIC
Http 2.0的問題很大情況是因?yàn)門CP本身在傳輸層本身就需要保證包的有序性導(dǎo)致的画切,因此QUIC干脆拋棄TCP協(xié)議竣稽,使用UDP協(xié)議,可以看看下面QUIC的協(xié)議構(gòu)成:
Http2是基于TCP協(xié)議,并在可以獨(dú)立出tls加密協(xié)議出來毫别⊥薰可以選擇是否使用tls加密。
能看到QUIC協(xié)議本質(zhì)上是基于UDP傳輸層協(xié)議岛宦。在這個(gè)之上的應(yīng)用層是QUIC協(xié)議台丛,其中包含了tls加密協(xié)議。而未來的Http3則是在QUIC協(xié)議上進(jìn)一步發(fā)展的砾肺。
QUIC的源碼十分之多和復(fù)雜挽霉?為什么如此呢?能看到QUIC實(shí)際上是在UDP上發(fā)展变汪,那么需要保證網(wǎng)絡(luò)數(shù)據(jù)包的有序性以及正確性侠坎,就需要把類似TCP可靠協(xié)議邏輯放在QUIC中實(shí)現(xiàn)。
也正因如此裙盾,QUIC是在應(yīng)用層實(shí)現(xiàn)的協(xié)議实胸,可以很靈活的切換各種協(xié)議狀態(tài),而不需要在內(nèi)核中增加socket中netFamily的族群番官,在傳輸層增加邏輯庐完。
為什么QUIC協(xié)議中內(nèi)置tls協(xié)議呢?往后看就知道優(yōu)勢在哪里了徘熔。
QUIC 使用
在聊QUIC之前假褪,我們需要熟悉這個(gè)協(xié)議cronet
是如何使用的QUIC協(xié)議的。
估計(jì)熟悉的人不多近顷,因?yàn)?code>cronet網(wǎng)絡(luò)庫官方告訴你的依賴方式生音,需要的引擎需要通過GooglePlay獲取到cronet
的引擎才能完整的使用所有的功能。國內(nèi)環(huán)境一般是沒有GooglePlay因此如果想要使用cronet窒升,最好把源碼弄下來缀遍,自己生成so庫或者使用生成好的so庫。
這里就不多說依賴饱须,來看看如何使用的:
- 1.先生成一個(gè)
CronetEngine
引擎
CronetEngine.Builder myBuilder = new CronetEngine.Builder(context);
CronetEngine cronetEngine = myBuilder.build();
- 2.構(gòu)造一個(gè)網(wǎng)絡(luò)請求過程中域醇,不同狀態(tài)的回調(diào)函數(shù):
class MyUrlRequestCallback extends UrlRequest.Callback {
private static final String TAG = "MyUrlRequestCallback";
@Override
public void onRedirectReceived(UrlRequest request, UrlResponseInfo info, String newLocationUrl) {
request.followRedirect();
}
@Override
public void onResponseStarted(UrlRequest request, UrlResponseInfo info) {
request.read(ByteBuffer.allocateDirect(102400));
}
@Override
public void onReadCompleted(UrlRequest request, UrlResponseInfo info, ByteBuffer byteBuffer) {
request.read(ByteBuffer.allocateDirect(102400));
}
@Override
public void onSucceeded(UrlRequest request, UrlResponseInfo info) {
}
}
- 生成
UrlRequest
對象,并啟動(dòng)請求:
- 生成
Executor executor = Executors.newSingleThreadExecutor();
UrlRequest.Builder requestBuilder = cronetEngine.newUrlRequestBuilder(
"https://www.example.com", new MyUrlRequestCallback(), executor);
UrlRequest request = requestBuilder.build();
request.start();
其實(shí)就是這么簡單蓉媳。
下面是對Cronet中UrlRequest請求的生命周期:
QUIC 源碼架構(gòu)
在聊QUIC源碼之前譬挚,我們需要初步的對Cronet
的源碼架構(gòu)有一個(gè)了解。這部分的源碼實(shí)在太多酪呻,接下來的源碼使用的分支是最新的chromium
瀏覽器內(nèi)核中Cronet
模塊减宣。
下面是一個(gè)Cronet的核心類在整個(gè)Cronet的組成:
根據(jù)上面的示意圖,可以看到Cronet玩荠,將整個(gè)模塊分為如下幾個(gè)部分:
-
面向應(yīng)用的api層漆腌,如Android贼邓,iOS。
- iOS 則是由一個(gè)Cronet的中類方法通過
cronet_environment
控制cronet
引擎闷尿。 - Android 則復(fù)雜很多塑径。首先面向開發(fā)者的java-api接口,在這個(gè)api接口中有4種不同的實(shí)現(xiàn)填具,分別是
GmsCoreCronetProvider
,PlayServicesCronetProvider
统舀,NativeCronetProvider
,JavaCronetProvider
.為什么會(huì)這樣呢?其實(shí)前兩者是在Google環(huán)境和存在Google商店下內(nèi)置了Cronet的組件庫劳景,那么就可以直接復(fù)用Google為你提供的Cronet 網(wǎng)絡(luò)請求服務(wù)绑咱,從而減小包大小。當(dāng)然如果上述Cronet
引擎都找不到枢泰,就會(huì)裝載默認(rèn)的JavaCronetProvider
對象描融,通過JavaUrlRequest
使用URLConnection
進(jìn)行網(wǎng)絡(luò)請求。當(dāng)然我們可以把GmsCoreCronetProvider
,PlayServicesCronetProvider
看成NativeCronetProvider
也未嘗不可衡蚂,之后我們也只看這個(gè)引擎加載器的源碼窿克。最終NativeCronetProvider 最終會(huì)生成
CronetUrlRequest`對象交給開發(fā)者進(jìn)行請求
- iOS 則是由一個(gè)Cronet的中類方法通過
-
對于Android jni層來說,幾乎java每一個(gè)步驟下生成的Java對象都會(huì)在jni的中有一個(gè)native對象毛甲。這里只說核心的幾個(gè)年叮。而在jni中,名字帶有Adapter的對象一般都是適配器玻募,連接java層對應(yīng)在native的對象只损。
-
CronetURLRequest
對應(yīng)在jni中也有一個(gè)CronetURLRequest
負(fù)責(zé)請求的總啟動(dòng). -
CronetURLRequestAdapter
負(fù)責(zé)監(jiān)聽CronetURLRequest
回調(diào)到j(luò)ava層對應(yīng)的生命周期回調(diào)。 -
CronetUploadDataStream
控制post時(shí)候需要發(fā)送的消息體數(shù)據(jù)流
-
-
Cronet Core
也就是cronet
的核心引擎層七咧。在這個(gè)層級(jí)里面跃惫,無論是iOS還是Android最終都會(huì)調(diào)用到他們的api。其中在這個(gè)引擎層中包含了3部分艾栋,UrlRequest 控制請求事務(wù)流轉(zhuǎn)曾層
爆存,緩存與請求流控制層
,QUIC實(shí)現(xiàn)層
蝗砾。當(dāng)然這cronet
并不只是包含了qui協(xié)議c先较,同級(jí)別的具體協(xié)議實(shí)現(xiàn)還包含了如http1.0,http2.0悼粮,webscoket等闲勺,可以在UrlRequest組建的時(shí)候決定當(dāng)前請求的協(xié)議應(yīng)該是什么。URLRequest
所有的請求都會(huì)存在一個(gè)URLRequestJobFactory
請求工作工廠扣猫,當(dāng)URLRequest
需要執(zhí)行的請求的時(shí)候菜循,就會(huì)通過這個(gè)工廠生成一個(gè)Job
對象進(jìn)行生命周期的流轉(zhuǎn),當(dāng)真正執(zhí)行的時(shí)候就會(huì)把事情委托給HttpCache
,進(jìn)行流級(jí)別的控制管理HttpCache
本質(zhì)上是一個(gè)面向流的緩存苞笨≌洌可以緩存多個(gè)請求事務(wù)(Transaction),同時(shí)每個(gè)事務(wù)會(huì)控制不同的流瀑凝。而每一個(gè)新的流都會(huì)生成一個(gè)全新的HttpStreamRequest
序芦,通過JobController
創(chuàng)建一個(gè)HttpStreamFactory::Job
進(jìn)行生命周期的流轉(zhuǎn)。而在這個(gè)全新的Job粤咪,就會(huì)根據(jù)之前在URLRequest
配置好的協(xié)議谚中,進(jìn)行不同的協(xié)議請求。QUIC
協(xié)議層部分則是對應(yīng)quic
的具體實(shí)現(xiàn)寥枝。其中會(huì)生成一個(gè)QuicStreamRequest
控制每一個(gè)quic
請求流宪塔,而這個(gè)請求流會(huì)把事務(wù)委托給QuicStreamFactory
生成QuicStreamFactory::Job
工作對象。在Job中流轉(zhuǎn)整個(gè)請求的狀態(tài)囊拜。并在這個(gè)Job中控制UDP傳輸quic協(xié)議格式的數(shù)據(jù)某筐。
有一個(gè)大致的認(rèn)識(shí)后,讓我們進(jìn)一步的了解整個(gè)QUIC的運(yùn)行機(jī)制冠跷,再來看看QUIC協(xié)議中原理以及其優(yōu)越性南誊。
QUIC 源碼解析
先根據(jù)使用來看看最初幾個(gè)api的設(shè)計(jì),來看看CronetEngine.Builder
的構(gòu)造函數(shù):
public Builder(Context context) {
this(createBuilderDelegate(context));
}
private static ICronetEngineBuilder createBuilderDelegate(Context context) {
List<CronetProvider> providers =
new ArrayList<>(CronetProvider.getAllProviders(context));
CronetProvider provider = getEnabledCronetProviders(context, providers).get(0);
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG,
String.format("Using '%s' provider for creating CronetEngine.Builder.",
provider));
}
return provider.createBuilder().mBuilderDelegate;
}
能看到實(shí)際上是通過CronetProvider.getAllProviders
獲取所有的Cronet引擎提供容器,通過getEnabledCronetProviders
篩選出第一個(gè)可用的Cronet引擎蜜托。
CronetProvider getAllProviders
private static final String JAVA_CRONET_PROVIDER_CLASS =
"org.chromium.net.impl.JavaCronetProvider";
private static final String NATIVE_CRONET_PROVIDER_CLASS =
"org.chromium.net.impl.NativeCronetProvider";
private static final String PLAY_SERVICES_CRONET_PROVIDER_CLASS =
"com.google.android.gms.net.PlayServicesCronetProvider";
private static final String GMS_CORE_CRONET_PROVIDER_CLASS =
"com.google.android.gms.net.GmsCoreCronetProvider";
...
private static final String RES_KEY_CRONET_IMPL_CLASS = "CronetProviderClassName";
public static List<CronetProvider> getAllProviders(Context context) {
// Use LinkedHashSet to preserve the order and eliminate duplicate providers.
Set<CronetProvider> providers = new LinkedHashSet<>();
addCronetProviderFromResourceFile(context, providers);
addCronetProviderImplByClassName(
context, PLAY_SERVICES_CRONET_PROVIDER_CLASS, providers, false);
addCronetProviderImplByClassName(context, GMS_CORE_CRONET_PROVIDER_CLASS, providers, false);
addCronetProviderImplByClassName(context, NATIVE_CRONET_PROVIDER_CLASS, providers, false);
addCronetProviderImplByClassName(context, JAVA_CRONET_PROVIDER_CLASS, providers, false);
return Collections.unmodifiableList(new ArrayList<>(providers));
}
private static boolean addCronetProviderImplByClassName(
Context context, String className, Set<CronetProvider> providers, boolean logError) {
ClassLoader loader = context.getClassLoader();
try {
Class<? extends CronetProvider> providerClass =
loader.loadClass(className).asSubclass(CronetProvider.class);
Constructor<? extends CronetProvider> ctor =
providerClass.getConstructor(Context.class);
providers.add(ctor.newInstance(context));
return true;
} catch (InstantiationException e) {
logReflectiveOperationException(className, logError, e);
} catch (InvocationTargetException e) {
logReflectiveOperationException(className, logError, e);
} catch (NoSuchMethodException e) {
logReflectiveOperationException(className, logError, e);
} catch (IllegalAccessException e) {
logReflectiveOperationException(className, logError, e);
} catch (ClassNotFoundException e) {
logReflectiveOperationException(className, logError, e);
}
return false;
}
private static boolean addCronetProviderFromResourceFile(
Context context, Set<CronetProvider> providers) {
int resId = context.getResources().getIdentifier(
RES_KEY_CRONET_IMPL_CLASS, "string", context.getPackageName());
// Resource not found
if (resId == 0) {
// The resource wasn't included in the app; therefore, there is nothing to add.
return false;
}
String className = context.getResources().getString(resId);
if (className == null || className.equals(PLAY_SERVICES_CRONET_PROVIDER_CLASS)
|| className.equals(GMS_CORE_CRONET_PROVIDER_CLASS)
|| className.equals(JAVA_CRONET_PROVIDER_CLASS)
|| className.equals(NATIVE_CRONET_PROVIDER_CLASS)) {
return false;
}
if (!addCronetProviderImplByClassName(context, className, providers, true)) {
...
}
return true;
}
能看到整個(gè)核心就是
- 1.獲取資源ID為
CronetProviderClassName
所對應(yīng)的Cronet引擎類名抄囚。 - 2.反射內(nèi)置好的
GmsCoreCronetProvider
,PlayServicesCronetProvider
,NativeCronetProvider
,JavaCronetProvider
四種類名為默認(rèn)引擎提供器
在這里我們只需要閱讀NativeCronetProvider#createBuilder().mBuilderDelegate
相關(guān)的源碼即可橄务。
NativeCronetProvider createBuilder
public class NativeCronetProvider extends CronetProvider {
@UsedByReflection("CronetProvider.java")
public NativeCronetProvider(Context context) {
super(context);
}
@Override
public CronetEngine.Builder createBuilder() {
ICronetEngineBuilder impl = new NativeCronetEngineBuilderWithLibraryLoaderImpl(mContext);
return new ExperimentalCronetEngine.Builder(impl);
}
從名字能很侵襲的看到整個(gè)過程是一個(gè)委托者設(shè)計(jì)模式幔托,構(gòu)建一個(gè)ExperimentalCronetEngine
對象,而這個(gè)對象將真正的執(zhí)行者委托給NativeCronetEngineBuilderWithLibraryLoaderImpl
.而之前的mBuilderDelegate
就是指NativeCronetEngineBuilderWithLibraryLoaderImpl
對象蜂挪。
NativeCronetEngineBuilderWithLibraryLoaderImpl build
public class NativeCronetEngineBuilderImpl extends CronetEngineBuilderImpl {
public NativeCronetEngineBuilderImpl(Context context) {
super(context);
}
@Override
public ExperimentalCronetEngine build() {
if (getUserAgent() == null) {
setUserAgent(getDefaultUserAgent());
}
ExperimentalCronetEngine builder = new CronetUrlRequestContext(this);
mMockCertVerifier = 0;
return builder;
}
}
能看到直接返回CronetUrlRequestContext
對象作為CronetEngine
返回給應(yīng)用層重挑。之后會(huì)通過CronetUrlRequestContext
調(diào)用newUrlRequestBuilder
獲取UrlRequestBuilder。
public CronetEngineBuilderImpl(Context context) {
mApplicationContext = context.getApplicationContext();
enableQuic(true);
enableHttp2(true);
enableBrotli(false);
enableHttpCache(CronetEngine.Builder.HTTP_CACHE_DISABLED, 0);
enableNetworkQualityEstimator(false);
enablePublicKeyPinningBypassForLocalTrustAnchors(true);
}
而CronetEngineBuilderImpl
將默認(rèn)支持quic的選項(xiàng)棠涮,http2選項(xiàng)攒驰,關(guān)閉httpCache。
CronetUrlRequestContext 構(gòu)造函數(shù)
public CronetUrlRequestContext(final CronetEngineBuilderImpl builder) {
mRttListenerList.disableThreadAsserts();
mThroughputListenerList.disableThreadAsserts();
mNetworkQualityEstimatorEnabled = builder.networkQualityEstimatorEnabled();
CronetLibraryLoader.ensureInitialized(builder.getContext(), builder);
if (!IntegratedModeState.INTEGRATED_MODE_ENABLED) {
CronetUrlRequestContextJni.get().setMinLogLevel(getLoggingLevel());
}
if (builder.httpCacheMode() == HttpCacheType.DISK) {
mInUseStoragePath = builder.storagePath();
synchronized (sInUseStoragePaths) {
if (!sInUseStoragePaths.add(mInUseStoragePath)) {
throw new IllegalStateException("Disk cache storage path already in use");
}
}
} else {
mInUseStoragePath = null;
}
synchronized (mLock) {
mUrlRequestContextAdapter =
CronetUrlRequestContextJni.get().createRequestContextAdapter(
createNativeUrlRequestContextConfig(builder));
if (mUrlRequestContextAdapter == 0) {
...
}
}
// Init native Chromium URLRequestContext on init thread.
CronetLibraryLoader.postToInitThread(new Runnable() {
@Override
public void run() {
CronetLibraryLoader.ensureInitializedOnInitThread();
synchronized (mLock) {
CronetUrlRequestContextJni.get().initRequestContextOnInitThread(
mUrlRequestContextAdapter, CronetUrlRequestContext.this);
}
}
});
}
這里核心就是圍繞3個(gè)native方法:
- 1.
CronetUrlRequestContextJni.get().setMinLogLevel(getLoggingLevel())
設(shè)置日志等級(jí) - 2.
CronetUrlRequestContextJni.get().createRequestContextAdapter
通過createNativeUrlRequestContextConfig
獲取當(dāng)前Cronet
的配置在native下層創(chuàng)建一個(gè)UrlRequestContextAdapter
- 3.
CronetUrlRequestContextJni.get().initRequestContextOnInitThread
在異步線程中初始化故爵。
核心是CronetUrlRequestContextJni.get().createRequestContextAdapter
以及CronetUrlRequestContextJni.get().initRequestContextOnInitThread
玻粪。要弄懂Cronet在jni層調(diào)用之前需要了解Cronet的jni初始化的JNI_OnLoad 做了什么。
不過在這之前先來簡單看看createNativeUrlRequestContextConfig
看看UrlRequestContextConfig(UrlRequest上下文的配置)都有些什么選項(xiàng)诬垂?
createNativeUrlRequestContextConfig 創(chuàng)建Context配置對象
public static long createNativeUrlRequestContextConfig(CronetEngineBuilderImpl builder) {
final long urlRequestContextConfig =
CronetUrlRequestContextJni.get().createRequestContextConfig(builder.getUserAgent(),
builder.storagePath(), builder.quicEnabled(),
builder.getDefaultQuicUserAgentId(), builder.http2Enabled(),
builder.brotliEnabled(), builder.cacheDisabled(), builder.httpCacheMode(),
builder.httpCacheMaxSize(), builder.experimentalOptions(),
builder.mockCertVerifier(), builder.networkQualityEstimatorEnabled(),
builder.publicKeyPinningBypassForLocalTrustAnchorsEnabled(),
builder.threadPriority(Process.THREAD_PRIORITY_BACKGROUND));
...
for (CronetEngineBuilderImpl.QuicHint quicHint : builder.quicHints()) {
CronetUrlRequestContextJni.get().addQuicHint(urlRequestContextConfig, quicHint.mHost,
quicHint.mPort, quicHint.mAlternatePort);
}
for (CronetEngineBuilderImpl.Pkp pkp : builder.publicKeyPins()) {
CronetUrlRequestContextJni.get().addPkp(urlRequestContextConfig, pkp.mHost, pkp.mHashes,
pkp.mIncludeSubdomains, pkp.mExpirationDate.getTime());
}
return urlRequestContextConfig;
}
能看到配置除了上面說過的quic模式和劲室,http2模式。還有httpCache的開關(guān)以及Cache的大小结窘。
注意如果想要使用QUIC
需要設(shè)置QuicHint
很洋,告訴QUIC協(xié)議哪些url
和host
支持quic
協(xié)議。
另外隧枫,還能通過設(shè)置CronetEngineBuilderImpl.Pkp
設(shè)置默認(rèn)的加密公鑰喉磁。
cronet_jni JNI_OnLoad
extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) {
return cronet::CronetOnLoad(vm, reserved);
}
extern "C" void JNI_OnUnLoad(JavaVM* vm, void* reserved) {
cronet::CronetOnUnLoad(vm, reserved);
}
jint CronetOnLoad(JavaVM* vm, void* reserved) {
base::android::InitVM(vm);
JNIEnv* env = base::android::AttachCurrentThread();
if (!RegisterMainDexNatives(env) || !RegisterNonMainDexNatives(env)) {
return -1;
}
if (!base::android::OnJNIOnLoadInit())
return -1;
NativeInit();
return JNI_VERSION_1_6;
}
void CronetOnUnLoad(JavaVM* jvm, void* reserved) {
if (base::ThreadPoolInstance::Get())
base::ThreadPoolInstance::Get()->Shutdown();
base::android::LibraryLoaderExitHook();
}
- 1.
RegisterMainDexNatives
和RegisterNonMainDexNatives
實(shí)際上是加載通過jni_registration_generator.py
生成的cpp文件谓苟。這種文件生成出來就是為了減少dlsym()
耗時(shí)。
了解jni
的小伙伴都會(huì)清楚jni
有兩種注冊方式一種是簡單的直接聲明native方法协怒,然后通過AS可以自動(dòng)生成包名_類名_方法名
的cpp方法涝焙。而后虛擬機(jī)加載native的方法時(shí)候,就會(huì)通過dlsym()
調(diào)用查找so
動(dòng)態(tài)庫中的對應(yīng)的方法孕暇。另一種則是通過JNIEnv->RegisterNatives
手動(dòng)在JNI_OnLoad
注冊當(dāng)前的native方法關(guān)聯(lián)的java方法(注意要有指向包和類名)仑撞。
而jni_registration_generator.py
就是會(huì)遍歷所有java文件中的native方法并JNIEnv->RegisterNatives
手動(dòng)注冊的代碼cpp代碼。同時(shí)會(huì)遍歷Java文件中帶上了@CalledByNative
方法妖滔,說明這是native
想要調(diào)用java
方法隧哮,也會(huì)生成相關(guān)的反射jmethod的方法的文件。
在這里RegisterMainDexNatives
和 RegisterNonMainDexNatives
本質(zhì)上就是裝在生成好的動(dòng)態(tài)注冊的jni方法座舍。
這個(gè)不是重點(diǎn)之后有機(jī)會(huì)再仔細(xì)聊聊沮翔。
-
OnJNIOnLoadInit
這個(gè)方法就是獲取JNIUtils
類中的ClassLoader
,并獲取ClassLoader#loadClass
的jmethodID保存到全局變量g_class_loader_load_class_method_id
-
3.
NativeInit
在全局生成一個(gè)名為Cronet
的線程池。
CreateRequestContextAdapter 初始化
static jlong JNI_CronetUrlRequestContext_CreateRequestContextAdapter(
JNIEnv* env,
jlong jconfig) {
std::unique_ptr<URLRequestContextConfig> context_config(
reinterpret_cast<URLRequestContextConfig*>(jconfig));
CronetURLRequestContextAdapter* context_adapter =
new CronetURLRequestContextAdapter(std::move(context_config));
return reinterpret_cast<jlong>(context_adapter);
}
很簡答就是初始化了CronetURLRequestContextAdapter
一個(gè)cpp對象對應(yīng)java對象曲秉,并返回當(dāng)前對象的地址到j(luò)ava中鉴竭。
CreateRequestContextAdapter 頭文件
要了解一個(gè)c++的類,首先看看頭文件岸浑,然后再看看構(gòu)造函數(shù)搏存。
...
class CronetURLRequestContextAdapter
: public CronetURLRequestContext::Callback {
public:
explicit CronetURLRequestContextAdapter(
std::unique_ptr<URLRequestContextConfig> context_config);
CronetURLRequestContextAdapter(const CronetURLRequestContextAdapter&) =
delete;
CronetURLRequestContextAdapter& operator=(
const CronetURLRequestContextAdapter&) = delete;
~CronetURLRequestContextAdapter() override;
...
private:
friend class TestUtil;
// Native Cronet URL Request Context.
raw_ptr<CronetURLRequestContext> context_;
// Java object that owns this CronetURLRequestContextAdapter.
base::android::ScopedJavaGlobalRef<jobject> jcronet_url_request_context_;
};
} // namespace cronet
...
在這里其實(shí)持有了一個(gè)CronetURLRequestContext
native對象,這個(gè)對象顧名思義就是Cronet請求時(shí)候的上下文矢洲。同時(shí)持有將會(huì)持有一個(gè)UrlRequestContext
的java對象璧眠。
之后當(dāng)Cronet
的狀態(tài)發(fā)生變化都會(huì)通過這里的回調(diào)java層的CronetUrlRequestContext
中的監(jiān)聽。
CronetURLRequestContextAdapter 構(gòu)造函數(shù)
CronetURLRequestContextAdapter::CronetURLRequestContextAdapter(
std::unique_ptr<URLRequestContextConfig> context_config) {
// Create context and pass ownership of |this| (self) to the context.
std::unique_ptr<CronetURLRequestContextAdapter> self(this);
#if BUILDFLAG(INTEGRATED_MODE)
// Create CronetURLRequestContext running in integrated network task runner.
...
#else
context_ =
new CronetURLRequestContext(std::move(context_config), std::move(self));
#endif
}
CronetURLRequestContextAdapter 則會(huì)創(chuàng)建一個(gè)CronetURLRequestContext
對象
CronetURLRequestContext 頭文件
class CronetURLRequestContext {
public:
// Callback implemented by CronetURLRequestContext() caller and owned by
// CronetURLRequestContext::NetworkTasks.
class Callback {
public:
virtual ~Callback() = default;
// Invoked on network thread when initialized.
virtual void OnInitNetworkThread() = 0;
// Invoked on network thread immediately prior to destruction.
virtual void OnDestroyNetworkThread() = 0;
...
};
CronetURLRequestContext(
std::unique_ptr<URLRequestContextConfig> context_config,
std::unique_ptr<Callback> callback,
scoped_refptr<base::SingleThreadTaskRunner> network_task_runner =
nullptr);
CronetURLRequestContext(const CronetURLRequestContext&) = delete;
CronetURLRequestContext& operator=(const CronetURLRequestContext&) = delete;
// Releases all resources for the request context and deletes the object.
// Blocks until network thread is destroyed after running all pending tasks.
virtual ~CronetURLRequestContext();
// Called on init thread to initialize URLRequestContext.
void InitRequestContextOnInitThread();
...
private:
friend class TestUtil;
class ContextGetter;
class NetworkTasks : public net::EffectiveConnectionTypeObserver,
public net::RTTAndThroughputEstimatesObserver,
public net::NetworkQualityEstimator::RTTObserver,
public net::NetworkQualityEstimator::ThroughputObserver {
public:
// Invoked off the network thread.
NetworkTasks(std::unique_ptr<URLRequestContextConfig> config,
std::unique_ptr<CronetURLRequestContext::Callback> callback);
NetworkTasks(const NetworkTasks&) = delete;
NetworkTasks& operator=(const NetworkTasks&) = delete;
// Invoked on the network thread.
~NetworkTasks() override;
...
private:
...
};
...
};
} // namespace cronet
#endif // COMPONENTS_CRONET_CRONET_URL_REQUEST_CONTEXT_H_
能看到這個(gè)頭文件分為3部分:
-
CronetURLRequestContext
承載所有URLRequest
請求的上下文读虏,主要用于構(gòu)建網(wǎng)絡(luò)任務(wù)的環(huán)境责静,回調(diào)網(wǎng)絡(luò)質(zhì)量相關(guān)的回調(diào)(如rtt耗時(shí)等) -
Callback
用于回調(diào)來自NetworkQualityEstimator
對象對網(wǎng)絡(luò)質(zhì)量的監(jiān)控 -
NetworkTasks
實(shí)現(xiàn)了Callback
的回調(diào),承載網(wǎng)絡(luò)請求的起始
CronetURLRequestContext 構(gòu)造函數(shù)
CronetURLRequestContext::CronetURLRequestContext(
std::unique_ptr<URLRequestContextConfig> context_config,
std::unique_ptr<Callback> callback,
scoped_refptr<base::SingleThreadTaskRunner> network_task_runner)
: bidi_stream_detect_broken_connection_(
context_config->bidi_stream_detect_broken_connection),
heartbeat_interval_(context_config->heartbeat_interval),
default_load_flags_(
net::LOAD_NORMAL |
(context_config->load_disable_cache ? net::LOAD_DISABLE_CACHE : 0)),
network_tasks_(
new NetworkTasks(std::move(context_config), std::move(callback))),
network_task_runner_(network_task_runner) {
if (!network_task_runner_) {
network_thread_ = std::make_unique<base::Thread>("network");
base::Thread::Options options;
options.message_pump_type = base::MessagePumpType::IO;
network_thread_->StartWithOptions(std::move(options));
network_task_runner_ = network_thread_->task_runner();
}
}
初始化一個(gè)NetworkTasks
作為一個(gè)網(wǎng)絡(luò)請求任務(wù)承載者盖桥。并初始化一個(gè)名為network
的線程灾螃,并以這個(gè)線程創(chuàng)建一個(gè)looper
賦值給network_task_runner_
,之后所有的請求任務(wù)都會(huì)在這個(gè)loop中開始揩徊。
initRequestContextOnInitThread
void CronetURLRequestContext::InitRequestContextOnInitThread() {
DCHECK(OnInitThread());
auto proxy_config_service =
cronet::CreateProxyConfigService(GetNetworkTaskRunner());
g_net_log.Get().EnsureInitializedOnInitThread();
GetNetworkTaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(&CronetURLRequestContext::NetworkTasks::Initialize,
base::Unretained(network_tasks_), GetNetworkTaskRunner(),
GetFileThread()->task_runner(),
std::move(proxy_config_service)));
}
GetNetworkTaskRunner
獲取network_task_runner_
調(diào)用PostTask
進(jìn)入切換到network
線程的loop腰鬼,執(zhí)行CronetURLRequestContext::NetworkTasks::Initialize
方法
void CronetURLRequestContext::NetworkTasks::Initialize(
scoped_refptr<base::SingleThreadTaskRunner> network_task_runner,
scoped_refptr<base::SequencedTaskRunner> file_task_runner,
std::unique_ptr<net::ProxyConfigService> proxy_config_service) {
std::unique_ptr<URLRequestContextConfig> config(std::move(context_config_));
network_task_runner_ = network_task_runner;
if (config->network_thread_priority)
SetNetworkThreadPriorityOnNetworkThread(
config->network_thread_priority.value());
base::DisallowBlocking();
net::URLRequestContextBuilder context_builder;
context_builder.set_network_delegate(
std::make_unique<BasicNetworkDelegate>());
context_builder.set_net_log(g_net_log.Get().net_log());
context_builder.set_proxy_resolution_service(
cronet::CreateProxyResolutionService(std::move(proxy_config_service),
g_net_log.Get().net_log()));
config->ConfigureURLRequestContextBuilder(&context_builder);
effective_experimental_options_ =
base::Value(config->effective_experimental_options);
if (config->enable_network_quality_estimator) {
std::unique_ptr<net::NetworkQualityEstimatorParams> nqe_params =
std::make_unique<net::NetworkQualityEstimatorParams>(
std::map<std::string, std::string>());
if (config->nqe_forced_effective_connection_type) {
nqe_params->SetForcedEffectiveConnectionType(
config->nqe_forced_effective_connection_type.value());
}
network_quality_estimator_ = std::make_unique<net::NetworkQualityEstimator>(
std::move(nqe_params), g_net_log.Get().net_log());
network_quality_estimator_->AddEffectiveConnectionTypeObserver(this);
network_quality_estimator_->AddRTTAndThroughputEstimatesObserver(this);
context_builder.set_network_quality_estimator(
network_quality_estimator_.get());
}
...
// Disable net::CookieStore.
context_builder.SetCookieStore(nullptr);
context_ = context_builder.Build();
..
if (config->enable_quic) {
for (const auto& quic_hint : config->quic_hints) {
if (quic_hint->host.empty()) {
...
continue;
}
url::CanonHostInfo host_info;
std::string canon_host(
net::CanonicalizeHost(quic_hint->host, &host_info));
if (!host_info.IsIPAddress() &&
!net::IsCanonicalizedHostCompliant(canon_host)) {
...
continue;
}
if (quic_hint->port <= std::numeric_limits<uint16_t>::min() ||
quic_hint->port > std::numeric_limits<uint16_t>::max()) {
...
continue;
}
if (quic_hint->alternate_port <= std::numeric_limits<uint16_t>::min() ||
quic_hint->alternate_port > std::numeric_limits<uint16_t>::max()) {
...
continue;
}
url::SchemeHostPort quic_server("https", canon_host, quic_hint->port);
net::AlternativeService alternative_service(
net::kProtoQUIC, "",
static_cast<uint16_t>(quic_hint->alternate_port));
context_->http_server_properties()->SetQuicAlternativeService(
quic_server, net::NetworkIsolationKey(), alternative_service,
base::Time::Max(), quic::ParsedQuicVersionVector());
}
}
for (const auto& pkp : config->pkp_list) {
// Add the host pinning.
context_->transport_security_state()->AddHPKP(
pkp->host, pkp->expiration_date, pkp->include_subdomains,
pkp->pin_hashes, GURL::EmptyGURL());
}
context_->transport_security_state()
->SetEnablePublicKeyPinningBypassForLocalTrustAnchors(
config->bypass_public_key_pinning_for_local_trust_anchors);
callback_->OnInitNetworkThread();
is_context_initialized_ = true;
if (config->enable_network_quality_estimator && cronet_prefs_manager_) {
network_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&CronetURLRequestContext::NetworkTasks::InitializeNQEPrefs,
base::Unretained(this)));
}
#if BUILDFLAG(ENABLE_REPORTING)
if (context_->reporting_service()) {
for (const auto& preloaded_header : config->preloaded_report_to_headers) {
context_->reporting_service()->ProcessReportToHeader(
preloaded_header.origin, net::NetworkIsolationKey(),
preloaded_header.value);
}
}
if (context_->network_error_logging_service()) {
for (const auto& preloaded_header : config->preloaded_nel_headers) {
context_->network_error_logging_service()->OnHeader(
net::NetworkIsolationKey(), preloaded_header.origin, net::IPAddress(),
preloaded_header.value);
}
}
#endif // BUILDFLAG(ENABLE_REPORTING)
while (!tasks_waiting_for_context_.empty()) {
std::move(tasks_waiting_for_context_.front()).Run();
tasks_waiting_for_context_.pop();
}
}
別看這段代碼很長實(shí)際上做的事情也就如下幾件:
- 1.根據(jù)
URLRequestContextConfig
裝載出NetworkQualityEstimator
網(wǎng)絡(luò)質(zhì)量監(jiān)控器 - 2.創(chuàng)建
URLRequestContext
對象,為之后的UrlRequest
做準(zhǔn)備 - 3.如果
URLRequestContextConfig
允許了quic協(xié)議那么會(huì)加載所有的QuicHint
中的資源路徑塑荒,端口號(hào)作為識(shí)別熄赡。之后遇到這些請求就會(huì)使用quic協(xié)議,最后生成的SchemeHostPort
通過SetQuicAlternativeService
保存到URLRequestContext
到目前為止java層的CronetUrlRequestContext
通過native層的CronetUrlRequestContextAdapter
創(chuàng)建了一個(gè)對應(yīng)在native層的CronetUrlRequestContext
對象進(jìn)行一一對應(yīng)齿税。
當(dāng)準(zhǔn)備好了CronetUrlRequestContext
,就可以使用CronetUrlRequestContext
創(chuàng)建newUrlRequestBuilder
請求
CronetEngineBase newUrlRequestBuilder 創(chuàng)建請求對象
@Override
public ExperimentalUrlRequest.Builder newUrlRequestBuilder(
String url, UrlRequest.Callback callback, Executor executor) {
return new UrlRequestBuilderImpl(url, callback, executor, this);
}
UrlRequestBuilderImpl createRequest 創(chuàng)建請求對象
@Override
public UrlRequestBase createRequest(String url, UrlRequest.Callback callback, Executor executor,
int priority, Collection<Object> requestAnnotations, boolean disableCache,
boolean disableConnectionMigration, boolean allowDirectExecutor,
boolean trafficStatsTagSet, int trafficStatsTag, boolean trafficStatsUidSet,
int trafficStatsUid, RequestFinishedInfo.Listener requestFinishedListener,
int idempotency) {
synchronized (mLock) {
checkHaveAdapter();
return new CronetUrlRequest(this, url, priority, callback, executor, requestAnnotations,
disableCache, disableConnectionMigration, allowDirectExecutor,
trafficStatsTagSet, trafficStatsTag, trafficStatsUidSet, trafficStatsUid,
requestFinishedListener, idempotency);
}
}
很簡單彼硫,這里把之前在UrlRequestBuilderImpl
組合的參數(shù)都保存到CronetUrlRequest
返回給應(yīng)用層。
CronetUrlRequest.start 啟動(dòng)請求
@Override
public void start() {
synchronized (mUrlRequestAdapterLock) {
checkNotStarted();
try {
mUrlRequestAdapter = CronetUrlRequestJni.get().createRequestAdapter(
CronetUrlRequest.this, mRequestContext.getUrlRequestContextAdapter(),
mInitialUrl, mPriority, mDisableCache, mDisableConnectionMigration,
mRequestContext.hasRequestFinishedListener()
|| mRequestFinishedListener != null,
mTrafficStatsTagSet, mTrafficStatsTag, mTrafficStatsUidSet,
mTrafficStatsUid, mIdempotency);
mRequestContext.onRequestStarted();
if (mInitialMethod != null) {
if (!CronetUrlRequestJni.get().setHttpMethod(
mUrlRequestAdapter, CronetUrlRequest.this, mInitialMethod)) {
...
}
}
boolean hasContentType = false;
for (Map.Entry<String, String> header : mRequestHeaders) {
if (header.getKey().equalsIgnoreCase("Content-Type")
&& !header.getValue().isEmpty()) {
hasContentType = true;
}
if (!CronetUrlRequestJni.get().addRequestHeader(mUrlRequestAdapter,
CronetUrlRequest.this, header.getKey(), header.getValue())) {
...
}
}
if (mUploadDataStream != null) {
if (!hasContentType) {
...
}
mStarted = true;
mUploadDataStream.postTaskToExecutor(new Runnable() {
@Override
public void run() {
mUploadDataStream.initializeWithRequest();
synchronized (mUrlRequestAdapterLock) {
if (isDoneLocked()) {
return;
}
mUploadDataStream.attachNativeAdapterToRequest(mUrlRequestAdapter);
startInternalLocked();
}
}
});
return;
}
} catch (RuntimeException e) {
...
}
mStarted = true;
startInternalLocked();
}
}
@GuardedBy("mUrlRequestAdapterLock")
private void startInternalLocked() {
CronetUrlRequestJni.get().start(mUrlRequestAdapter, CronetUrlRequest.this);
}
這里圍繞著4個(gè)核心的jni方法:
- 1.
createRequestAdapter
生成一個(gè)jni的UrlRequestAdapter
對象 - 2.回調(diào)
onRequestStarted
生命周期 - 3.
setHttpMethod
為jni的UrlRequestAdapter
設(shè)置 http請求類型 - 4.
addRequestHeader
為請求裝載header - 5.
mUploadDataStream
讀取并把body的數(shù)據(jù)緩存到j(luò)ni的UploadDataStream
中. - 6.調(diào)用
startInternalLocked
也就是CronetUrlRequest
的start
CronetURLRequestAdapter 創(chuàng)建
static jlong JNI_CronetUrlRequest_CreateRequestAdapter(
JNIEnv* env,
const JavaParamRef<jobject>& jurl_request,
jlong jurl_request_context_adapter,
const JavaParamRef<jstring>& jurl_string,
jint jpriority,
jboolean jdisable_cache,
jboolean jdisable_connection_migration,
jboolean jenable_metrics,
jboolean jtraffic_stats_tag_set,
jint jtraffic_stats_tag,
jboolean jtraffic_stats_uid_set,
jint jtraffic_stats_uid,
jint jidempotency) {
CronetURLRequestContextAdapter* context_adapter =
reinterpret_cast<CronetURLRequestContextAdapter*>(
jurl_request_context_adapter);
CronetURLRequestAdapter* adapter = new CronetURLRequestAdapter(
context_adapter, env, jurl_request, url,
static_cast<net::RequestPriority>(jpriority), jdisable_cache,
jdisable_connection_migration, jenable_metrics, jtraffic_stats_tag_set,
jtraffic_stats_tag, jtraffic_stats_uid_set, jtraffic_stats_uid,
static_cast<net::Idempotency>(jidempotency));
return reinterpret_cast<jlong>(adapter);
}
CronetURLRequestAdapter::CronetURLRequestAdapter(
CronetURLRequestContextAdapter* context,
JNIEnv* env,
jobject jurl_request,
const GURL& url,
net::RequestPriority priority,
jboolean jdisable_cache,
jboolean jdisable_connection_migration,
jboolean jenable_metrics,
jboolean jtraffic_stats_tag_set,
jint jtraffic_stats_tag,
jboolean jtraffic_stats_uid_set,
jint jtraffic_stats_uid,
net::Idempotency idempotency)
: request_(
new CronetURLRequest(context->cronet_url_request_context(),
std::unique_ptr<CronetURLRequestAdapter>(this),
url,
priority,
jdisable_cache == JNI_TRUE,
jdisable_connection_migration == JNI_TRUE,
jenable_metrics == JNI_TRUE,
jtraffic_stats_tag_set == JNI_TRUE,
jtraffic_stats_tag,
jtraffic_stats_uid_set == JNI_TRUE,
jtraffic_stats_uid,
idempotency)) {
owner_.Reset(env, jurl_request);
}
CronetURLRequestContextAdapter
持有一個(gè) CronetURLRequest
對象。剛剛好對應(yīng)java層中的CronetURLRequest
拧篮。整個(gè)請求的發(fā)起就是從CronetURLRequest
開始词渤。
CronetURLRequest 構(gòu)造函數(shù)
CronetURLRequest::CronetURLRequest(CronetURLRequestContext* context,
std::unique_ptr<Callback> callback,
const GURL& url,
net::RequestPriority priority,
bool disable_cache,
bool disable_connection_migration,
bool enable_metrics,
bool traffic_stats_tag_set,
int32_t traffic_stats_tag,
bool traffic_stats_uid_set,
int32_t traffic_stats_uid,
net::Idempotency idempotency)
: context_(context),
network_tasks_(std::move(callback),
url,
priority,
CalculateLoadFlags(context->default_load_flags(),
disable_cache,
disable_connection_migration),
enable_metrics,
traffic_stats_tag_set,
traffic_stats_tag,
traffic_stats_uid_set,
traffic_stats_uid,
idempotency),
initial_method_("GET"),
initial_request_headers_(std::make_unique<net::HttpRequestHeaders>()) {
}
能看到CronetURLRequest
默認(rèn)設(shè)置GET
http的方法,同時(shí)創(chuàng)建HttpRequestHeaders
接受Http協(xié)議的頭部信息串绩。
CronetURLRequest start
java方法CronetUrlRequestJni.get().start
所對應(yīng)的jni方法如下德澈,也就是CronetURLRequest
的start方法滴须。
void CronetURLRequestAdapter::Start(JNIEnv* env,
const JavaParamRef<jobject>& jcaller) {
request_->Start();
}
void CronetURLRequest::Start() {
DCHECK(!context_->IsOnNetworkThread());
context_->PostTaskToNetworkThread(
FROM_HERE,
base::BindOnce(&CronetURLRequest::NetworkTasks::Start,
base::Unretained(&network_tasks_),
base::Unretained(context_), initial_method_,
std::move(initial_request_headers_), std::move(upload_)));
}
start方法其實(shí)就是切換到network線程棚赔。調(diào)用NetworkTasks
名為start類方法脉幢。
NetworkTasks Start
void CronetURLRequest::NetworkTasks::Start(
CronetURLRequestContext* context,
const std::string& method,
std::unique_ptr<net::HttpRequestHeaders> request_headers,
std::unique_ptr<net::UploadDataStream> upload) {
url_request_ = context->GetURLRequestContext()->CreateRequest(
initial_url_, net::DEFAULT_PRIORITY, this, MISSING_TRAFFIC_ANNOTATION);
url_request_->SetLoadFlags(initial_load_flags_);
url_request_->set_method(method);
url_request_->SetExtraRequestHeaders(*request_headers);
url_request_->SetPriority(initial_priority_);
url_request_->SetIdempotency(idempotency_);
std::string referer;
if (request_headers->GetHeader(net::HttpRequestHeaders::kReferer, &referer)) {
url_request_->SetReferrer(referer);
}
if (upload)
url_request_->set_upload(std::move(upload));
if (traffic_stats_tag_set_ || traffic_stats_uid_set_) {
#if BUILDFLAG(IS_ANDROID)
url_request_->set_socket_tag(net::SocketTag(
traffic_stats_uid_set_ ? traffic_stats_uid_ : net::SocketTag::UNSET_UID,
traffic_stats_tag_set_ ? traffic_stats_tag_
: net::SocketTag::UNSET_TAG));
#else
...
#endif
}
url_request_->Start();
}
GetURLRequestContext()->CreateRequest
創(chuàng)造一個(gè)URLRequest
對象益咬。將保存在CronetURLRequest
填充到URLRequest
中佳窑,并調(diào)用這個(gè)對象的start方法单旁。
而這個(gè)URLRequest
你可以看成Cronet的內(nèi)核對外的最重要的接口墨林。因?yàn)閕OS的模塊最終也是對接到URLRequest
對象中腰涧。
URLRequest Start
void URLRequest::Start() {
if (status_ != OK)
return;
...
...
StartJob(context_->job_factory()->CreateJob(this));
}
很見到在這里獲取job_factory
通過CreateJob
創(chuàng)建一個(gè)URLRequestJob
工作項(xiàng)韧掩,并調(diào)用UrlRequest
的StartJob
啟動(dòng)URLRequestJob。
簡單看看CreateJob
返回的是什么類型的URLRequestJob
.
std::unique_ptr<URLRequestJob> URLRequestJobFactory::CreateJob(
URLRequest* request) const {
if (!request->url().is_valid())
return std::make_unique<URLRequestErrorJob>(request, ERR_INVALID_URL);
if (g_interceptor_for_testing) {
std::unique_ptr<URLRequestJob> job(
g_interceptor_for_testing->MaybeInterceptRequest(request));
if (job)
return job;
}
auto it = protocol_handler_map_.find(request->url().scheme());
if (it == protocol_handler_map_.end()) {
return std::make_unique<URLRequestErrorJob>(request,
ERR_UNKNOWN_URL_SCHEME);
}
return it->second->CreateJob(request);
}
實(shí)際上在不同的協(xié)議都會(huì)對應(yīng)上不同的URLRequestJob
工廠窖铡,而這些網(wǎng)絡(luò)協(xié)議創(chuàng)建工廠為ProtocolHandler
.這些ProtocolHandler
都可以通過設(shè)置到protocol_handler_map_
中疗锐,根據(jù)協(xié)議頭scheme進(jìn)行自定義協(xié)議實(shí)現(xiàn)。
而在這個(gè)內(nèi)核層中费彼,默認(rèn)自帶了HttpProtocolHandler
實(shí)現(xiàn),如下:
class HttpProtocolHandler : public URLRequestJobFactory::ProtocolHandler {
public:
explicit HttpProtocolHandler(bool is_for_websockets)
: is_for_websockets_(is_for_websockets) {}
HttpProtocolHandler(const HttpProtocolHandler&) = delete;
HttpProtocolHandler& operator=(const HttpProtocolHandler&) = delete;
~HttpProtocolHandler() override = default;
std::unique_ptr<URLRequestJob> CreateJob(URLRequest* request) const override {
if (request->is_for_websockets() != is_for_websockets_) {
return std::make_unique<URLRequestErrorJob>(request,
ERR_UNKNOWN_URL_SCHEME);
}
return URLRequestHttpJob::Create(request);
}
const bool is_for_websockets_;
};
能看到默認(rèn)的 Http對應(yīng)的協(xié)議處理器HttpProtocolHandler
,并通過CreateJob
創(chuàng)建請求任務(wù)對應(yīng)URLRequestHttpJob
滑臊。而這個(gè)的設(shè)置時(shí)機(jī):
URLRequestJobFactory::URLRequestJobFactory() {
SetProtocolHandler(url::kHttpScheme, std::make_unique<HttpProtocolHandler>(
/*is_for_websockets=*/false));
SetProtocolHandler(url::kHttpsScheme, std::make_unique<HttpProtocolHandler>(
/*is_for_websockets=*/false));
#if BUILDFLAG(ENABLE_WEBSOCKETS)
SetProtocolHandler(url::kWsScheme, std::make_unique<HttpProtocolHandler>(
/*is_for_websockets=*/true));
SetProtocolHandler(url::kWssScheme, std::make_unique<HttpProtocolHandler>(
/*is_for_websockets=*/true));
#endif // BUILDFLAG(ENABLE_WEBSOCKETS)
}
job_factory
也就是URLRequestJobFactory
類型,能看到構(gòu)造函數(shù)中默認(rèn)的設(shè)置了http和https箍铲,ws,wss的協(xié)議處理器雇卷。
void URLRequest::StartJob(std::unique_ptr<URLRequestJob> job) {
...
job_ = std::move(job);
job_->SetExtraRequestHeaders(extra_request_headers_);
job_->SetPriority(priority_);
job_->SetRequestHeadersCallback(request_headers_callback_);
job_->SetEarlyResponseHeadersCallback(early_response_headers_callback_);
job_->SetResponseHeadersCallback(response_headers_callback_);
if (upload_data_stream_.get())
job_->SetUpload(upload_data_stream_.get());
...
job_->Start();
}
很簡單就是把URLRequest 中的頭部,優(yōu)先級(jí)颠猴,回調(diào)关划,消息體的數(shù)據(jù)流引用數(shù)據(jù)保存到URLRequestJob
,并調(diào)用URLRequestJob
的Start翘瓮。此時(shí)URLRequestJob
一般是指URLRequestHttpJob
.
URLRequestHttpJob Start
void URLRequestHttpJob::Start() {
request_info_.url = request_->url();
request_info_.method = request_->method();
request_info_.network_isolation_key =
request_->isolation_info().network_isolation_key();
request_info_.possibly_top_frame_origin =
request_->isolation_info().top_frame_origin();
request_info_.is_subframe_document_resource =
request_->isolation_info().request_type() ==
net::IsolationInfo::RequestType::kSubFrame;
request_info_.load_flags = request_->load_flags();
request_info_.secure_dns_policy = request_->secure_dns_policy();
request_info_.traffic_annotation =
net::MutableNetworkTrafficAnnotationTag(request_->traffic_annotation());
request_info_.socket_tag = request_->socket_tag();
request_info_.idempotency = request_->GetIdempotency();
#if BUILDFLAG(ENABLE_REPORTING)
request_info_.reporting_upload_depth = request_->reporting_upload_depth();
#endif
bool should_add_cookie_header = ShouldAddCookieHeader();
if (!should_add_cookie_header) {
OnGotFirstPartySetMetadata(FirstPartySetMetadata());
return;
}
absl::optional<FirstPartySetMetadata> metadata =
cookie_util::ComputeFirstPartySetMetadataMaybeAsync(
SchemefulSite(request()->url()), request()->isolation_info(),
request()->context()->cookie_store()->cookie_access_delegate(),
request()->force_ignore_top_frame_party_for_cookies(),
base::BindOnce(&URLRequestHttpJob::OnGotFirstPartySetMetadata,
weak_factory_.GetWeakPtr()));
if (metadata.has_value())
OnGotFirstPartySetMetadata(std::move(metadata.value()));
}
將UrlRequest
的請求參數(shù)保存到request_info_
贮折。如果沒有任何的cookie則直接調(diào)用OnGotFirstPartySetMetadata
,如果存在全局通用cookie,則把數(shù)據(jù)保存到FirstPartySetMetadata
。并調(diào)用OnGotFirstPartySetMetadata
.
OnGotFirstPartySetMetadata
void URLRequestHttpJob::OnGotFirstPartySetMetadata(
FirstPartySetMetadata first_party_set_metadata) {
first_party_set_metadata_ = std::move(first_party_set_metadata);
request_info_.privacy_mode = DeterminePrivacyMode();
...
GURL referrer(request_->referrer());
if (referrer.is_valid()) {
std::string referer_value = referrer.spec();
request_info_.extra_headers.SetHeader(HttpRequestHeaders::kReferer,
referer_value);
}
request_info_.extra_headers.SetHeaderIfMissing(
HttpRequestHeaders::kUserAgent,
http_user_agent_settings_ ?
http_user_agent_settings_->GetUserAgent() : std::string());
AddExtraHeaders();
if (ShouldAddCookieHeader()) {
cookie_partition_key_ =
absl::make_optional(CookiePartitionKey::FromNetworkIsolationKey(
request_->isolation_info().network_isolation_key(),
base::OptionalOrNullptr(
first_party_set_metadata_.top_frame_owner())));
AddCookieHeaderAndStart();
} else {
StartTransaction();
}
}
- 1.先通過
SetHeader
以及AddExtraHeaders
設(shè)置Referer
,GZIP
等常用的Header - 2.如果存在cookie則通過
AddCookieHeaderAndStart
添加到Header中Cookie
為key的數(shù)據(jù)集合中资盅。不過 - 3.
StartTransaction
啟動(dòng)事務(wù)调榄。
URLRequestHttpJob StartTransaction
void URLRequestHttpJob::StartTransaction() {
...
StartTransactionInternal();
}
void URLRequestHttpJob::StartTransactionInternal() {
int rv;
...
if (transaction_.get()) {
rv = transaction_->RestartWithAuth(
auth_credentials_, base::BindOnce(&URLRequestHttpJob::OnStartCompleted,
base::Unretained(this)));
auth_credentials_ = AuthCredentials();
} else {
rv = request_->context()->http_transaction_factory()->CreateTransaction(
priority_, &transaction_);
...
if (rv == OK) {
transaction_->SetConnectedCallback(base::BindRepeating(
&URLRequestHttpJob::NotifyConnectedCallback, base::Unretained(this)));
transaction_->SetRequestHeadersCallback(request_headers_callback_);
transaction_->SetEarlyResponseHeadersCallback(
early_response_headers_callback_);
transaction_->SetResponseHeadersCallback(response_headers_callback_);
if (!throttling_entry_.get() ||
!throttling_entry_->ShouldRejectRequest(*request_)) {
rv = transaction_->Start(
&request_info_,
base::BindOnce(&URLRequestHttpJob::OnStartCompleted,
base::Unretained(this)),
request_->net_log());
start_time_ = base::TimeTicks::Now();
} else {
// Special error code for the exponential back-off module.
rv = ERR_TEMPORARILY_THROTTLED;
}
}
}
if (rv == ERR_IO_PENDING)
return;
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&URLRequestHttpJob::OnStartCompleted,
weak_factory_.GetWeakPtr(), rv));
}
能看到這個(gè)過程中存在一個(gè)核心的對象transaction_
也就是HttpTransaction
。之后請求Job工作項(xiàng)呵扛,就將請求委托給HttpTransaction
.
如果發(fā)現(xiàn)URLRequestHttpJob
已經(jīng)存在了HttpTransaction
振峻,那么就會(huì)調(diào)用HttpTransaction
的RestartWithAuth
重新啟動(dòng)并且校驗(yàn)權(quán)限。
如果發(fā)現(xiàn)沒有創(chuàng)建择份,則調(diào)用事務(wù)工廠CreateTransaction
創(chuàng)建HttpTransaction
扣孟,然后調(diào)用Start方法正式啟動(dòng)事務(wù),開始請求荣赶。
總結(jié)
首先進(jìn)行一個(gè)初步的總結(jié)凤价,到了HttpTransaction
之后鸽斟,就會(huì)開始流轉(zhuǎn)請求的生命周期,然后進(jìn)行quic協(xié)議的初始化利诺,執(zhí)行quic的請求富蓄。
不過限于篇幅,以及Cronet設(shè)計(jì)上的確實(shí)比較冗長慢逾,這里先做一個(gè)簡單的總結(jié)先:
可以將Cronet的設(shè)計(jì)組合看成3層:
- 1.java的api層
- 2.用于連通java和native的jni的adapter層
- 3.通用于所有平臺(tái)的內(nèi)核層
java層會(huì)通過反射嘗試獲取不同環(huán)境依賴下的cronetProvider立倍,也就是Cornet的內(nèi)核提供器。有的是依賴Google環(huán)境侣滩,有的可以自己自己直接依賴native的包口注,都沒有則使用默認(rèn)的 android自帶的網(wǎng)絡(luò)請求。
jni層君珠,實(shí)際上就是末尾帶上了Adapter的類以及和java層中相同類名的類寝志,這些類一般不做任何事情,一般會(huì)包裹一個(gè)對應(yīng)相同名字的cpp對象在native中策添,并且把相同的行為賦予給Adpater以及對應(yīng)的native對象材部。
java層CronetUrlRequestContext 會(huì)對應(yīng)上 jni中的
CronetURLRequestContextAdapter
作為樞紐,間接控制native中的CronetURLRequestContext
唯竹。而CronetURLRequestContext
則是控制了整個(gè)請求的上下文java層
CronetUrlRequest
會(huì)對應(yīng)上jni中的CronetURLRequestAdapter
,并間接控制native層的CronetUrlRequest
對象乐导。
而這個(gè)對象最終會(huì)控制native層的UrlRequest
,而這個(gè)對象最終會(huì)通向Cronet的內(nèi)核層浸颓。并且會(huì)從thridParty文件夾中找到quic協(xié)議相關(guān)的處理物臂。
后續(xù)的文章將會(huì)繼續(xù)揭曉HttpTransaction
如何進(jìn)行事務(wù)流轉(zhuǎn),并且quic是如何執(zhí)行猾愿。