日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

OkHttp3中的HTTP/2首部压缩

發(fā)布時間:2024/4/11 编程问答 51 豆豆
生活随笔 收集整理的這篇文章主要介紹了 OkHttp3中的HTTP/2首部压缩 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

當(dāng)前網(wǎng)絡(luò)環(huán)境中,同一個頁面發(fā)出幾十個HTTP請求已經(jīng)是司空見慣的事情了。在HTTP/1.1中,請求之間完全相互獨立,使得請求中冗余的首部字段不必要地浪費了大量的網(wǎng)絡(luò)帶寬,并增加了網(wǎng)絡(luò)延時。以對某站點的一次頁面訪問為例,直觀地看一下這種狀況:


Header 1
Header 2

如上圖,同一個頁面中對兩個資源的請求,請求中的頭部字段絕大部分是完全相同的。"User-Agent" 等頭部字段通常還會消耗大量的帶寬。

首部壓縮正是為了解決這樣的問題而設(shè)計。

首部壓縮是HTTP/2中一個非常重要的特性,它大大減少了網(wǎng)絡(luò)中HTTP請求/響應(yīng)頭部傳輸所需的帶寬。HTTP/2的首部壓縮,主要從兩個方面實現(xiàn),一是首部表示,二是請求間首部字段內(nèi)容的復(fù)用。

首部表示

在HTTP中,首部字段是一個名值隊,所有的首部字段組成首部字段列表。在HTTP/1.x中,首部字段都被表示為字符串,一行一行的首部字段字符串組成首部字段列表。而在HTTP/2的首部壓縮HPACK算法中,則有著不同的表示方法。

HPACK算法表示的對象,主要有原始數(shù)據(jù)類型的整型值和字符串,頭部字段,以及頭部字段列表。

整數(shù)的表示

在HPACK中,整數(shù)用于表示 頭部字段的名字的索引頭部字段索引字符串長度。整數(shù)的表示可在字節(jié)內(nèi)的任何位置開始。但為了處理上的優(yōu)化,整數(shù)的表示總是在字節(jié)的結(jié)尾處結(jié)束。

整數(shù)由兩部分表示:填滿當(dāng)前字節(jié)的前綴,以及在前綴不足以表示整數(shù)時的一個可選字節(jié)列表。如果整數(shù)值足夠小,比如,小于2^N-1,那么把它編碼進(jìn)前綴即可,而不需要額外的空間。如:

0 1 2 3 4 5 6 7 +---+---+---+---+---+---+---+---+ | ? | ? | ? | Value | +---+---+---+-------------------+

在這個圖中,前綴有5位,而要表示的數(shù)足夠小,因此無需更多空間就可以表示整數(shù)了。

當(dāng)前綴不足以表示整數(shù)時,前綴的所有位被置為1,再將值減去2^N-1之后用一個或多個字節(jié)編碼。每個字節(jié)的最高有效位被用作連續(xù)標(biāo)記:除列表的最后一個字節(jié)外,該位的值都被設(shè)為1。字節(jié)中剩余的位被用于編碼減小后的值。

0 1 2 3 4 5 6 7 +---+---+---+---+---+---+---+---+ | ? | ? | ? | 1 1 1 1 1 | +---+---+---+-------------------+ | 1 | Value-(2^N-1) LSB | +---+---------------------------+... +---+---------------------------+ | 0 | Value-(2^N-1) MSB | +---+---------------------------+

要由字節(jié)列表解碼出整數(shù)值,首先需要將列表中的字節(jié)順序反過來。然后,移除每個字節(jié)的最高有效位。連接字節(jié)的剩余位,再將結(jié)果加2^N-1獲得整數(shù)值。

前綴大小N,總是在1到8之間。從字節(jié)邊界處開始編碼的整數(shù)值其前綴為8位。

這種整數(shù)表示法允許編碼無限大的值。

表示整數(shù)I的偽代碼如下:

if I < 2^N - 1, encode I on N bits elseencode (2^N - 1) on N bitsI = I - (2^N - 1)while I >= 128encode (I % 128 + 128) on 8 bitsI = I / 128encode I on 8 bits

encode (I % 128 + 128) on 8 bits 一行中,加上128的意思是,最高有效位是1。如果要編碼的整數(shù)值等于 (2^N - 1),則用前綴和緊跟在前綴后面的值位0的一個字節(jié)來表示。

OkHttp中,這個算法的實現(xiàn)在 okhttp3.internal.http2.Hpack.Writer 中:

// http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-12#section-4.1.1void writeInt(int value, int prefixMask, int bits) {// Write the raw value for a single byte value.if (value < prefixMask) {out.writeByte(bits | value);return;}// Write the mask to start a multibyte value.out.writeByte(bits | prefixMask);value -= prefixMask;// Write 7 bits at a time 'til we're done.while (value >= 0x80) {int b = value & 0x7f;out.writeByte(b | 0x80);value >>>= 7;}out.writeByte(value);}

這里給最高有效位置 1 的方法就不是加上128,而是與0x80執(zhí)行或操作。

解碼整數(shù)I的偽代碼如下:

decode I from the next N bits if I < 2^N - 1, return I elseM = 0repeatB = next octetI = I + (B & 127) * 2^MM = M + 7while B & 128 == 128return I

decode I from the next N bits 這一行等價于一個賦值語句 *I = byteValue & (2^N - 1)

OkHttp中,這個算法的實現(xiàn)在 okhttp3.internal.http2.Hpack.Reader

int readInt(int firstByte, int prefixMask) throws IOException {int prefix = firstByte & prefixMask;if (prefix < prefixMask) {return prefix; // This was a single byte value.}// This is a multibyte value. Read 7 bits at a time.int result = prefixMask;int shift = 0;while (true) {int b = readByte();if ((b & 0x80) != 0) { // Equivalent to (b >= 128) since b is in [0..255].result += (b & 0x7f) << shift;shift += 7;} else {result += b << shift; // Last byte.break;}}return result;}

盡管HPACK的整數(shù)表示方法可以表示無限大的數(shù),但實際的實現(xiàn)中并不會將整數(shù)當(dāng)做無限大的整數(shù)來處理。

字符串字面量的編碼

頭部字段名和頭部字段值可使用字符串字面量表示。字符串字面量有兩種表示方式,一種是直接用UTF-8這樣的字符串編碼方式表示,另一種是將字符串編碼用Huffman 碼表示。 字符串表示的格式如下:

0 1 2 3 4 5 6 7 +---+---+---+---+---+---+---+---+ | H | String Length (7+) | +---+---------------------------+ | String Data (Length octets) | +-------------------------------+

先是標(biāo)記位 H + 字符串長度,然后是字符串的實際數(shù)據(jù)。各部分說明如下:

  • H: 一位的標(biāo)記,指示字符串的字節(jié)是否為Huffman編碼。
  • 字符串長度: 編碼字符串字面量的字節(jié)數(shù),一個整數(shù),編碼方式可以參考前面 整數(shù)的表示 的部分,一個7位前綴的整數(shù)編碼。
  • 字符串?dāng)?shù)據(jù): 字符串的實際數(shù)據(jù)。如果H是'0',則數(shù)據(jù)是字符串字面量的原始字節(jié)。如果H是'1',則數(shù)據(jù)是字符串字面量的Huffman編碼。

在OkHttp3中,總是會使用直接的字符串編碼,而不是Huffman編碼, okhttp3.internal.http2.Hpack.Writer 中編碼字符串的過程如下:

void writeByteString(ByteString data) throws IOException {writeInt(data.size(), PREFIX_7_BITS, 0);out.write(data);}

OkHttp中,解碼字符串在 okhttp3.internal.http2.Hpack.Reader 中實現(xiàn):

