368 lines
14 KiB
Java
368 lines
14 KiB
Java
package okhttp3.internal.http2;
|
|
|
|
import com.ubt.jimu.base.util.FileUtil;
|
|
import java.io.Closeable;
|
|
import java.io.IOException;
|
|
import java.util.List;
|
|
import java.util.logging.Level;
|
|
import java.util.logging.Logger;
|
|
import okhttp3.internal.Util;
|
|
import okhttp3.internal.http2.Hpack;
|
|
import okio.Buffer;
|
|
import okio.BufferedSource;
|
|
import okio.ByteString;
|
|
import okio.Source;
|
|
import okio.Timeout;
|
|
|
|
/* loaded from: classes2.dex */
|
|
final class Http2Reader implements Closeable {
|
|
static final Logger logger = Logger.getLogger(Http2.class.getName());
|
|
private final boolean client;
|
|
private final ContinuationSource continuation;
|
|
final Hpack.Reader hpackReader;
|
|
private final BufferedSource source;
|
|
|
|
static final class ContinuationSource implements Source {
|
|
byte flags;
|
|
int left;
|
|
int length;
|
|
short padding;
|
|
private final BufferedSource source;
|
|
int streamId;
|
|
|
|
ContinuationSource(BufferedSource bufferedSource) {
|
|
this.source = bufferedSource;
|
|
}
|
|
|
|
private void readContinuationHeader() throws IOException {
|
|
int i = this.streamId;
|
|
int readMedium = Http2Reader.readMedium(this.source);
|
|
this.left = readMedium;
|
|
this.length = readMedium;
|
|
byte readByte = (byte) (this.source.readByte() & 255);
|
|
this.flags = (byte) (this.source.readByte() & 255);
|
|
if (Http2Reader.logger.isLoggable(Level.FINE)) {
|
|
Http2Reader.logger.fine(Http2.frameLog(true, this.streamId, this.length, readByte, this.flags));
|
|
}
|
|
this.streamId = this.source.readInt() & Integer.MAX_VALUE;
|
|
if (readByte != 9) {
|
|
throw Http2.ioException("%s != TYPE_CONTINUATION", Byte.valueOf(readByte));
|
|
}
|
|
if (this.streamId != i) {
|
|
throw Http2.ioException("TYPE_CONTINUATION streamId changed", new Object[0]);
|
|
}
|
|
}
|
|
|
|
@Override // okio.Source, java.io.Closeable, java.lang.AutoCloseable
|
|
public void close() throws IOException {
|
|
}
|
|
|
|
@Override // okio.Source
|
|
public long read(Buffer buffer, long j) throws IOException {
|
|
while (true) {
|
|
int i = this.left;
|
|
if (i != 0) {
|
|
long read = this.source.read(buffer, Math.min(j, i));
|
|
if (read == -1) {
|
|
return -1L;
|
|
}
|
|
this.left = (int) (this.left - read);
|
|
return read;
|
|
}
|
|
this.source.skip(this.padding);
|
|
this.padding = (short) 0;
|
|
if ((this.flags & 4) != 0) {
|
|
return -1L;
|
|
}
|
|
readContinuationHeader();
|
|
}
|
|
}
|
|
|
|
@Override // okio.Source
|
|
public Timeout timeout() {
|
|
return this.source.timeout();
|
|
}
|
|
}
|
|
|
|
interface Handler {
|
|
void ackSettings();
|
|
|
|
void alternateService(int i, String str, ByteString byteString, String str2, int i2, long j);
|
|
|
|
void data(boolean z, int i, BufferedSource bufferedSource, int i2) throws IOException;
|
|
|
|
void goAway(int i, ErrorCode errorCode, ByteString byteString);
|
|
|
|
void headers(boolean z, int i, int i2, List<Header> list);
|
|
|
|
void ping(boolean z, int i, int i2);
|
|
|
|
void priority(int i, int i2, int i3, boolean z);
|
|
|
|
void pushPromise(int i, int i2, List<Header> list) throws IOException;
|
|
|
|
void rstStream(int i, ErrorCode errorCode);
|
|
|
|
void settings(boolean z, Settings settings);
|
|
|
|
void windowUpdate(int i, long j);
|
|
}
|
|
|
|
Http2Reader(BufferedSource bufferedSource, boolean z) {
|
|
this.source = bufferedSource;
|
|
this.client = z;
|
|
this.continuation = new ContinuationSource(this.source);
|
|
this.hpackReader = new Hpack.Reader(FileUtil.ZIP_BUFFER_SIZE, this.continuation);
|
|
}
|
|
|
|
static int lengthWithoutPadding(int i, byte b, short s) throws IOException {
|
|
if ((b & 8) != 0) {
|
|
i--;
|
|
}
|
|
if (s <= i) {
|
|
return (short) (i - s);
|
|
}
|
|
throw Http2.ioException("PROTOCOL_ERROR padding %s > remaining length %s", Short.valueOf(s), Integer.valueOf(i));
|
|
}
|
|
|
|
private void readData(Handler handler, int i, byte b, int i2) throws IOException {
|
|
if (i2 == 0) {
|
|
throw Http2.ioException("PROTOCOL_ERROR: TYPE_DATA streamId == 0", new Object[0]);
|
|
}
|
|
boolean z = (b & 1) != 0;
|
|
if ((b & 32) != 0) {
|
|
throw Http2.ioException("PROTOCOL_ERROR: FLAG_COMPRESSED without SETTINGS_COMPRESS_DATA", new Object[0]);
|
|
}
|
|
short readByte = (b & 8) != 0 ? (short) (this.source.readByte() & 255) : (short) 0;
|
|
handler.data(z, i2, this.source, lengthWithoutPadding(i, b, readByte));
|
|
this.source.skip(readByte);
|
|
}
|
|
|
|
private void readGoAway(Handler handler, int i, byte b, int i2) throws IOException {
|
|
if (i < 8) {
|
|
throw Http2.ioException("TYPE_GOAWAY length < 8: %s", Integer.valueOf(i));
|
|
}
|
|
if (i2 != 0) {
|
|
throw Http2.ioException("TYPE_GOAWAY streamId != 0", new Object[0]);
|
|
}
|
|
int readInt = this.source.readInt();
|
|
int readInt2 = this.source.readInt();
|
|
int i3 = i - 8;
|
|
ErrorCode fromHttp2 = ErrorCode.fromHttp2(readInt2);
|
|
if (fromHttp2 == null) {
|
|
throw Http2.ioException("TYPE_GOAWAY unexpected error code: %d", Integer.valueOf(readInt2));
|
|
}
|
|
ByteString byteString = ByteString.EMPTY;
|
|
if (i3 > 0) {
|
|
byteString = this.source.readByteString(i3);
|
|
}
|
|
handler.goAway(readInt, fromHttp2, byteString);
|
|
}
|
|
|
|
private List<Header> readHeaderBlock(int i, short s, byte b, int i2) throws IOException {
|
|
ContinuationSource continuationSource = this.continuation;
|
|
continuationSource.left = i;
|
|
continuationSource.length = i;
|
|
continuationSource.padding = s;
|
|
continuationSource.flags = b;
|
|
continuationSource.streamId = i2;
|
|
this.hpackReader.readHeaders();
|
|
return this.hpackReader.getAndResetHeaderList();
|
|
}
|
|
|
|
private void readHeaders(Handler handler, int i, byte b, int i2) throws IOException {
|
|
if (i2 == 0) {
|
|
throw Http2.ioException("PROTOCOL_ERROR: TYPE_HEADERS streamId == 0", new Object[0]);
|
|
}
|
|
boolean z = (b & 1) != 0;
|
|
short readByte = (b & 8) != 0 ? (short) (this.source.readByte() & 255) : (short) 0;
|
|
if ((b & 32) != 0) {
|
|
readPriority(handler, i2);
|
|
i -= 5;
|
|
}
|
|
handler.headers(z, i2, -1, readHeaderBlock(lengthWithoutPadding(i, b, readByte), readByte, b, i2));
|
|
}
|
|
|
|
static int readMedium(BufferedSource bufferedSource) throws IOException {
|
|
return (bufferedSource.readByte() & 255) | ((bufferedSource.readByte() & 255) << 16) | ((bufferedSource.readByte() & 255) << 8);
|
|
}
|
|
|
|
private void readPing(Handler handler, int i, byte b, int i2) throws IOException {
|
|
if (i != 8) {
|
|
throw Http2.ioException("TYPE_PING length != 8: %s", Integer.valueOf(i));
|
|
}
|
|
if (i2 != 0) {
|
|
throw Http2.ioException("TYPE_PING streamId != 0", new Object[0]);
|
|
}
|
|
handler.ping((b & 1) != 0, this.source.readInt(), this.source.readInt());
|
|
}
|
|
|
|
private void readPriority(Handler handler, int i, byte b, int i2) throws IOException {
|
|
if (i != 5) {
|
|
throw Http2.ioException("TYPE_PRIORITY length: %d != 5", Integer.valueOf(i));
|
|
}
|
|
if (i2 == 0) {
|
|
throw Http2.ioException("TYPE_PRIORITY streamId == 0", new Object[0]);
|
|
}
|
|
readPriority(handler, i2);
|
|
}
|
|
|
|
private void readPushPromise(Handler handler, int i, byte b, int i2) throws IOException {
|
|
if (i2 == 0) {
|
|
throw Http2.ioException("PROTOCOL_ERROR: TYPE_PUSH_PROMISE streamId == 0", new Object[0]);
|
|
}
|
|
short readByte = (b & 8) != 0 ? (short) (this.source.readByte() & 255) : (short) 0;
|
|
handler.pushPromise(i2, this.source.readInt() & Integer.MAX_VALUE, readHeaderBlock(lengthWithoutPadding(i - 4, b, readByte), readByte, b, i2));
|
|
}
|
|
|
|
private void readRstStream(Handler handler, int i, byte b, int i2) throws IOException {
|
|
if (i != 4) {
|
|
throw Http2.ioException("TYPE_RST_STREAM length: %d != 4", Integer.valueOf(i));
|
|
}
|
|
if (i2 == 0) {
|
|
throw Http2.ioException("TYPE_RST_STREAM streamId == 0", new Object[0]);
|
|
}
|
|
int readInt = this.source.readInt();
|
|
ErrorCode fromHttp2 = ErrorCode.fromHttp2(readInt);
|
|
if (fromHttp2 == null) {
|
|
throw Http2.ioException("TYPE_RST_STREAM unexpected error code: %d", Integer.valueOf(readInt));
|
|
}
|
|
handler.rstStream(i2, fromHttp2);
|
|
}
|
|
|
|
private void readSettings(Handler handler, int i, byte b, int i2) throws IOException {
|
|
if (i2 != 0) {
|
|
throw Http2.ioException("TYPE_SETTINGS streamId != 0", new Object[0]);
|
|
}
|
|
if ((b & 1) != 0) {
|
|
if (i != 0) {
|
|
throw Http2.ioException("FRAME_SIZE_ERROR ack frame should be empty!", new Object[0]);
|
|
}
|
|
handler.ackSettings();
|
|
return;
|
|
}
|
|
if (i % 6 != 0) {
|
|
throw Http2.ioException("TYPE_SETTINGS length %% 6 != 0: %s", Integer.valueOf(i));
|
|
}
|
|
Settings settings = new Settings();
|
|
for (int i3 = 0; i3 < i; i3 += 6) {
|
|
int readShort = this.source.readShort() & 65535;
|
|
int readInt = this.source.readInt();
|
|
switch (readShort) {
|
|
case 2:
|
|
if (readInt != 0 && readInt != 1) {
|
|
throw Http2.ioException("PROTOCOL_ERROR SETTINGS_ENABLE_PUSH != 0 or 1", new Object[0]);
|
|
}
|
|
break;
|
|
case 3:
|
|
readShort = 4;
|
|
break;
|
|
case 4:
|
|
readShort = 7;
|
|
if (readInt < 0) {
|
|
throw Http2.ioException("PROTOCOL_ERROR SETTINGS_INITIAL_WINDOW_SIZE > 2^31 - 1", new Object[0]);
|
|
}
|
|
break;
|
|
case 5:
|
|
if (readInt < 16384 || readInt > 16777215) {
|
|
throw Http2.ioException("PROTOCOL_ERROR SETTINGS_MAX_FRAME_SIZE: %s", Integer.valueOf(readInt));
|
|
}
|
|
break;
|
|
break;
|
|
}
|
|
settings.set(readShort, readInt);
|
|
}
|
|
handler.settings(false, settings);
|
|
}
|
|
|
|
private void readWindowUpdate(Handler handler, int i, byte b, int i2) throws IOException {
|
|
if (i != 4) {
|
|
throw Http2.ioException("TYPE_WINDOW_UPDATE length !=4: %s", Integer.valueOf(i));
|
|
}
|
|
long readInt = this.source.readInt() & 2147483647L;
|
|
if (readInt == 0) {
|
|
throw Http2.ioException("windowSizeIncrement was 0", Long.valueOf(readInt));
|
|
}
|
|
handler.windowUpdate(i2, readInt);
|
|
}
|
|
|
|
@Override // java.io.Closeable, java.lang.AutoCloseable
|
|
public void close() throws IOException {
|
|
this.source.close();
|
|
}
|
|
|
|
public boolean nextFrame(boolean z, Handler handler) throws IOException {
|
|
try {
|
|
this.source.require(9L);
|
|
int readMedium = readMedium(this.source);
|
|
if (readMedium < 0 || readMedium > 16384) {
|
|
throw Http2.ioException("FRAME_SIZE_ERROR: %s", Integer.valueOf(readMedium));
|
|
}
|
|
byte readByte = (byte) (this.source.readByte() & 255);
|
|
if (z && readByte != 4) {
|
|
throw Http2.ioException("Expected a SETTINGS frame but was %s", Byte.valueOf(readByte));
|
|
}
|
|
byte readByte2 = (byte) (this.source.readByte() & 255);
|
|
int readInt = this.source.readInt() & Integer.MAX_VALUE;
|
|
if (logger.isLoggable(Level.FINE)) {
|
|
logger.fine(Http2.frameLog(true, readInt, readMedium, readByte, readByte2));
|
|
}
|
|
switch (readByte) {
|
|
case 0:
|
|
readData(handler, readMedium, readByte2, readInt);
|
|
return true;
|
|
case 1:
|
|
readHeaders(handler, readMedium, readByte2, readInt);
|
|
return true;
|
|
case 2:
|
|
readPriority(handler, readMedium, readByte2, readInt);
|
|
return true;
|
|
case 3:
|
|
readRstStream(handler, readMedium, readByte2, readInt);
|
|
return true;
|
|
case 4:
|
|
readSettings(handler, readMedium, readByte2, readInt);
|
|
return true;
|
|
case 5:
|
|
readPushPromise(handler, readMedium, readByte2, readInt);
|
|
return true;
|
|
case 6:
|
|
readPing(handler, readMedium, readByte2, readInt);
|
|
return true;
|
|
case 7:
|
|
readGoAway(handler, readMedium, readByte2, readInt);
|
|
return true;
|
|
case 8:
|
|
readWindowUpdate(handler, readMedium, readByte2, readInt);
|
|
return true;
|
|
default:
|
|
this.source.skip(readMedium);
|
|
return true;
|
|
}
|
|
} catch (IOException unused) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public void readConnectionPreface(Handler handler) throws IOException {
|
|
if (this.client) {
|
|
if (!nextFrame(true, handler)) {
|
|
throw Http2.ioException("Required SETTINGS preface not received", new Object[0]);
|
|
}
|
|
return;
|
|
}
|
|
ByteString readByteString = this.source.readByteString(Http2.CONNECTION_PREFACE.size());
|
|
if (logger.isLoggable(Level.FINE)) {
|
|
logger.fine(Util.format("<< CONNECTION %s", readByteString.hex()));
|
|
}
|
|
if (!Http2.CONNECTION_PREFACE.equals(readByteString)) {
|
|
throw Http2.ioException("Expected a connection header but was %s", readByteString.utf8());
|
|
}
|
|
}
|
|
|
|
private void readPriority(Handler handler, int i) throws IOException {
|
|
int readInt = this.source.readInt();
|
|
handler.priority(i, readInt & Integer.MAX_VALUE, (this.source.readByte() & 255) + 1, (Integer.MIN_VALUE & readInt) != 0);
|
|
}
|
|
}
|