/** Reads a potentially Huffman encoded byte string. */ByteString readByteString() throws IOException {int firstByte = readByte();boolean huffmanDecode = (firstByte & 0x80) == 0x80; // 1NNNNNNNint length = readInt(firstByte, PREFIX_7_BITS);if (huffmanDecode) {return ByteString.of(Huffman.get().decode(source.readByteArray(length)));} else {return source.readByteString(length);}}

字符串編碼沒有使用Huffman編碼時,解碼過程比較簡單,而使用了Huffman編碼時會借助于Huffman類來解碼。

Huffman編碼是一種變長字節(jié)編碼,對于使用頻率高的字節(jié),使用更少的位數(shù),對于使用頻率低的字節(jié)則使用更多的位數(shù)。每個字節(jié)的Huffman碼是根據(jù)統(tǒng)計經(jīng)驗值分配的。為每個字節(jié)分配Huffman碼的方法可以參考 哈夫曼(huffman)樹和哈夫曼編碼 。

哈夫曼樹的構(gòu)造

Huffman 類被設(shè)計為一個單例類。對象在創(chuàng)建時構(gòu)造一個哈夫曼樹以用于編碼和解碼操作。

private static final Huffman INSTANCE = new Huffman();public static Huffman get() {return INSTANCE;}private final Node root = new Node();private Huffman() {buildTree();} ......private void buildTree() {for (int i = 0; i < CODE_LENGTHS.length; i++) {addCode(i, CODES[i], CODE_LENGTHS[i]);}}private void addCode(int sym, int code, byte len) {Node terminal = new Node(sym, len);Node current = root;while (len > 8) {len -= 8;int i = ((code >>> len) & 0xFF);if (current.children == null) {throw new IllegalStateException("invalid dictionary: prefix not unique");}if (current.children[i] == null) {current.children[i] = new Node();}current = current.children[i];}int shift = 8 - len;int start = (code << shift) & 0xFF;int end = 1 << shift;for (int i = start; i < start + end; i++) {current.children[i] = terminal;}} ......private static final class Node {// Null if terminal.private final Node[] children;// Terminal nodes have a symbol.private final int symbol;// Number of bits represented in the terminal node.private final int terminalBits;/** Construct an internal node. */Node() {this.children = new Node[256];this.symbol = 0; // Not read.this.terminalBits = 0; // Not read.}/*** Construct a terminal node.** @param symbol symbol the node represents* @param bits length of Huffman code in bits*/Node(int symbol, int bits) {this.children = null;this.symbol = symbol;int b = bits & 0x07;this.terminalBits = b == 0 ? 8 : b;}}

OkHttp3中的 哈夫曼樹 并不是一個二叉樹,它的每個節(jié)點最多都可以有256個字節(jié)點。OkHttp3用這種方式來優(yōu)化Huffman編碼解碼的效率。用一個圖來表示,將是下面這個樣子的:


Huffman Tree

Huffman 編碼

void encode(byte[] data, OutputStream out) throws IOException {long current = 0;int n = 0;for (int i = 0; i < data.length; i++) {int b = data[i] & 0xFF;int code = CODES[b];int nbits = CODE_LENGTHS[b];current <<= nbits;current |= code;n += nbits;while (n >= 8) {n -= 8;out.write(((int) (current >> n)));}}if (n > 0) {current <<= (8 - n);current |= (0xFF >>> n);out.write((int) current);}}

逐個字節(jié)地編碼數(shù)據(jù)。編碼的最后一個字節(jié)沒有字節(jié)對齊時,會在低位填充1。

Huffman 解碼

byte[] decode(byte[] buf) {ByteArrayOutputStream baos = new ByteArrayOutputStream();Node node = root;int current = 0;int nbits = 0;for (int i = 0; i < buf.length; i++) {int b = buf[i] & 0xFF;current = (current << 8) | b;nbits += 8;while (nbits >= 8) {int c = (current >>> (nbits - 8)) & 0xFF;node = node.children[c];if (node.children == null) {// terminal nodebaos.write(node.symbol);nbits -= node.terminalBits;node = root;} else {// non-terminal nodenbits -= 8;}}}while (nbits > 0) {int c = (current << (8 - nbits)) & 0xFF;node = node.children[c];if (node.children != null || node.terminalBits > nbits) {break;}baos.write(node.symbol);nbits -= node.terminalBits;node = root;}return baos.toByteArray();}

配合Huffman樹的構(gòu)造過程,分幾種情況來看。Huffman碼自己對齊時;Huffman碼沒有字節(jié)對齊,最后一個字節(jié)的最低有效位包含了數(shù)據(jù)流中下一個Huffman碼的最高有效位;Huffman碼沒有字節(jié)對齊,最后一個字節(jié)的最低有效位包含了填充的1。

有興趣的可以參考其它文檔對Huffman編碼算法做更多了解。

首部字段及首部塊的表示

首部字段主要有兩種表示方法,分別是索引表示和字面量表示。字面量表示又分為首部字段的名字用索引表示值用字面量表示和名字及值都用字面量表示等方法。

說到用索引表示首部字段,就不能不提一下HPACK的動態(tài)表和靜態(tài)表。

HPACK使用兩個表將 頭部字段 與 索引 關(guān)聯(lián)起來。 靜態(tài)表 是預(yù)定義的,它包含了常見的頭部字段(其中的大多數(shù)值為空)。 動態(tài)表 是動態(tài)的,它可被編碼器用于編碼重復(fù)的頭部字段。

靜態(tài)表由一個預(yù)定義的頭部字段靜態(tài)列表組成。它的條目在 HPACK規(guī)范的 附錄 A 中定義。

動態(tài)表由以先進(jìn)先出順序維護的 頭部字段列表 組成。動態(tài)表中第一個且最新的條目索引值最低,動態(tài)表最舊的條目索引值最高。

動態(tài)表最初是空的。條目隨著每個頭部塊的解壓而添加。

靜態(tài)表和動態(tài)表被組合為統(tǒng)一的索引地址空間。

在 (1 ~ 靜態(tài)表的長度(包含)) 之間的索引值指向靜態(tài)表中的元素。

大于靜態(tài)表長度的索引值指向動態(tài)表中的元素。通過將頭部字段的索引減去靜態(tài)表的長度來查找指向動態(tài)表的索引。

對于靜態(tài)表大小為 s,動態(tài)表大小為 k 的情況,下圖展示了完整的有效索引地址空間。

<---------- Index Address Space ----------><-- Static Table --> <-- Dynamic Table -->+---+-----------+---+ +---+-----------+---+| 1 | ... | s | |s+1| ... |s+k|+---+-----------+---+ +---+-----------+---+^ || VInsertion Point Dropping Point

用索引表示頭部字段

當(dāng)一個頭部字段的名-值已經(jīng)包含在了靜態(tài)表或動態(tài)表中時,就可以用一個指向靜態(tài)表或動態(tài)表的索引來表示它了。表示方法如下:

0 1 2 3 4 5 6 7 +---+---+---+---+---+---+---+---+ | 1 | Index (7+) | +---+---------------------------+

頭部字段表示的最高有效位置1,然后用前面看到的表示整數(shù)的方法表示索引,即索引是一個7位前綴編碼的整數(shù)。

用字面量表示頭部字段

在這種表示法中,頭部字段的值是用字面量表示的,但頭部字段的名字則不一定。根據(jù)名字的表示方法的差異,以及是否將頭部字段加進(jìn)動態(tài)表等,而分為多種情況。

增量索引的字面量表示

以這種方法表示的頭部字段需要被 加進(jìn)動態(tài)表中。在這種表示方法下,頭部字段的值用索引表示時,頭部字段的表示如下:

0 1 2 3 4 5 6 7 +---+---+---+---+---+---+---+---+ | 0 | 1 | Index (6+) | +---+---+-----------------------+ | H | Value Length (7+) | +---+---------------------------+ | Value String (Length octets) | +-------------------------------+

頭部字段的名字和值都用字面量表示時,表示如下:

0 1 2 3 4 5 6 7 +---+---+---+---+---+---+---+---+ | 0 | 1 | 0 | +---+---+-----------------------+ | H | Name Length (7+) | +---+---------------------------+ | Name String (Length octets) | +---+---------------------------+ | H | Value Length (7+) | +---+---------------------------+ | Value String (Length octets) | +-------------------------------+

增量索引的字面量頭部字段表示以'01' 的2位模式開始。

如果頭部字段名與靜態(tài)表或動態(tài)表中存儲的條目的頭部字段名匹配,則頭部字段名稱可用那個條目的索引表示。在這種情況下,條目的索引以一個具有6位前綴的整數(shù) 表示。這個值總是非0。否則,頭部字段名由一個字符串字面量 表示,使用0值代替6位索引,其后是頭部字段名。

兩種形式的 頭部字段名表示 之后是字符串字面量表示的頭部字段值。

無索引的字面量頭部字段

這種表示方法不改變動態(tài)表。頭部字段名用索引表示時的頭部字段表示如下:

0 1 2 3 4 5 6 7 +---+---+---+---+---+---+---+---+ | 0 | 0 | 0 | 0 | Index (4+) | +---+---+-----------------------+ | H | Value Length (7+) | +---+---------------------------+ | Value String (Length octets) | +-------------------------------+

頭部字段名不用索引表示時的頭部字段表示如下:

0 1 2 3 4 5 6 7 +---+---+---+---+---+---+---+---+ | 0 | 0 | 0 | 0 | 0 | +---+---+-----------------------+ | H | Name Length (7+) | +---+---------------------------+ | Name String (Length octets) | +---+---------------------------+ | H | Value Length (7+) | +---+---------------------------+ | Value String (Length octets) | +-------------------------------+

無索引的字面量頭部字段表示以'0000' 的4位模式開始,其它方面與 增量索引的字面量表示 類似。

從不索引的字面量頭部字段

這種表示方法與 無索引的字面量頭部字段 類似,但它主要影響網(wǎng)絡(luò)中的中間節(jié)點。頭部字段名用索引表示時的頭部字段如:

0 1 2 3 4 5 6 7 +---+---+---+---+---+---+---+---+ | 0 | 0 | 0 | 1 | Index (4+) | +---+---+-----------------------+ | H | Value Length (7+) | +---+---------------------------+ | Value String (Length octets) | +-------------------------------+

頭部字段名不用索引表示時的頭部字段如:

0 1 2 3 4 5 6 7 +---+---+---+---+---+---+---+---+ | 0 | 0 | 0 | 1 | 0 | +---+---+-----------------------+ | H | Name Length (7+) | +---+---------------------------+ | Name String (Length octets) | +---+---------------------------+ | H | Value Length (7+) | +---+---------------------------+ | Value String (Length octets) | +-------------------------------+

首部列表的表示

各個首部字段表示合并起來形成首部列表。在 okhttp3.internal.framed.Hpack.Writer 的writeHeaders() 中完成編碼首部塊的動作:

/** This does not use "never indexed" semantics for sensitive headers. */// http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-12#section-6.2.3void writeHeaders(List<Header> headerBlock) throws IOException {if (emitDynamicTableSizeUpdate) {if (smallestHeaderTableSizeSetting < maxDynamicTableByteCount) {// Multiple dynamic table size updates!writeInt(smallestHeaderTableSizeSetting, PREFIX_5_BITS, 0x20);}emitDynamicTableSizeUpdate = false;smallestHeaderTableSizeSetting = Integer.MAX_VALUE;writeInt(maxDynamicTableByteCount, PREFIX_5_BITS, 0x20);}// TODO: implement index trackingfor (int i = 0, size = headerBlock.size(); i < size; i++) {Header header = headerBlock.get(i);ByteString name = header.name.toAsciiLowercase();ByteString value = header.value;Integer staticIndex = NAME_TO_FIRST_INDEX.get(name);if (staticIndex != null) {// Literal Header Field without Indexing - Indexed Name.writeInt(staticIndex + 1, PREFIX_4_BITS, 0);writeByteString(value);} else {int dynamicIndex = Util.indexOf(dynamicTable, header);if (dynamicIndex != -1) {// Indexed Header.writeInt(dynamicIndex - nextHeaderIndex + STATIC_HEADER_TABLE.length, PREFIX_7_BITS,0x80);} else {// Literal Header Field with Incremental Indexing - New Nameout.writeByte(0x40);writeByteString(name);writeByteString(value);insertIntoDynamicTable(header);}}}}

HPACK的規(guī)范描述了多種頭部字段的表示方法,但并沒有指明各個表示方法的適用場景。

在OkHttp3中,實現(xiàn)了3種表示頭部字段的表示方法:

  • 頭部字段名在靜態(tài)表中,頭部字段名用指向靜態(tài)表的索引表示,值用字面量表示。頭部字段無需加入動態(tài)表。
  • 頭部字段的 名-值 對在動態(tài)表中,用指向動態(tài)表的索引表示頭部字段。
  • 其它情況,用字面量表示頭部字段名和值,頭部字段需要加入動態(tài)表。
  • 如果頭部字段的 名-值 對在靜態(tài)表中,OkHttp3也不會用索引表示。

    請求間首部字段內(nèi)容的復(fù)用

    HPACK中,最重要的優(yōu)化就是消除請求間冗余的首部字段。在實現(xiàn)上,主要有兩個方面,一是前面看到的首部字段的索引表示,另一方面則是動態(tài)表的維護。

    HTTP/2中數(shù)據(jù)發(fā)送方向和數(shù)據(jù)接收方向各有一個動態(tài)表。通信的雙方,一端發(fā)送方向的動態(tài)表需要與另一端接收方向的動態(tài)表保持一致,反之亦然。

    HTTP/2的連接復(fù)用及請求并發(fā)執(zhí)行指的是邏輯上的并發(fā)。由于底層傳輸還是用的TCP協(xié)議,因而,發(fā)送方發(fā)送數(shù)據(jù)的順序,與接收方接收數(shù)據(jù)的順序是一致的。

    數(shù)據(jù)發(fā)送方在發(fā)送一個請求的首部數(shù)據(jù)時會順便維護自己的動態(tài)表,接收方在收到首部數(shù)據(jù)時,也需要立馬維護自己接收方向的動態(tài)表,然后將解碼之后的首部字段列表dispatch出去。

    如果通信雙方同時在進(jìn)行2個HTTP請求,分別稱為Req1和Req2,假設(shè)在發(fā)送方Req1的頭部字段列表先發(fā)送,Req2的頭部字段后發(fā)送。接收方必然先收到Req1的頭部字段列表,然后是Req2的。如果接收方在收到Req1的頭部字段列表后,沒有立即解碼,而是等Req2的首部字段列表接收并處理完成之后,再來處理Req1的,則兩端的動態(tài)表必然是不一致的。

    這里來看一下OkHttp3中的動態(tài)表維護。

    發(fā)送方向的動態(tài)表,在 okhttp3.internal.framed.Hpack.Writer 中維護。在HTTP/2中,動態(tài)表的最大大小在連接建立的初期會進(jìn)行協(xié)商,后面在數(shù)據(jù)收發(fā)過程中也會進(jìn)行更新。

    在編碼頭部字段列表的 writeHeaders(List<Header> headerBlock) 中,會在需要的時候,將頭部字段插入動態(tài)表,具體來說,就是在頭部字段的名字不在靜態(tài)表中,同時 名-值對不在動態(tài)表中的情況。

    將頭部字段插入動態(tài)表的過程如下:

    private void clearDynamicTable() {Arrays.fill(dynamicTable, null);nextHeaderIndex = dynamicTable.length - 1;headerCount = 0;dynamicTableByteCount = 0;}/** Returns the count of entries evicted. */private int evictToRecoverBytes(int bytesToRecover) {int entriesToEvict = 0;if (bytesToRecover > 0) {// determine how many headers need to be evicted.for (int j = dynamicTable.length - 1; j >= nextHeaderIndex && bytesToRecover > 0; j--) {bytesToRecover -= dynamicTable[j].hpackSize;dynamicTableByteCount -= dynamicTable[j].hpackSize;headerCount--;entriesToEvict++;}System.arraycopy(dynamicTable, nextHeaderIndex + 1, dynamicTable,nextHeaderIndex + 1 + entriesToEvict, headerCount);Arrays.fill(dynamicTable, nextHeaderIndex + 1, nextHeaderIndex + 1 + entriesToEvict, null);nextHeaderIndex += entriesToEvict;}return entriesToEvict;}private void insertIntoDynamicTable(Header entry) {int delta = entry.hpackSize;// if the new or replacement header is too big, drop all entries.if (delta > maxDynamicTableByteCount) {clearDynamicTable();return;}// Evict headers to the required length.int bytesToRecover = (dynamicTableByteCount + delta) - maxDynamicTableByteCount;evictToRecoverBytes(bytesToRecover);if (headerCount + 1 > dynamicTable.length) { // Need to grow the dynamic table.Header[] doubled = new Header[dynamicTable.length * 2];System.arraycopy(dynamicTable, 0, doubled, dynamicTable.length, dynamicTable.length);nextHeaderIndex = dynamicTable.length - 1;dynamicTable = doubled;}int index = nextHeaderIndex--;dynamicTable[index] = entry;headerCount++;dynamicTableByteCount += delta;}

    動態(tài)表占用的空間超出限制時,老的頭部字段將被移除。在OkHttp3中,動態(tài)表是一個自后向前生長的表。

    在數(shù)據(jù)的接收防線,okhttp3.internal.http2.Http2Reader 的 nextFrame(Handler handler) 會不停從網(wǎng)絡(luò)讀取一幀幀的數(shù)據(jù):

    public boolean nextFrame(Handler handler) throws IOException {try {source.require(9); // Frame header size} catch (IOException e) {return false; // This might be a normal socket close.}/* 0 1 2 3* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+* | Length (24) |* +---------------+---------------+---------------+* | Type (8) | Flags (8) |* +-+-+-----------+---------------+-------------------------------+* |R| Stream Identifier (31) |* +=+=============================================================+* | Frame Payload (0...) ...* +---------------------------------------------------------------+*/int length = readMedium(source);if (length < 0 || length > INITIAL_MAX_FRAME_SIZE) {throw ioException("FRAME_SIZE_ERROR: %s", length);}byte type = (byte) (source.readByte() & 0xff);byte flags = (byte) (source.readByte() & 0xff);int streamId = (source.readInt() & 0x7fffffff); // Ignore reserved bit.if (logger.isLoggable(FINE)) logger.fine(frameLog(true, streamId, length, type, flags));switch (type) {case TYPE_DATA:readData(handler, length, flags, streamId);break;case TYPE_HEADERS:readHeaders(handler, length, flags, streamId);break;

    讀到頭部塊時,會立即維護本地接收方向的動態(tài)表:

    private void readHeaders(Handler handler, int length, byte flags, int streamId)throws IOException {if (streamId == 0) throw ioException("PROTOCOL_ERROR: TYPE_HEADERS streamId == 0");boolean endStream = (flags & FLAG_END_STREAM) != 0;short padding = (flags & FLAG_PADDED) != 0 ? (short) (source.readByte() & 0xff) : 0;if ((flags & FLAG_PRIORITY) != 0) {readPriority(handler, streamId);length -= 5; // account for above read.}length = lengthWithoutPadding(length, flags, padding);List<Header> headerBlock = readHeaderBlock(length, padding, flags, streamId);handler.headers(endStream, streamId, -1, headerBlock);}private List<Header> readHeaderBlock(int length, short padding, byte flags, int streamId)throws IOException {continuation.length = continuation.left = length;continuation.padding = padding;continuation.flags = flags;continuation.streamId = streamId;// TODO: Concat multi-value headers with 0x0, except COOKIE, which uses 0x3B, 0x20.// http://tools.ietf.org/html/draft-ietf-httpbis-http2-17#section-8.1.2.5hpackReader.readHeaders();return hpackReader.getAndResetHeaderList();}

    okhttp3.internal.http2.Hpack.Reader的readHeaders()如下:

    static final class Reader {private final List<Header> headerList = new ArrayList<>();private final BufferedSource source;private final int headerTableSizeSetting;private int maxDynamicTableByteCount;// Visible for testing.Header[] dynamicTable = new Header[8];// Array is populated back to front, so new entries always have lowest index.int nextHeaderIndex = dynamicTable.length - 1;int headerCount = 0;int dynamicTableByteCount = 0;Reader(int headerTableSizeSetting, Source source) {this(headerTableSizeSetting, headerTableSizeSetting, source);}Reader(int headerTableSizeSetting, int maxDynamicTableByteCount, Source source) {this.headerTableSizeSetting = headerTableSizeSetting;this.maxDynamicTableByteCount = maxDynamicTableByteCount;this.source = Okio.buffer(source);}int maxDynamicTableByteCount() {return maxDynamicTableByteCount;}private void adjustDynamicTableByteCount() {if (maxDynamicTableByteCount < dynamicTableByteCount) {if (maxDynamicTableByteCount == 0) {clearDynamicTable();} else {evictToRecoverBytes(dynamicTableByteCount - maxDynamicTableByteCount);}}}private void clearDynamicTable() {headerList.clear();Arrays.fill(dynamicTable, null);nextHeaderIndex = dynamicTable.length - 1;headerCount = 0;dynamicTableByteCount = 0;}/** Returns the count of entries evicted. */private int evictToRecoverBytes(int bytesToRecover) {int entriesToEvict = 0;if (bytesToRecover > 0) {// determine how many headers need to be evicted.for (int j = dynamicTable.length - 1; j >= nextHeaderIndex && bytesToRecover > 0; j--) {bytesToRecover -= dynamicTable[j].hpackSize;dynamicTableByteCount -= dynamicTable[j].hpackSize;headerCount--;entriesToEvict++;}System.arraycopy(dynamicTable, nextHeaderIndex + 1, dynamicTable,nextHeaderIndex + 1 + entriesToEvict, headerCount);nextHeaderIndex += entriesToEvict;}return entriesToEvict;}/*** Read {@code byteCount} bytes of headers from the source stream. This implementation does not* propagate the never indexed flag of a header.*/void readHeaders() throws IOException {while (!source.exhausted()) {int b = source.readByte() & 0xff;if (b == 0x80) { // 10000000throw new IOException("index == 0");} else if ((b & 0x80) == 0x80) { // 1NNNNNNNint index = readInt(b, PREFIX_7_BITS);readIndexedHeader(index - 1);} else if (b == 0x40) { // 01000000readLiteralHeaderWithIncrementalIndexingNewName();} else if ((b & 0x40) == 0x40) { // 01NNNNNNint index = readInt(b, PREFIX_6_BITS);readLiteralHeaderWithIncrementalIndexingIndexedName(index - 1);} else if ((b & 0x20) == 0x20) { // 001NNNNNmaxDynamicTableByteCount = readInt(b, PREFIX_5_BITS);if (maxDynamicTableByteCount < 0|| maxDynamicTableByteCount > headerTableSizeSetting) {throw new IOException("Invalid dynamic table size update " + maxDynamicTableByteCount);}adjustDynamicTableByteCount();} else if (b == 0x10 || b == 0) { // 000?0000 - Ignore never indexed bit.readLiteralHeaderWithoutIndexingNewName();} else { // 000?NNNN - Ignore never indexed bit.int index = readInt(b, PREFIX_4_BITS);readLiteralHeaderWithoutIndexingIndexedName(index - 1);}}}public List<Header> getAndResetHeaderList() {List<Header> result = new ArrayList<>(headerList);headerList.clear();return result;}private void readIndexedHeader(int index) throws IOException {if (isStaticHeader(index)) {Header staticEntry = STATIC_HEADER_TABLE[index];headerList.add(staticEntry);} else {int dynamicTableIndex = dynamicTableIndex(index - STATIC_HEADER_TABLE.length);if (dynamicTableIndex < 0 || dynamicTableIndex > dynamicTable.length - 1) {throw new IOException("Header index too large " + (index + 1));}headerList.add(dynamicTable[dynamicTableIndex]);}}// referencedHeaders is relative to nextHeaderIndex + 1.private int dynamicTableIndex(int index) {return nextHeaderIndex + 1 + index;}private void readLiteralHeaderWithoutIndexingIndexedName(int index) throws IOException {ByteString name = getName(index);ByteString value = readByteString();headerList.add(new Header(name, value));}private void readLiteralHeaderWithoutIndexingNewName() throws IOException {ByteString name = checkLowercase(readByteString());ByteString value = readByteString();headerList.add(new Header(name, value));}private void readLiteralHeaderWithIncrementalIndexingIndexedName(int nameIndex)throws IOException {ByteString name = getName(nameIndex);ByteString value = readByteString();insertIntoDynamicTable(-1, new Header(name, value));}private void readLiteralHeaderWithIncrementalIndexingNewName() throws IOException {ByteString name = checkLowercase(readByteString());ByteString value = readByteString();insertIntoDynamicTable(-1, new Header(name, value));}private ByteString getName(int index) {if (isStaticHeader(index)) {return STATIC_HEADER_TABLE[index].name;} else {return dynamicTable[dynamicTableIndex(index - STATIC_HEADER_TABLE.length)].name;}}private boolean isStaticHeader(int index) {return index >= 0 && index <= STATIC_HEADER_TABLE.length - 1;}/** index == -1 when new. */private void insertIntoDynamicTable(int index, Header entry) {headerList.add(entry);int delta = entry.hpackSize;if (index != -1) { // Index -1 == new header.delta -= dynamicTable[dynamicTableIndex(index)].hpackSize;}// if the new or replacement header is too big, drop all entries.if (delta > maxDynamicTableByteCount) {clearDynamicTable();return;}// Evict headers to the required length.int bytesToRecover = (dynamicTableByteCount + delta) - maxDynamicTableByteCount;int entriesEvicted = evictToRecoverBytes(bytesToRecover);if (index == -1) { // Adding a value to the dynamic table.if (headerCount + 1 > dynamicTable.length) { // Need to grow the dynamic table.Header[] doubled = new Header[dynamicTable.length * 2];System.arraycopy(dynamicTable, 0, doubled, dynamicTable.length, dynamicTable.length);nextHeaderIndex = dynamicTable.length - 1;dynamicTable = doubled;}index = nextHeaderIndex--;dynamicTable[index] = entry;headerCount++;} else { // Replace value at same position.index += dynamicTableIndex(index) + entriesEvicted;dynamicTable[index] = entry;}dynamicTableByteCount += delta;}

    HTTP/2中數(shù)據(jù)收發(fā)兩端的動態(tài)表一致性主要是依賴TCP來實現(xiàn)的。

    Done。

    總結(jié)

    以上是生活随笔為你收集整理的OkHttp3中的HTTP/2首部压缩的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

    黄色网址在线播放 | 久久久久久亚洲精品 | 97超碰.com | 久久久国产精品网站 | 免费91在线 | 国产精品成人av久久 | 人人狠狠综合久久亚洲 | 在线视频 你懂得 | 亚洲播播| 久久婷婷一区二区三区 | 91九色国产视频 | 欧美性免费 | 国产亚洲精品电影 | 激情久久伊人 | 国产精品1区2区在线观看 | 国产精品久久久久三级 | 五月综合激情婷婷 | 久久999精品 | 国产精品久久久久av免费 | 少妇性aaaaaaaaa视频 | 天天射天天 | 日韩精品一区二区不卡 | 久久深夜福利免费观看 | 天天操天天操天天爽 | 香蕉在线视频观看 | 久久国产一二区 | 亚洲高清av | 国产精品99久久久久久宅男 | 99精品国产福利在线观看免费 | 九九有精品 | 深夜国产福利 | 97香蕉久久超级碰碰高清版 | 美女黄视频免费 | 国产精品久久人 | 在线看污网站 | 亚洲91av| www.五月婷| 亚洲欧美成人 | 成人在线免费av | 91九色视频在线观看 | 久久免费av | 九色精品免费永久在线 | 日本精品久久久久 | 国产精品专区h在线观看 | 国产一级在线免费观看 | 欧美久草在线 | 久久久久久久久久久久亚洲 | 日韩欧美在线视频一区二区 | 久久精品国产一区 | 久久免费黄色大片 | 色国产在线 | 精品一区电影 | 最近免费在线观看 | 蜜臀精品久久久久久蜜臀 | 绯色av一区 | 中文字幕黄色 | 激情网综合 | 韩国在线视频一区 | 亚洲国产最新 | 欧美精品久久久久 | 欧美三级在线播放 | 国产亚洲综合在线 | 国产在线精品播放 | 成年人在线免费看片 | 日韩欧美观看 | 99精品免费在线 | 欧美性春潮 | 超碰在线最新网址 | avav片| 五月激情久久久 | 欧美一级专区免费大片 | 国产综合视频在线观看 | 国产午夜免费视频 | 国产精品一二三 | 美女视频黄,久久 | 亚洲欧美日韩在线看 | 成人精品一区二区三区电影免费 | 久久久久在线观看 | 一区二区三区四区在线 | 狠狠天天| 国产五月婷 | 91桃色在线播放 | 精品综合久久久 | 国产精品一区二区三区在线播放 | 国产精品videossex国产高清 | 色夜影院 | 一级黄色片在线观看 | 很黄很污的视频网站 | 日韩性片 | 国产99re| 国产夫妻av在线 | 亚洲欧美日韩在线一区二区 | 国产精品大片免费观看 | 少妇性aaaaaaaaa视频 | 波多野结衣理论片 | 欧美一区二区三区在线播放 | 日本系列中文字幕 | 88av网站 | 在线av资源| 色在线中文字幕 | 欧洲精品视频一区 | 国产精品久久久久久久久久东京 | 久久久五月天 | 美女免费视频黄 | 玖玖在线资源 | av电影免费在线看 | 九九久久国产 | 日韩精品一区在线播放 | 久久久久www | 在线国产日韩 | 亚洲视频分类 | 综合久久婷婷 | 国产视频日韩视频欧美视频 | 欧美日韩1区2区 | 在线亚洲午夜片av大片 | 国产精品色婷婷视频 | 欧美人牲 | 超碰97久久| 国产xxxx性hd极品 | 2022久久国产露脸精品国产 | 人人艹视频| 黄色网中文字幕 | 在线免费视频你懂的 | 日韩免费播放 | 99热9| 国产亚洲欧洲 | 国产色拍 | 江苏妇搡bbbb搡bbbb | 久久久久亚洲精品成人网小说 | 免费网站在线观看成人 | 国产在线观看污片 | 国产手机在线视频 | 日日日天天天 | 草免费视频| 狠狠88综合久久久久综合网 | 日韩一区二区三区高清在线观看 | 日本精品中文字幕 | 国产精品久久综合 | 亚洲黄色激情小说 | 黄污在线观看 | 在线视频 影院 | 国产精品第2页 | 久久99热精品这里久久精品 | 射射色| 久久色在线播放 | 日韩乱理 | 欧美精品亚州精品 | 精品国产一区二区三区久久久蜜月 | 日韩免费在线观看 | 亚洲欧美日韩一二三区 | 免费av片在线 | 久久高清精品 | 亚洲女欲精品久久久久久久18 | av不卡中文字幕 | 免费看成人av | 一区二区三区久久 | 亚洲一区日韩在线 | 成人午夜电影在线 | 爱爱av网 | 日韩精品视频一二三 | 日韩理论在线观看 | 欧美日韩视频一区二区三区 | 天天天天色射综合 | 麻豆网站免费观看 | 亚洲精品高清一区二区三区四区 | 色鬼综合网 | 免费在线观看av的网站 | 狠狠干夜夜操天天爽 | 成人午夜电影在线 | 在线精品一区二区 | 天天干天天操天天操 | 天天鲁一鲁摸一摸爽一爽 | 国产精品毛片一区二区三区 | 国产中文字幕视频在线观看 | 亚洲精品国产精品久久99 | 337p西西人体大胆瓣开下部 | 精品国偷自产国产一区 | 中文字幕一区二区三区乱码在线 | 黄色aaa级片 | 久久久久女人精品毛片九一 | 97视频在线观看播放 | wwxxxx日本 | 91成品人影院 | 日韩av在线资源 | 天堂久色| 在线视频 区 | 免费看麻豆| 国产一区二区三区视频在线 | 五月婷婷激情六月 | 国产三级久久久 | 国产精品乱码久久 | 91久久一区二区 | 国产日产精品久久久久快鸭 | 在线日韩精品视频 | 成人午夜黄色 | 操久久网| 精品国产日本 | 99精品视频在线观看 | 24小时日本在线www免费的 | 91在线精品视频 | 日日狠狠 | 免费精品国产va自在自线 | 激情开心站 | 成人av视屏| 天天干干| 国产精品99久久久精品 | 福利视频一区二区 | 丁香视频全集免费观看 | 男女激情免费网站 | 天天操天天添天天吹 | 国产精品久久久久影视 | 日韩网站免费观看 | 色瓜 | 中文字幕欧美三区 | 少妇bbb搡bbbb搡bbbb | 免费合欢视频成人app | 毛片网免费 | 夜色资源站wwwcom | 欧美极度另类性三渗透 | 欧美日韩一区久久 | 精品影院一区二区久久久 | 四虎成人在线 | 久久理论影院 | 人人网av | 国产精品一区二区免费视频 | 在线精品视频在线观看高清 | 日韩精品视频在线免费观看 | 五月天丁香视频 | 国产精品久久久久久妇 | 色九九影院 | 日本精品一区二区三区在线播放视频 | 欧美亚洲另类在线视频 | 97超碰人人澡人人爱 | 亚洲免费a | 免费99精品国产自在在线 | 日韩视频一区二区三区 | 人人爱人人添 | 精品 一区 在线 | 99精品视频精品精品视频 | 国产高清中文字幕 | 欧美日韩精品在线观看 | 精品国产成人av | 视频一区在线免费观看 | 超碰99在线 | 久久精品麻豆 | 精品国产一区二区三区久久久 | 色吊丝在线永久观看最新版本 | 日韩欧美黄色网址 | 国产精品久免费的黄网站 | 激情综合色播五月 | 在线国产视频一区 | 亚洲视频在线看 | 精品视频免费 | 最近中文字幕大全中文字幕免费 | 久久男人免费视频 | 国产精品久久久久久一区二区 | 日日摸日日添日日躁av | 国产成人综合在线观看 | 国产视频一区二区三区在线 | 一区二区三区在线免费观看 | 免费视频久久 | 亚洲精品乱码久久久久久9色 | 欧美在线a视频 | 日韩欧美高清免费 | 久久艹精品 | 麻豆 free xxxx movies hd | 国产在线不卡 | 国产精品久久久久9999 | 色综合久久久久久久久五月 | 国产在线精品一区二区不卡了 | 国产精品久久久久久久久毛片 | 久久精选| 久久免费精彩视频 | www.夜夜夜 | 91av在线播放 | 亚洲在线免费视频 | 国产精品一区二区久久 | 中文字幕免费在线看 | 久久精品永久免费 | 91麻豆.com| 日韩一区视频在线 | 国产黄色大片免费看 | 狠狠干狠狠操 | 中文字幕资源在线 | 天天干天天操天天拍 | 久热爱| 久久国产精品视频 | 狠狠色伊人亚洲综合成人 | 欧美做受xxx | 精品 激情 | 色综合久久66 | 91黄站| 国产一级电影 | 四虎在线免费视频 | 夜夜爽88888免费视频4848 | 欧美aa级| 欧美乱熟臀69xxxxxx | www.夜夜爽 | 婷婷色中文 | 久久99精品国产91久久来源 | 久久a国产 | 国产成人精品一区二区三区福利 | avcom在线| 色婷婷综合在线 | 97视频资源 | 亚洲aⅴ在线 | 亚洲激情视频 | 亚洲综合在线视频 | 人人看人人 | 91在线观看视频网站 | 国产精品av在线 | 中文字幕日韩精品有码视频 | 麻豆精品传媒视频 | 免费av大全| 欧美大片aaa | 久久精品国产99国产 | 亚洲va男人天堂 | 日韩精品中文字幕av | 国产91综合一区在线观看 | 天天干人人 | 99久久99久国产黄毛片 | 在线观看视频99 | 中文字幕一区二区三区在线播放 | 久久好看免费视频 | 91av电影在线| av片一区二区 | 色在线国产 | 免费瑟瑟网站 | 久久综合精品一区 | 成人97视频 | 久久久精品影视 | 日韩二区三区 | 日韩a免费| 五月天婷亚洲天综合网精品偷 | 国产一级片免费观看 | 最近更新好看的中文字幕 | 香蕉视频啪啪 | 欧美日韩大片在线观看 | 国产精品毛片一区二区在线看 | 久久精品99精品国产香蕉 | 黄色毛片在线观看 | 国内精品久久久久久久影视麻豆 | 久久亚洲热 | 四虎免费av| 91久久爱热色涩涩 | 97精品国产97久久久久久 | 蜜臀久久99精品久久久久久网站 | 欧美日韩免费一区二区三区 | 国产婷婷精品 | 精品美女在线观看 | 91精品久久香蕉国产线看观看 | 日韩美精品视频 | 国产裸体bbb视频 | 天天操夜夜操夜夜操 | 久久久久伦理电影 | 天天做日日爱夜夜爽 | 久久激情电影 | 韩日电影在线 | 一级黄色a视频 | 日韩网站免费观看 | 日韩精品一二三 | 久久精品美女视频网站 | 在线播放日韩 | 精品毛片一区二区免费看 | 日精品| 日韩91精品 | 日韩资源在线播放 | 国产高清绿奴videos | 亚洲国产精品电影在线观看 | 久久精品三 | 日韩免费看 | av观看免费在线 | 99国产一区二区三精品乱码 | 99精品视频网 | 99视屏 | 久久成人在线视频 | 久久精品国产成人精品 | 中文字幕精品三级久久久 | 亚洲最新在线视频 | 成人免费xxxxxx视频 | 毛片网站在线 | 成人影音在线 | www.97色.com| 成人毛片100免费观看 | 中文字幕免费观看全部电影 | 久久国产成人午夜av影院潦草 | 黄污网| 亚洲欧美怡红院 | 国产一区二区观看 | 国产黄网在线 | 中文字幕在线看视频 | 成人免费观看视频网站 | 少妇av网 | 亚洲精品视频免费观看 | 精品国产电影一区 | 国产免费作爱视频 | 国产成a人亚洲精v品在线观看 | 丁香在线观看完整电影视频 | 特级西西人体444是什么意思 | 999久久久久久久久 69av视频在线观看 | 亚洲午夜在线视频 | 国产中文字幕在线观看 | 91精品网站在线观看 | 成人小视频在线免费观看 | 亚洲视频 视频在线 | 日韩在线观看高清 | 免费视频在线观看网站 | 99热999| 国产精品久久久久免费a∨ 欧美一级性生活片 | 天天爱天天操天天爽 | 欧美成人精品三级在线观看播放 | 色999视频 | 91在线91拍拍在线91 | 91国内产香蕉 | www.日韩免费| 天天操天天操天天爽 | 国产不卡视频在线播放 | 日日草天天干 | 激情五月婷婷激情 | 精品国产久 | 免费观看www视频 | 欧美日本不卡视频 | 久久国产剧场电影 | 日韩在线视频播放 | 天天躁日日躁狠狠躁av麻豆 | 日韩理论电影在线观看 | 婷婷丁香在线视频 | 国产综合久久 | 一二三区av | 黄色亚洲大片免费在线观看 | 亚洲一区二区高潮无套美女 | av黄色成人| 九九日韩| 一区二区三区在线不卡 | 91九色蝌蚪视频 | 人人插人人搞 | 欧美不卡视频在线 | av一级二级 | 国产欧美日韩视频 | 欧美一级淫片videoshd | www.com黄色 | 狠狠操狠狠干天天操 | 最新av观看 | av免费网站观看 | 久久av黄色 | 免费一级特黄毛大片 | 中文字幕在线播放日韩 | 国产精品午夜久久久久久99热 | 日韩二三区| 国产 在线观看 | 国产激情久久久 | 精品亚洲国产视频 | 成人午夜电影在线 | 97av视频| 中文字幕电影一区 | 久久这里有精品 | 久久婷婷亚洲 | 欧美xxxxx在线视频 | 久久精品美女视频网站 | 久久综合免费视频 | 亚洲国产合集 | 免费国产一区二区视频 | 激情五月婷婷网 | 国产精品综合在线观看 | 九九涩涩av台湾日本热热 | 麻豆视传媒官网免费观看 | 亚洲乱码精品久久久久 | 欧美一级片在线 | 日韩中文在线播放 | 国产又粗又猛又黄又爽 | 伊人资源站 | 欧美一级黄色片 | 免费看黄在线网站 | 久久久久久久久久久精 | 亚洲国产日韩一区 | 日韩69视频 | 久久精品国产亚洲精品2020 | 蜜桃视频在线观看一区 | 97成人在线观看视频 | 久9在线 | 精品国自产在线观看 | 一区二区三区免费在线观看视频 | 天天射天天爽 | 1024手机看片国产 | 99精品99| 日韩激情片在线观看 | 久久的色 | 五月婷婷一区二区三区 | 韩日色视频 | 日韩av在线一区二区 | 久久只精品99品免费久23小说 | 一区二区久久久久 | 999久久久免费视频 午夜国产在线观看 | 久久国产精品久久久久 | 又色又爽又激情的59视频 | 三级黄色三级 | 永久免费的av电影 | 在线三级播放 | 中文字幕在线观看第三页 | 婷婷久月 | 国内精品久久久久久久久久清纯 | 久久av免费| 五月激情站 | 一区二区三区国产精品 | 国产精品久久久久一区 | 在线免费观看欧美日韩 | 久久福利综合 | 91污污视频在线观看 | 免费网站在线观看人 | 亚洲日本成人 | 高潮毛片无遮挡高清免费 | 国产不卡片 | 久久永久视频 | 久久综合九色综合欧美就去吻 | 免费看av在线 | 六月天色婷婷 | 久久久影院一区二区三区 | 色综合五月天 | 在线观看亚洲免费视频 | 精品久久久久久国产 | 日韩一区二区三区观看 | 成 人 a v天堂| 美女网站黄在线观看 | 亚洲午夜久久久综合37日本 | 国产亚洲成人网 | 亚洲精品乱码久久久一二三 | 免费在线国产精品 | 国产又粗又硬又爽视频 | 在线免费观看视频一区二区三区 | 婷婷亚洲激情 | 欧美三人交| 国产精品大片免费观看 | 99久久久久久国产精品 | 亚洲精品在线免费播放 | 中文成人字幕 | 亚洲午夜精品福利 | 黄色在线免费观看网址 | 国产在线精品一区二区三区 | 国产一级二级在线播放 | 久热国产视频 | 97成人精品 | 激情五月伊人 | 日韩午夜电影院 | 成年人视频在线观看免费 | 啪啪免费观看网站 | 丰满少妇对白在线偷拍 | 久久久久久久久久影院 | 免费网站在线观看成人 | 日韩在线观看三区 | 超碰最新网址 | 国产剧情一区在线 | 丁香婷婷色综合亚洲电影 | 国产午夜在线观看视频 | 欧美男同视频网站 | 综合久久精品 | 免费三级大片 | 日韩欧美在线视频一区二区三区 | 国产精品高清在线观看 | 精品国产资源 | 成人久久18免费网站麻豆 | 狠狠躁日日躁狂躁夜夜躁 | 97夜夜澡人人爽人人免费 | 一二区精品 | 五月激情电影 | 国产精品视频app | 日本99精品| 日韩精品无码一区二区三区 | 成人在线观看资源 | 亚洲天堂网在线视频 | 国产精品第2页 | 国产最新视频在线 | 久久超级碰 | 久久99精品久久久久久清纯直播 | 黄免费在线观看 | 九九九热精品免费视频观看网站 | 中文字幕免费高清在线 | 免费毛片aaaaaa | 国产专区在线看 | 中文在线a√在线 | 99国产在线视频 | 在线观看视频国产 | 日本久久免费电影 | 欧美一级看片 | 夜夜爽夜夜操 | 色婷婷电影 | 在线观看国产福利片 | 国产理论片在线观看 | 国产精品自产拍在线观看蜜 | 97网| 久草爱| 亚洲高清网站 | 黄色视屏在线免费观看 | 国外成人在线视频网站 | 黄色特一级 | 91大片网站 | 天天射狠狠干 | 成年人在线观看免费视频 | 成年人黄色av | 免费国产一区二区视频 | 欧美日韩国产综合一区二区 | 狠狠色综合欧美激情 | 国产小视频福利在线 | 最近乱久中文字幕 | 色多多视频在线观看 | 在线免费观看一区二区三区 | 91精品天码美女少妇 | 黄色资源在线 | 国产麻豆电影 | 在线91播放 | 中午字幕在线观看 | 一区精品久久 | 在线观看理论 | 中文字幕在线看视频 | 又紧又大又爽精品一区二区 | 国产亚洲情侣一区二区无 | 国产精品99久久久久久小说 | 国产在线精品区 | 久久精品综合一区 | 色妞色视频一区二区三区四区 | 日韩女同一区二区三区在线观看 | 亚洲一二三在线 | 国产成人高清 | avove黑丝| 亚洲精品视频免费在线观看 | 97电影在线观看 | 成人免费色 | 国产精品videoxxxx | 不卡视频在线 | 伊人网av| 天天射射天天 | 日韩电影在线一区 | 高清在线一区二区 | 亚洲一二三区精品 | 成人午夜在线电影 | 国产在线传媒 | 免费看片黄色 | 91在线看黄 | 欧美激情精品一区 | 亚洲精品小视频 | 99在线视频精品 | 久草精品免费 | 中文字幕日韩伦理 | 日日操天天爽 | 欧美一级免费黄色片 | 91视频在线观看免费 | 色在线亚洲 | 综合网伊人 | 国产精品k频道 | 黄色精品一区 | 三级黄色网络 | 成人毛片一区 | 一区二区三区四区在线 | 五月婷婷在线观看视频 | 亚洲免费成人 | 久久国产经典视频 | 成人免费xxx在线观看 | 中文av不卡| 亚洲精品网址在线观看 | 亚洲 中文字幕av | 国产成人亚洲精品自产在线 | 国产精品激情在线观看 | www.xxxx欧美| 日韩在线免费电影 | 国产精品一区二区三区在线免费观看 | 五月色婷 | a在线免费 | 久久成人国产精品一区二区 | 97国产在线视频 | 天天综合网在线观看 | 热精品 | 成人免费看电影 | 最近av在线| 天天干夜夜夜操天 | 亚洲精品高清视频在线观看 | 亚洲精选在线观看 | 久久毛片网站 | 久一在线 | 激情综合婷婷 | 日日夜夜艹 | 久久人视频 | 国产精品久久久久久久久久久免费看 | 亚洲精品视频网址 | 亚洲国产精品99久久久久久久久 | 国产免费专区 | 女人18毛片90分钟 | 欧美日韩免费一区二区 | 在线免费观看黄网站 | www.精选视频.com| 最近中文字幕高清字幕在线视频 | 国产日韩在线看 | 日本黄色免费网站 | av片在线看 | 在线国产视频一区 | 国产精品一区免费观看 | 免费a级大片| 五月天激情婷婷 | 久久综合国产伦精品免费 | 最近中文字幕在线中文高清版 | 亚洲一区av | 三级黄免费看 | 在线国产91| 精品一区二区视频 | 日本久久不卡视频 | 免费在线观看黄 | 国产一卡久久电影永久 | 日本中文字幕在线一区 | 日韩av有码在线 | 精品91在线 | 久久久亚洲精华液 | 日韩网站在线观看 | 亚洲精品ww | 天天做综合网 | 日韩高清无线码2023 | 99精品国产99久久久久久福利 | 黄色影院在线免费观看 | 在线免费观看的av | 亚洲 欧美 综合 在线 精品 | 麻豆视频在线看 | 国产精品美女久久久久久久 | 国产精品免费久久久久久久久久中文 | 免费观看的av | 免费观看午夜视频 | 黄色看片| 九九色网 | 国产一区在线免费观看视频 | 免费下载高清毛片 | 啪啪免费观看网站 | 国语自产偷拍精品视频偷 | 欧美精品v国产精品 | 中文字幕在线观看第一区 | 国产精品第一视频 | 国产大片黄色 | 99精品久久久久久久 | 日韩欧美久久 | 亚洲日本国产精品 | 99久久精品久久亚洲精品 | 在线观看日韩精品视频 | 午夜视频在线观看一区二区三区 | 蜜臀91丨九色丨蝌蚪老版 | 欧美成人高清 | 亚洲高清视频在线观看 | 亚洲精品国产视频 | 欧美久草网 | 国产麻豆视频免费观看 | 黄a网站 | 亚洲三级黄色 | 精品久久久久一区二区国产 | 国产精品精品久久久久久 | 国产99久久九九精品免费 | 高清av免费一区中文字幕 | 天天综合天天做 | 免费看亚洲毛片 | 精品国产欧美一区二区三区不卡 | 黄色网免费 | 一区二区网| 天天天天天天天操 | 日韩在线观看视频网站 | 97免费视频在线播放 | 国产精品 亚洲精品 | 久久精品国产免费看久久精品 | 五月天av在线 | 久久久亚洲影院 | 97在线观看免费高清完整版在线观看 | 亚洲一区精品二人人爽久久 | 国产一区二区三区视频在线 | 成人精品电影 | 在线免费av电影 | h视频日本 | 欧美一级黄色片 | 午夜久久视频 | 国产精品久久久久高潮 | 成年人免费看片 | 中文字幕乱码在线播放 | 免费男女羞羞的视频网站中文字幕 | 久久久亚洲影院 | 国产精品21区 | 国产又粗又长的视频 | 中文字幕av有码 | 天堂av网在线 | 国产成人a亚洲精品 | 婷婷五月色综合 | 天天色天天艹 | 99精品欧美一区二区蜜桃免费 | 天天爽夜夜爽人人爽曰av | 久久人人爽人人片av | 欧美中文字幕久久 | 亚洲精品国产精品乱码在线观看 | 99精品国产在热久久 | 亚洲伊人天堂 | 亚洲精品18日本一区app | 激情伊人五月天 | 国产91成人在在线播放 | 国产精品videoxxxx| 日韩精品中文字幕av | 国产精品久久久久久久久久久久午夜 | 人人插人人舔 | 婷婷色站 | 午夜国产一区二区三区四区 | 亚洲狠狠干 | 91视频 - v11av| 最新色视频 | 97超碰资源| 久久黄网站 | 婷婷精品国产欧美精品亚洲人人爽 | 日韩午夜三级 | 国产成人综合精品 | 久久香蕉一区 | 91亚洲夫妻 | 97精品免费视频 | 亚洲国产小视频在线观看 | 日韩高清二区 | 久久草草影视免费网 | 久久精品视频一 | 亚洲精品乱码久久久久久高潮 | 国产精品自产拍在线观看桃花 | 午夜精品久久久 | 亚洲va在线va天堂 | 久久久国产影视 | 91九色视频在线观看 | 天天色天天色天天色 | 免费成人短视频 | 欧美怡红院 | 少妇高潮流白浆在线观看 | 88av视频| 国产亚洲婷婷 | 久久有精品 | 中文字幕免费高清av | 青春草国产视频 | 亚洲女同videos| 免费网站在线 | 免费激情在线电影 | wwwwww国产 | 97免费在线观看 | 国内少妇自拍视频一区 | 欧美专区日韩专区 | 久在线观看视频 | 中日韩男男gay无套 日韩精品一区二区三区高清免费 | 日韩在线电影 | 国产三级午夜理伦三级 | 国产视频在线观看免费 | 公开超碰在线 | 麻豆传媒在线免费看 | 99re视频在线观看 | 国产精品a久久 | 麻豆视传媒官网免费观看 | 黄色资源在线观看 | 麻豆精品视频在线 | 91精品在线观看入口 | 蜜桃视频在线视频 | 美女黄网站视频免费 | 婷婷国产v亚洲v欧美久久 | 久久综合久久综合久久 | 视频一区二区在线 | 成年人免费在线观看网站 | 91亚色在线观看 | 激情视频在线观看网址 | 国产色视频一区二区三区qq号 | 精品国产aⅴ麻豆 | 毛片.com| 亚洲片在线资源 | 国产精品大片在线观看 | 亚洲午夜av久久乱码 | 日韩电影一区二区三区在线观看 | 在线观看免费av网站 | 5月丁香婷婷综合 | 日韩欧美在线播放 | 久久久网页 | h视频日本 | 久久精品资源 | 在线看国产精品 | 欧美一区二视频在线免费观看 | 亚洲成a人片在线www | 天天干天天草 | www日| 91天天操| 狠狠狠色丁香综合久久天下网 | 日本大片免费观看在线 | 免费欧美高清视频 | 亚洲国产一区二区精品专区 | 久久久国产在线视频 | 免费观看www7722午夜电影 | 欧美成人精品三级在线观看播放 | 欧美日韩亚洲第一页 | 黄色网在线免费观看 | 日韩成片| 国产精久久久久久妇女av | 久久99精品久久久久久久久久久久 | 久久精品欧美一区二区三区麻豆 | 成人av一区二区在线观看 | 91视频-88av| 久久久污 | 一级片免费观看视频 | 久久一二三四 | 日本一区二区三区免费观看 | 成人在线视频网 | 深夜国产福利 | 国产成人综 | 免费看国产视频 | 一区三区在线欧 | 西西444www大胆高清图片 | 91精品专区 | 国产永久网站 | 在线免费视频a | 超碰在线人人艹 | 一区二区三区在线免费观看视频 | 九九激情视频 | 久久99精品视频 | 最近在线中文字幕 | 亚洲成年人在线播放 | 久久精品视频国产 | 国产高清日韩欧美 | 欧美午夜寂寞影院 | 婷五月天激情 | 国产精品久久久久9999 | 日韩字幕| 国产精品青青 | 热久久这里只有精品 | 国产综合91 | 亚洲在线成人精品 | 香蕉视频国产在线 | 日韩区在线观看 | 欧美一级高清片 | 91亚洲精品国偷拍自产在线观看 | 99精品免费久久久久久日本 | 日韩成人免费观看 | 超碰av免费| www.夜色321.com | 久久999精品 | 日韩三级视频在线观看 | 91精品1区| 色夜视频 | 国产精品色婷婷视频 | 日本系列中文字幕 | 国产精品99爱 | 韩国av一区二区三区 | 中文字幕在线观看不卡 | 六月丁香伊人 | 黄色一级影院 | 免费观看一级特黄欧美大片 | 韩国视频一区二区三区 | 免费av在线网站 | 国产一级精品视频 | 在线影视 一区 二区 三区 | 日韩视频在线一区 | 激情综合色图 | 正在播放国产一区 | 亚洲狠狠操 | 天天天天天天操 | 天无日天天操天天干 | 亚州国产精品久久久 | 97av视频在线观看 | 免费av大全 | 一区二区三区 亚洲 | av国产网站 | 亚洲精品国产精品国自产在线 | 天天操天天操天天操天天 | 国产精品自产拍在线观看桃花 | 波多野结衣精品在线 | 免费在线91| 精品国产亚洲日本 | 黄网站app在线观看免费视频 | 日夜夜精品视频 | 日韩视频免费 | 在线视频观看成人 | 久久精品亚洲精品国产欧美 | 欧美黄色成人 | 欧美视频99| 久久中文字幕导航 | 五月天色网站 | 日韩视频专区 | 国产在线高清 | 黄色精品免费 | 男女日麻批 | 午夜精品一区二区三区四区 | 中文字幕观看在线 | 欧洲亚洲精品 | 天天射天天操天天干 | 国产 日韩 欧美 中文 在线播放 | 亚洲综合精品视频 | 伊人成人激情 | japanese黑人亚洲人4k | 国产一在线精品一区在线观看 | 黄网站色 | 福利视频导航网址 | 99久久婷婷国产综合亚洲 | 一区二区av| 午夜av日韩 | 久久久久影视 | 免费亚洲成人 | 亚洲国产日韩在线 | 超碰在线人人草 | 人人澡超碰碰97碰碰碰软件 | 亚洲专区路线二 | 一级片免费在线 | 国产亚洲精品v | 亚洲国产日韩一区 | 精品久久久久久久久久久久久久久久久久 | 亚洲国产午夜视频 |