Initial commit
This commit is contained in:
555
sources/okhttp3/internal/ws/RealWebSocket.java
Normal file
555
sources/okhttp3/internal/ws/RealWebSocket.java
Normal file
@@ -0,0 +1,555 @@
|
||||
package okhttp3.internal.ws;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.net.ProtocolException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import okhttp3.Call;
|
||||
import okhttp3.Callback;
|
||||
import okhttp3.EventListener;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Protocol;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.WebSocket;
|
||||
import okhttp3.WebSocketListener;
|
||||
import okhttp3.internal.Internal;
|
||||
import okhttp3.internal.Util;
|
||||
import okhttp3.internal.connection.StreamAllocation;
|
||||
import okhttp3.internal.ws.WebSocketReader;
|
||||
import okio.BufferedSink;
|
||||
import okio.BufferedSource;
|
||||
import okio.ByteString;
|
||||
|
||||
/* loaded from: classes2.dex */
|
||||
public final class RealWebSocket implements WebSocket, WebSocketReader.FrameCallback {
|
||||
static final /* synthetic */ boolean $assertionsDisabled = false;
|
||||
private static final long CANCEL_AFTER_CLOSE_MILLIS = 60000;
|
||||
private static final long MAX_QUEUE_SIZE = 16777216;
|
||||
private static final List<Protocol> ONLY_HTTP1 = Collections.singletonList(Protocol.HTTP_1_1);
|
||||
private boolean awaitingPong;
|
||||
private Call call;
|
||||
private ScheduledFuture<?> cancelFuture;
|
||||
private boolean enqueuedClose;
|
||||
private ScheduledExecutorService executor;
|
||||
private boolean failed;
|
||||
private final String key;
|
||||
final WebSocketListener listener;
|
||||
private final Request originalRequest;
|
||||
private final long pingIntervalMillis;
|
||||
private long queueSize;
|
||||
private final Random random;
|
||||
private WebSocketReader reader;
|
||||
private String receivedCloseReason;
|
||||
private int receivedPingCount;
|
||||
private int receivedPongCount;
|
||||
private int sentPingCount;
|
||||
private Streams streams;
|
||||
private WebSocketWriter writer;
|
||||
private final Runnable writerRunnable;
|
||||
private final ArrayDeque<ByteString> pongQueue = new ArrayDeque<>();
|
||||
private final ArrayDeque<Object> messageAndCloseQueue = new ArrayDeque<>();
|
||||
private int receivedCloseCode = -1;
|
||||
|
||||
final class CancelRunnable implements Runnable {
|
||||
CancelRunnable() {
|
||||
}
|
||||
|
||||
@Override // java.lang.Runnable
|
||||
public void run() {
|
||||
RealWebSocket.this.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
static final class Close {
|
||||
final long cancelAfterCloseMillis;
|
||||
final int code;
|
||||
final ByteString reason;
|
||||
|
||||
Close(int i, ByteString byteString, long j) {
|
||||
this.code = i;
|
||||
this.reason = byteString;
|
||||
this.cancelAfterCloseMillis = j;
|
||||
}
|
||||
}
|
||||
|
||||
static final class Message {
|
||||
final ByteString data;
|
||||
final int formatOpcode;
|
||||
|
||||
Message(int i, ByteString byteString) {
|
||||
this.formatOpcode = i;
|
||||
this.data = byteString;
|
||||
}
|
||||
}
|
||||
|
||||
private final class PingRunnable implements Runnable {
|
||||
PingRunnable() {
|
||||
}
|
||||
|
||||
@Override // java.lang.Runnable
|
||||
public void run() {
|
||||
RealWebSocket.this.writePingFrame();
|
||||
}
|
||||
}
|
||||
|
||||
public static abstract class Streams implements Closeable {
|
||||
public final boolean client;
|
||||
public final BufferedSink sink;
|
||||
public final BufferedSource source;
|
||||
|
||||
public Streams(boolean z, BufferedSource bufferedSource, BufferedSink bufferedSink) {
|
||||
this.client = z;
|
||||
this.source = bufferedSource;
|
||||
this.sink = bufferedSink;
|
||||
}
|
||||
}
|
||||
|
||||
public RealWebSocket(Request request, WebSocketListener webSocketListener, Random random, long j) {
|
||||
if (!"GET".equals(request.method())) {
|
||||
throw new IllegalArgumentException("Request must be GET: " + request.method());
|
||||
}
|
||||
this.originalRequest = request;
|
||||
this.listener = webSocketListener;
|
||||
this.random = random;
|
||||
this.pingIntervalMillis = j;
|
||||
byte[] bArr = new byte[16];
|
||||
random.nextBytes(bArr);
|
||||
this.key = ByteString.of(bArr).base64();
|
||||
this.writerRunnable = new Runnable() { // from class: okhttp3.internal.ws.RealWebSocket.1
|
||||
@Override // java.lang.Runnable
|
||||
public void run() {
|
||||
do {
|
||||
try {
|
||||
} catch (IOException e) {
|
||||
RealWebSocket.this.failWebSocket(e, null);
|
||||
return;
|
||||
}
|
||||
} while (RealWebSocket.this.writeOneFrame());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void runWriter() {
|
||||
ScheduledExecutorService scheduledExecutorService = this.executor;
|
||||
if (scheduledExecutorService != null) {
|
||||
scheduledExecutorService.execute(this.writerRunnable);
|
||||
}
|
||||
}
|
||||
|
||||
void awaitTermination(int i, TimeUnit timeUnit) throws InterruptedException {
|
||||
this.executor.awaitTermination(i, timeUnit);
|
||||
}
|
||||
|
||||
@Override // okhttp3.WebSocket
|
||||
public void cancel() {
|
||||
this.call.cancel();
|
||||
}
|
||||
|
||||
void checkResponse(Response response) throws ProtocolException {
|
||||
if (response.code() != 101) {
|
||||
throw new ProtocolException("Expected HTTP 101 response but was '" + response.code() + " " + response.message() + "'");
|
||||
}
|
||||
String header = response.header("Connection");
|
||||
if (!"Upgrade".equalsIgnoreCase(header)) {
|
||||
throw new ProtocolException("Expected 'Connection' header value 'Upgrade' but was '" + header + "'");
|
||||
}
|
||||
String header2 = response.header("Upgrade");
|
||||
if (!"websocket".equalsIgnoreCase(header2)) {
|
||||
throw new ProtocolException("Expected 'Upgrade' header value 'websocket' but was '" + header2 + "'");
|
||||
}
|
||||
String header3 = response.header("Sec-WebSocket-Accept");
|
||||
String base64 = ByteString.encodeUtf8(this.key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").sha1().base64();
|
||||
if (base64.equals(header3)) {
|
||||
return;
|
||||
}
|
||||
throw new ProtocolException("Expected 'Sec-WebSocket-Accept' header value '" + base64 + "' but was '" + header3 + "'");
|
||||
}
|
||||
|
||||
@Override // okhttp3.WebSocket
|
||||
public boolean close(int i, String str) {
|
||||
return close(i, str, CANCEL_AFTER_CLOSE_MILLIS);
|
||||
}
|
||||
|
||||
public void connect(OkHttpClient okHttpClient) {
|
||||
OkHttpClient build = okHttpClient.newBuilder().eventListener(EventListener.NONE).protocols(ONLY_HTTP1).build();
|
||||
final Request build2 = this.originalRequest.newBuilder().header("Upgrade", "websocket").header("Connection", "Upgrade").header("Sec-WebSocket-Key", this.key).header("Sec-WebSocket-Version", "13").build();
|
||||
this.call = Internal.instance.newWebSocketCall(build, build2);
|
||||
this.call.enqueue(new Callback() { // from class: okhttp3.internal.ws.RealWebSocket.2
|
||||
@Override // okhttp3.Callback
|
||||
public void onFailure(Call call, IOException iOException) {
|
||||
RealWebSocket.this.failWebSocket(iOException, null);
|
||||
}
|
||||
|
||||
@Override // okhttp3.Callback
|
||||
public void onResponse(Call call, Response response) {
|
||||
try {
|
||||
RealWebSocket.this.checkResponse(response);
|
||||
StreamAllocation streamAllocation = Internal.instance.streamAllocation(call);
|
||||
streamAllocation.noNewStreams();
|
||||
Streams newWebSocketStreams = streamAllocation.connection().newWebSocketStreams(streamAllocation);
|
||||
try {
|
||||
RealWebSocket.this.listener.onOpen(RealWebSocket.this, response);
|
||||
RealWebSocket.this.initReaderAndWriter("OkHttp WebSocket " + build2.url().redact(), newWebSocketStreams);
|
||||
streamAllocation.connection().socket().setSoTimeout(0);
|
||||
RealWebSocket.this.loopReader();
|
||||
} catch (Exception e) {
|
||||
RealWebSocket.this.failWebSocket(e, null);
|
||||
}
|
||||
} catch (ProtocolException e2) {
|
||||
RealWebSocket.this.failWebSocket(e2, response);
|
||||
Util.closeQuietly(response);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void failWebSocket(Exception exc, Response response) {
|
||||
synchronized (this) {
|
||||
if (this.failed) {
|
||||
return;
|
||||
}
|
||||
this.failed = true;
|
||||
Streams streams = this.streams;
|
||||
this.streams = null;
|
||||
if (this.cancelFuture != null) {
|
||||
this.cancelFuture.cancel(false);
|
||||
}
|
||||
if (this.executor != null) {
|
||||
this.executor.shutdown();
|
||||
}
|
||||
try {
|
||||
this.listener.onFailure(this, exc, response);
|
||||
} finally {
|
||||
Util.closeQuietly(streams);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void initReaderAndWriter(String str, Streams streams) throws IOException {
|
||||
synchronized (this) {
|
||||
this.streams = streams;
|
||||
this.writer = new WebSocketWriter(streams.client, streams.sink, this.random);
|
||||
this.executor = new ScheduledThreadPoolExecutor(1, Util.threadFactory(str, false));
|
||||
if (this.pingIntervalMillis != 0) {
|
||||
this.executor.scheduleAtFixedRate(new PingRunnable(), this.pingIntervalMillis, this.pingIntervalMillis, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
if (!this.messageAndCloseQueue.isEmpty()) {
|
||||
runWriter();
|
||||
}
|
||||
}
|
||||
this.reader = new WebSocketReader(streams.client, streams.source, this);
|
||||
}
|
||||
|
||||
public void loopReader() throws IOException {
|
||||
while (this.receivedCloseCode == -1) {
|
||||
this.reader.processNextFrame();
|
||||
}
|
||||
}
|
||||
|
||||
@Override // okhttp3.internal.ws.WebSocketReader.FrameCallback
|
||||
public void onReadClose(int i, String str) {
|
||||
Streams streams;
|
||||
if (i == -1) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
synchronized (this) {
|
||||
if (this.receivedCloseCode != -1) {
|
||||
throw new IllegalStateException("already closed");
|
||||
}
|
||||
this.receivedCloseCode = i;
|
||||
this.receivedCloseReason = str;
|
||||
if (this.enqueuedClose && this.messageAndCloseQueue.isEmpty()) {
|
||||
streams = this.streams;
|
||||
this.streams = null;
|
||||
if (this.cancelFuture != null) {
|
||||
this.cancelFuture.cancel(false);
|
||||
}
|
||||
this.executor.shutdown();
|
||||
} else {
|
||||
streams = null;
|
||||
}
|
||||
}
|
||||
try {
|
||||
this.listener.onClosing(this, i, str);
|
||||
if (streams != null) {
|
||||
this.listener.onClosed(this, i, str);
|
||||
}
|
||||
} finally {
|
||||
Util.closeQuietly(streams);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // okhttp3.internal.ws.WebSocketReader.FrameCallback
|
||||
public void onReadMessage(String str) throws IOException {
|
||||
this.listener.onMessage(this, str);
|
||||
}
|
||||
|
||||
@Override // okhttp3.internal.ws.WebSocketReader.FrameCallback
|
||||
public synchronized void onReadPing(ByteString byteString) {
|
||||
if (!this.failed && (!this.enqueuedClose || !this.messageAndCloseQueue.isEmpty())) {
|
||||
this.pongQueue.add(byteString);
|
||||
runWriter();
|
||||
this.receivedPingCount++;
|
||||
}
|
||||
}
|
||||
|
||||
@Override // okhttp3.internal.ws.WebSocketReader.FrameCallback
|
||||
public synchronized void onReadPong(ByteString byteString) {
|
||||
this.receivedPongCount++;
|
||||
this.awaitingPong = false;
|
||||
}
|
||||
|
||||
synchronized boolean pong(ByteString byteString) {
|
||||
if (!this.failed && (!this.enqueuedClose || !this.messageAndCloseQueue.isEmpty())) {
|
||||
this.pongQueue.add(byteString);
|
||||
runWriter();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean processNextFrame() throws IOException {
|
||||
try {
|
||||
this.reader.processNextFrame();
|
||||
return this.receivedCloseCode == -1;
|
||||
} catch (Exception e) {
|
||||
failWebSocket(e, null);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override // okhttp3.WebSocket
|
||||
public synchronized long queueSize() {
|
||||
return this.queueSize;
|
||||
}
|
||||
|
||||
synchronized int receivedPingCount() {
|
||||
return this.receivedPingCount;
|
||||
}
|
||||
|
||||
synchronized int receivedPongCount() {
|
||||
return this.receivedPongCount;
|
||||
}
|
||||
|
||||
@Override // okhttp3.WebSocket
|
||||
public Request request() {
|
||||
return this.originalRequest;
|
||||
}
|
||||
|
||||
@Override // okhttp3.WebSocket
|
||||
public boolean send(String str) {
|
||||
if (str != null) {
|
||||
return send(ByteString.encodeUtf8(str), 1);
|
||||
}
|
||||
throw new NullPointerException("text == null");
|
||||
}
|
||||
|
||||
synchronized int sentPingCount() {
|
||||
return this.sentPingCount;
|
||||
}
|
||||
|
||||
void tearDown() throws InterruptedException {
|
||||
ScheduledFuture<?> scheduledFuture = this.cancelFuture;
|
||||
if (scheduledFuture != null) {
|
||||
scheduledFuture.cancel(false);
|
||||
}
|
||||
this.executor.shutdown();
|
||||
this.executor.awaitTermination(10L, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/* JADX WARN: Removed duplicated region for block: B:18:0x0052 A[Catch: all -> 0x00a5, TRY_ENTER, TryCatch #1 {all -> 0x00a5, blocks: (B:18:0x0052, B:21:0x0056, B:23:0x005a, B:24:0x0076, B:32:0x0085, B:33:0x0086, B:35:0x008a, B:37:0x0095, B:38:0x009f, B:39:0x00a4, B:26:0x0077, B:27:0x0081), top: B:16:0x0050 }] */
|
||||
/* JADX WARN: Removed duplicated region for block: B:21:0x0056 A[Catch: all -> 0x00a5, TryCatch #1 {all -> 0x00a5, blocks: (B:18:0x0052, B:21:0x0056, B:23:0x005a, B:24:0x0076, B:32:0x0085, B:33:0x0086, B:35:0x008a, B:37:0x0095, B:38:0x009f, B:39:0x00a4, B:26:0x0077, B:27:0x0081), top: B:16:0x0050 }] */
|
||||
/*
|
||||
Code decompiled incorrectly, please refer to instructions dump.
|
||||
To view partially-correct code enable 'Show inconsistent code' option in preferences
|
||||
*/
|
||||
boolean writeOneFrame() throws java.io.IOException {
|
||||
/*
|
||||
r11 = this;
|
||||
monitor-enter(r11)
|
||||
boolean r0 = r11.failed // Catch: java.lang.Throwable -> Laa
|
||||
r1 = 0
|
||||
if (r0 == 0) goto L8
|
||||
monitor-exit(r11) // Catch: java.lang.Throwable -> Laa
|
||||
return r1
|
||||
L8:
|
||||
okhttp3.internal.ws.WebSocketWriter r0 = r11.writer // Catch: java.lang.Throwable -> Laa
|
||||
java.util.ArrayDeque<okio.ByteString> r2 = r11.pongQueue // Catch: java.lang.Throwable -> Laa
|
||||
java.lang.Object r2 = r2.poll() // Catch: java.lang.Throwable -> Laa
|
||||
okio.ByteString r2 = (okio.ByteString) r2 // Catch: java.lang.Throwable -> Laa
|
||||
r3 = -1
|
||||
r4 = 0
|
||||
if (r2 != 0) goto L4c
|
||||
java.util.ArrayDeque<java.lang.Object> r5 = r11.messageAndCloseQueue // Catch: java.lang.Throwable -> Laa
|
||||
java.lang.Object r5 = r5.poll() // Catch: java.lang.Throwable -> Laa
|
||||
boolean r6 = r5 instanceof okhttp3.internal.ws.RealWebSocket.Close // Catch: java.lang.Throwable -> Laa
|
||||
if (r6 == 0) goto L46
|
||||
int r1 = r11.receivedCloseCode // Catch: java.lang.Throwable -> Laa
|
||||
java.lang.String r6 = r11.receivedCloseReason // Catch: java.lang.Throwable -> Laa
|
||||
if (r1 == r3) goto L31
|
||||
okhttp3.internal.ws.RealWebSocket$Streams r3 = r11.streams // Catch: java.lang.Throwable -> Laa
|
||||
r11.streams = r4 // Catch: java.lang.Throwable -> Laa
|
||||
java.util.concurrent.ScheduledExecutorService r4 = r11.executor // Catch: java.lang.Throwable -> Laa
|
||||
r4.shutdown() // Catch: java.lang.Throwable -> Laa
|
||||
r4 = r3
|
||||
goto L4f
|
||||
L31:
|
||||
java.util.concurrent.ScheduledExecutorService r3 = r11.executor // Catch: java.lang.Throwable -> Laa
|
||||
okhttp3.internal.ws.RealWebSocket$CancelRunnable r7 = new okhttp3.internal.ws.RealWebSocket$CancelRunnable // Catch: java.lang.Throwable -> Laa
|
||||
r7.<init>() // Catch: java.lang.Throwable -> Laa
|
||||
r8 = r5
|
||||
okhttp3.internal.ws.RealWebSocket$Close r8 = (okhttp3.internal.ws.RealWebSocket.Close) r8 // Catch: java.lang.Throwable -> Laa
|
||||
long r8 = r8.cancelAfterCloseMillis // Catch: java.lang.Throwable -> Laa
|
||||
java.util.concurrent.TimeUnit r10 = java.util.concurrent.TimeUnit.MILLISECONDS // Catch: java.lang.Throwable -> Laa
|
||||
java.util.concurrent.ScheduledFuture r3 = r3.schedule(r7, r8, r10) // Catch: java.lang.Throwable -> Laa
|
||||
r11.cancelFuture = r3 // Catch: java.lang.Throwable -> Laa
|
||||
goto L4f
|
||||
L46:
|
||||
if (r5 != 0) goto L4a
|
||||
monitor-exit(r11) // Catch: java.lang.Throwable -> Laa
|
||||
return r1
|
||||
L4a:
|
||||
r6 = r4
|
||||
goto L4e
|
||||
L4c:
|
||||
r5 = r4
|
||||
r6 = r5
|
||||
L4e:
|
||||
r1 = -1
|
||||
L4f:
|
||||
monitor-exit(r11) // Catch: java.lang.Throwable -> Laa
|
||||
if (r2 == 0) goto L56
|
||||
r0.writePong(r2) // Catch: java.lang.Throwable -> La5
|
||||
goto L9a
|
||||
L56:
|
||||
boolean r2 = r5 instanceof okhttp3.internal.ws.RealWebSocket.Message // Catch: java.lang.Throwable -> La5
|
||||
if (r2 == 0) goto L86
|
||||
r1 = r5
|
||||
okhttp3.internal.ws.RealWebSocket$Message r1 = (okhttp3.internal.ws.RealWebSocket.Message) r1 // Catch: java.lang.Throwable -> La5
|
||||
okio.ByteString r1 = r1.data // Catch: java.lang.Throwable -> La5
|
||||
okhttp3.internal.ws.RealWebSocket$Message r5 = (okhttp3.internal.ws.RealWebSocket.Message) r5 // Catch: java.lang.Throwable -> La5
|
||||
int r2 = r5.formatOpcode // Catch: java.lang.Throwable -> La5
|
||||
int r3 = r1.size() // Catch: java.lang.Throwable -> La5
|
||||
long r5 = (long) r3 // Catch: java.lang.Throwable -> La5
|
||||
okio.Sink r0 = r0.newMessageSink(r2, r5) // Catch: java.lang.Throwable -> La5
|
||||
okio.BufferedSink r0 = okio.Okio.buffer(r0) // Catch: java.lang.Throwable -> La5
|
||||
r0.write(r1) // Catch: java.lang.Throwable -> La5
|
||||
r0.close() // Catch: java.lang.Throwable -> La5
|
||||
monitor-enter(r11) // Catch: java.lang.Throwable -> La5
|
||||
long r2 = r11.queueSize // Catch: java.lang.Throwable -> L83
|
||||
int r0 = r1.size() // Catch: java.lang.Throwable -> L83
|
||||
long r0 = (long) r0 // Catch: java.lang.Throwable -> L83
|
||||
long r2 = r2 - r0
|
||||
r11.queueSize = r2 // Catch: java.lang.Throwable -> L83
|
||||
monitor-exit(r11) // Catch: java.lang.Throwable -> L83
|
||||
goto L9a
|
||||
L83:
|
||||
r0 = move-exception
|
||||
monitor-exit(r11) // Catch: java.lang.Throwable -> L83
|
||||
throw r0 // Catch: java.lang.Throwable -> La5
|
||||
L86:
|
||||
boolean r2 = r5 instanceof okhttp3.internal.ws.RealWebSocket.Close // Catch: java.lang.Throwable -> La5
|
||||
if (r2 == 0) goto L9f
|
||||
okhttp3.internal.ws.RealWebSocket$Close r5 = (okhttp3.internal.ws.RealWebSocket.Close) r5 // Catch: java.lang.Throwable -> La5
|
||||
int r2 = r5.code // Catch: java.lang.Throwable -> La5
|
||||
okio.ByteString r3 = r5.reason // Catch: java.lang.Throwable -> La5
|
||||
r0.writeClose(r2, r3) // Catch: java.lang.Throwable -> La5
|
||||
if (r4 == 0) goto L9a
|
||||
okhttp3.WebSocketListener r0 = r11.listener // Catch: java.lang.Throwable -> La5
|
||||
r0.onClosed(r11, r1, r6) // Catch: java.lang.Throwable -> La5
|
||||
L9a:
|
||||
r0 = 1
|
||||
okhttp3.internal.Util.closeQuietly(r4)
|
||||
return r0
|
||||
L9f:
|
||||
java.lang.AssertionError r0 = new java.lang.AssertionError // Catch: java.lang.Throwable -> La5
|
||||
r0.<init>() // Catch: java.lang.Throwable -> La5
|
||||
throw r0 // Catch: java.lang.Throwable -> La5
|
||||
La5:
|
||||
r0 = move-exception
|
||||
okhttp3.internal.Util.closeQuietly(r4)
|
||||
throw r0
|
||||
Laa:
|
||||
r0 = move-exception
|
||||
monitor-exit(r11) // Catch: java.lang.Throwable -> Laa
|
||||
throw r0
|
||||
*/
|
||||
throw new UnsupportedOperationException("Method not decompiled: okhttp3.internal.ws.RealWebSocket.writeOneFrame():boolean");
|
||||
}
|
||||
|
||||
void writePingFrame() {
|
||||
synchronized (this) {
|
||||
if (this.failed) {
|
||||
return;
|
||||
}
|
||||
WebSocketWriter webSocketWriter = this.writer;
|
||||
int i = this.awaitingPong ? this.sentPingCount : -1;
|
||||
this.sentPingCount++;
|
||||
this.awaitingPong = true;
|
||||
if (i == -1) {
|
||||
try {
|
||||
webSocketWriter.writePing(ByteString.EMPTY);
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
failWebSocket(e, null);
|
||||
return;
|
||||
}
|
||||
}
|
||||
failWebSocket(new SocketTimeoutException("sent ping but didn't receive pong within " + this.pingIntervalMillis + "ms (after " + (i - 1) + " successful ping/pongs)"), null);
|
||||
}
|
||||
}
|
||||
|
||||
synchronized boolean close(int i, String str, long j) {
|
||||
WebSocketProtocol.validateCloseCode(i);
|
||||
ByteString byteString = null;
|
||||
if (str != null) {
|
||||
byteString = ByteString.encodeUtf8(str);
|
||||
if (byteString.size() > 123) {
|
||||
throw new IllegalArgumentException("reason.size() > 123: " + str);
|
||||
}
|
||||
}
|
||||
if (!this.failed && !this.enqueuedClose) {
|
||||
this.enqueuedClose = true;
|
||||
this.messageAndCloseQueue.add(new Close(i, byteString, j));
|
||||
runWriter();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override // okhttp3.internal.ws.WebSocketReader.FrameCallback
|
||||
public void onReadMessage(ByteString byteString) throws IOException {
|
||||
this.listener.onMessage(this, byteString);
|
||||
}
|
||||
|
||||
@Override // okhttp3.WebSocket
|
||||
public boolean send(ByteString byteString) {
|
||||
if (byteString != null) {
|
||||
return send(byteString, 2);
|
||||
}
|
||||
throw new NullPointerException("bytes == null");
|
||||
}
|
||||
|
||||
private synchronized boolean send(ByteString byteString, int i) {
|
||||
if (!this.failed && !this.enqueuedClose) {
|
||||
if (this.queueSize + byteString.size() > MAX_QUEUE_SIZE) {
|
||||
close(1001, null);
|
||||
return false;
|
||||
}
|
||||
this.queueSize += byteString.size();
|
||||
this.messageAndCloseQueue.add(new Message(i, byteString));
|
||||
runWriter();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
71
sources/okhttp3/internal/ws/WebSocketProtocol.java
Normal file
71
sources/okhttp3/internal/ws/WebSocketProtocol.java
Normal file
@@ -0,0 +1,71 @@
|
||||
package okhttp3.internal.ws;
|
||||
|
||||
import okio.Buffer;
|
||||
import okio.ByteString;
|
||||
|
||||
/* loaded from: classes2.dex */
|
||||
public final class WebSocketProtocol {
|
||||
static final String ACCEPT_MAGIC = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||
static final int B0_FLAG_FIN = 128;
|
||||
static final int B0_FLAG_RSV1 = 64;
|
||||
static final int B0_FLAG_RSV2 = 32;
|
||||
static final int B0_FLAG_RSV3 = 16;
|
||||
static final int B0_MASK_OPCODE = 15;
|
||||
static final int B1_FLAG_MASK = 128;
|
||||
static final int B1_MASK_LENGTH = 127;
|
||||
static final int CLOSE_CLIENT_GOING_AWAY = 1001;
|
||||
static final long CLOSE_MESSAGE_MAX = 123;
|
||||
static final int CLOSE_NO_STATUS_CODE = 1005;
|
||||
static final int OPCODE_BINARY = 2;
|
||||
static final int OPCODE_CONTINUATION = 0;
|
||||
static final int OPCODE_CONTROL_CLOSE = 8;
|
||||
static final int OPCODE_CONTROL_PING = 9;
|
||||
static final int OPCODE_CONTROL_PONG = 10;
|
||||
static final int OPCODE_FLAG_CONTROL = 8;
|
||||
static final int OPCODE_TEXT = 1;
|
||||
static final long PAYLOAD_BYTE_MAX = 125;
|
||||
static final int PAYLOAD_LONG = 127;
|
||||
static final int PAYLOAD_SHORT = 126;
|
||||
static final long PAYLOAD_SHORT_MAX = 65535;
|
||||
|
||||
private WebSocketProtocol() {
|
||||
throw new AssertionError("No instances.");
|
||||
}
|
||||
|
||||
public static String acceptHeader(String str) {
|
||||
return ByteString.encodeUtf8(str + ACCEPT_MAGIC).sha1().base64();
|
||||
}
|
||||
|
||||
static String closeCodeExceptionMessage(int i) {
|
||||
if (i < 1000 || i >= 5000) {
|
||||
return "Code must be in range [1000,5000): " + i;
|
||||
}
|
||||
if ((i < 1004 || i > 1006) && (i < 1012 || i > 2999)) {
|
||||
return null;
|
||||
}
|
||||
return "Code " + i + " is reserved and may not be used.";
|
||||
}
|
||||
|
||||
static void toggleMask(Buffer.UnsafeCursor unsafeCursor, byte[] bArr) {
|
||||
int length = bArr.length;
|
||||
int i = 0;
|
||||
do {
|
||||
byte[] bArr2 = unsafeCursor.data;
|
||||
int i2 = unsafeCursor.start;
|
||||
int i3 = unsafeCursor.end;
|
||||
while (i2 < i3) {
|
||||
int i4 = i % length;
|
||||
bArr2[i2] = (byte) (bArr2[i2] ^ bArr[i4]);
|
||||
i2++;
|
||||
i = i4 + 1;
|
||||
}
|
||||
} while (unsafeCursor.next() != -1);
|
||||
}
|
||||
|
||||
static void validateCloseCode(int i) {
|
||||
String closeCodeExceptionMessage = closeCodeExceptionMessage(i);
|
||||
if (closeCodeExceptionMessage != null) {
|
||||
throw new IllegalArgumentException(closeCodeExceptionMessage);
|
||||
}
|
||||
}
|
||||
}
|
199
sources/okhttp3/internal/ws/WebSocketReader.java
Normal file
199
sources/okhttp3/internal/ws/WebSocketReader.java
Normal file
@@ -0,0 +1,199 @@
|
||||
package okhttp3.internal.ws;
|
||||
|
||||
import com.ubtrobot.jimu.robotapi.PeripheralType;
|
||||
import java.io.IOException;
|
||||
import java.net.ProtocolException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import okio.Buffer;
|
||||
import okio.BufferedSource;
|
||||
import okio.ByteString;
|
||||
|
||||
/* loaded from: classes2.dex */
|
||||
final class WebSocketReader {
|
||||
boolean closed;
|
||||
final FrameCallback frameCallback;
|
||||
long frameLength;
|
||||
final boolean isClient;
|
||||
boolean isControlFrame;
|
||||
boolean isFinalFrame;
|
||||
private final Buffer.UnsafeCursor maskCursor;
|
||||
private final byte[] maskKey;
|
||||
int opcode;
|
||||
final BufferedSource source;
|
||||
private final Buffer controlFrameBuffer = new Buffer();
|
||||
private final Buffer messageFrameBuffer = new Buffer();
|
||||
|
||||
public interface FrameCallback {
|
||||
void onReadClose(int i, String str);
|
||||
|
||||
void onReadMessage(String str) throws IOException;
|
||||
|
||||
void onReadMessage(ByteString byteString) throws IOException;
|
||||
|
||||
void onReadPing(ByteString byteString);
|
||||
|
||||
void onReadPong(ByteString byteString);
|
||||
}
|
||||
|
||||
WebSocketReader(boolean z, BufferedSource bufferedSource, FrameCallback frameCallback) {
|
||||
if (bufferedSource == null) {
|
||||
throw new NullPointerException("source == null");
|
||||
}
|
||||
if (frameCallback == null) {
|
||||
throw new NullPointerException("frameCallback == null");
|
||||
}
|
||||
this.isClient = z;
|
||||
this.source = bufferedSource;
|
||||
this.frameCallback = frameCallback;
|
||||
this.maskKey = z ? null : new byte[4];
|
||||
this.maskCursor = z ? null : new Buffer.UnsafeCursor();
|
||||
}
|
||||
|
||||
private void readControlFrame() throws IOException {
|
||||
String str;
|
||||
long j = this.frameLength;
|
||||
if (j > 0) {
|
||||
this.source.readFully(this.controlFrameBuffer, j);
|
||||
if (!this.isClient) {
|
||||
this.controlFrameBuffer.readAndWriteUnsafe(this.maskCursor);
|
||||
this.maskCursor.seek(0L);
|
||||
WebSocketProtocol.toggleMask(this.maskCursor, this.maskKey);
|
||||
this.maskCursor.close();
|
||||
}
|
||||
}
|
||||
switch (this.opcode) {
|
||||
case 8:
|
||||
short s = 1005;
|
||||
long size = this.controlFrameBuffer.size();
|
||||
if (size == 1) {
|
||||
throw new ProtocolException("Malformed close payload length of 1.");
|
||||
}
|
||||
if (size != 0) {
|
||||
s = this.controlFrameBuffer.readShort();
|
||||
str = this.controlFrameBuffer.readUtf8();
|
||||
String closeCodeExceptionMessage = WebSocketProtocol.closeCodeExceptionMessage(s);
|
||||
if (closeCodeExceptionMessage != null) {
|
||||
throw new ProtocolException(closeCodeExceptionMessage);
|
||||
}
|
||||
} else {
|
||||
str = "";
|
||||
}
|
||||
this.frameCallback.onReadClose(s, str);
|
||||
this.closed = true;
|
||||
return;
|
||||
case 9:
|
||||
this.frameCallback.onReadPing(this.controlFrameBuffer.readByteString());
|
||||
return;
|
||||
case 10:
|
||||
this.frameCallback.onReadPong(this.controlFrameBuffer.readByteString());
|
||||
return;
|
||||
default:
|
||||
throw new ProtocolException("Unknown control opcode: " + Integer.toHexString(this.opcode));
|
||||
}
|
||||
}
|
||||
|
||||
/* JADX WARN: Finally extract failed */
|
||||
private void readHeader() throws IOException {
|
||||
if (this.closed) {
|
||||
throw new IOException("closed");
|
||||
}
|
||||
long timeoutNanos = this.source.timeout().timeoutNanos();
|
||||
this.source.timeout().clearTimeout();
|
||||
try {
|
||||
int readByte = this.source.readByte() & 255;
|
||||
this.source.timeout().timeout(timeoutNanos, TimeUnit.NANOSECONDS);
|
||||
this.opcode = readByte & 15;
|
||||
this.isFinalFrame = (readByte & PeripheralType.SERVO) != 0;
|
||||
this.isControlFrame = (readByte & 8) != 0;
|
||||
if (this.isControlFrame && !this.isFinalFrame) {
|
||||
throw new ProtocolException("Control frames must be final.");
|
||||
}
|
||||
boolean z = (readByte & 64) != 0;
|
||||
boolean z2 = (readByte & 32) != 0;
|
||||
boolean z3 = (readByte & 16) != 0;
|
||||
if (z || z2 || z3) {
|
||||
throw new ProtocolException("Reserved flags are unsupported.");
|
||||
}
|
||||
boolean z4 = ((this.source.readByte() & 255) & PeripheralType.SERVO) != 0;
|
||||
boolean z5 = this.isClient;
|
||||
if (z4 == z5) {
|
||||
throw new ProtocolException(z5 ? "Server-sent frames must not be masked." : "Client-sent frames must be masked.");
|
||||
}
|
||||
this.frameLength = r0 & 127;
|
||||
long j = this.frameLength;
|
||||
if (j == 126) {
|
||||
this.frameLength = this.source.readShort() & 65535;
|
||||
} else if (j == 127) {
|
||||
this.frameLength = this.source.readLong();
|
||||
if (this.frameLength < 0) {
|
||||
throw new ProtocolException("Frame length 0x" + Long.toHexString(this.frameLength) + " > 0x7FFFFFFFFFFFFFFF");
|
||||
}
|
||||
}
|
||||
if (this.isControlFrame && this.frameLength > 125) {
|
||||
throw new ProtocolException("Control frame must be less than 125B.");
|
||||
}
|
||||
if (z4) {
|
||||
this.source.readFully(this.maskKey);
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
this.source.timeout().timeout(timeoutNanos, TimeUnit.NANOSECONDS);
|
||||
throw th;
|
||||
}
|
||||
}
|
||||
|
||||
private void readMessage() throws IOException {
|
||||
while (!this.closed) {
|
||||
long j = this.frameLength;
|
||||
if (j > 0) {
|
||||
this.source.readFully(this.messageFrameBuffer, j);
|
||||
if (!this.isClient) {
|
||||
this.messageFrameBuffer.readAndWriteUnsafe(this.maskCursor);
|
||||
this.maskCursor.seek(this.messageFrameBuffer.size() - this.frameLength);
|
||||
WebSocketProtocol.toggleMask(this.maskCursor, this.maskKey);
|
||||
this.maskCursor.close();
|
||||
}
|
||||
}
|
||||
if (this.isFinalFrame) {
|
||||
return;
|
||||
}
|
||||
readUntilNonControlFrame();
|
||||
if (this.opcode != 0) {
|
||||
throw new ProtocolException("Expected continuation opcode. Got: " + Integer.toHexString(this.opcode));
|
||||
}
|
||||
}
|
||||
throw new IOException("closed");
|
||||
}
|
||||
|
||||
private void readMessageFrame() throws IOException {
|
||||
int i = this.opcode;
|
||||
if (i != 1 && i != 2) {
|
||||
throw new ProtocolException("Unknown opcode: " + Integer.toHexString(i));
|
||||
}
|
||||
readMessage();
|
||||
if (i == 1) {
|
||||
this.frameCallback.onReadMessage(this.messageFrameBuffer.readUtf8());
|
||||
} else {
|
||||
this.frameCallback.onReadMessage(this.messageFrameBuffer.readByteString());
|
||||
}
|
||||
}
|
||||
|
||||
private void readUntilNonControlFrame() throws IOException {
|
||||
while (!this.closed) {
|
||||
readHeader();
|
||||
if (!this.isControlFrame) {
|
||||
return;
|
||||
} else {
|
||||
readControlFrame();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void processNextFrame() throws IOException {
|
||||
readHeader();
|
||||
if (this.isControlFrame) {
|
||||
readControlFrame();
|
||||
} else {
|
||||
readMessageFrame();
|
||||
}
|
||||
}
|
||||
}
|
198
sources/okhttp3/internal/ws/WebSocketWriter.java
Normal file
198
sources/okhttp3/internal/ws/WebSocketWriter.java
Normal file
@@ -0,0 +1,198 @@
|
||||
package okhttp3.internal.ws;
|
||||
|
||||
import android.support.v4.media.session.PlaybackStateCompat;
|
||||
import com.ubtrobot.jimu.robotapi.PeripheralType;
|
||||
import java.io.IOException;
|
||||
import java.util.Random;
|
||||
import okio.Buffer;
|
||||
import okio.BufferedSink;
|
||||
import okio.ByteString;
|
||||
import okio.Sink;
|
||||
import okio.Timeout;
|
||||
|
||||
/* loaded from: classes2.dex */
|
||||
final class WebSocketWriter {
|
||||
boolean activeWriter;
|
||||
final Buffer buffer = new Buffer();
|
||||
final FrameSink frameSink = new FrameSink();
|
||||
final boolean isClient;
|
||||
private final Buffer.UnsafeCursor maskCursor;
|
||||
private final byte[] maskKey;
|
||||
final Random random;
|
||||
final BufferedSink sink;
|
||||
final Buffer sinkBuffer;
|
||||
boolean writerClosed;
|
||||
|
||||
final class FrameSink implements Sink {
|
||||
boolean closed;
|
||||
long contentLength;
|
||||
int formatOpcode;
|
||||
boolean isFirstFrame;
|
||||
|
||||
FrameSink() {
|
||||
}
|
||||
|
||||
@Override // okio.Sink, java.io.Closeable, java.lang.AutoCloseable
|
||||
public void close() throws IOException {
|
||||
if (this.closed) {
|
||||
throw new IOException("closed");
|
||||
}
|
||||
WebSocketWriter webSocketWriter = WebSocketWriter.this;
|
||||
webSocketWriter.writeMessageFrame(this.formatOpcode, webSocketWriter.buffer.size(), this.isFirstFrame, true);
|
||||
this.closed = true;
|
||||
WebSocketWriter.this.activeWriter = false;
|
||||
}
|
||||
|
||||
@Override // okio.Sink, java.io.Flushable
|
||||
public void flush() throws IOException {
|
||||
if (this.closed) {
|
||||
throw new IOException("closed");
|
||||
}
|
||||
WebSocketWriter webSocketWriter = WebSocketWriter.this;
|
||||
webSocketWriter.writeMessageFrame(this.formatOpcode, webSocketWriter.buffer.size(), this.isFirstFrame, false);
|
||||
this.isFirstFrame = false;
|
||||
}
|
||||
|
||||
@Override // okio.Sink
|
||||
public Timeout timeout() {
|
||||
return WebSocketWriter.this.sink.timeout();
|
||||
}
|
||||
|
||||
@Override // okio.Sink
|
||||
public void write(Buffer buffer, long j) throws IOException {
|
||||
if (this.closed) {
|
||||
throw new IOException("closed");
|
||||
}
|
||||
WebSocketWriter.this.buffer.write(buffer, j);
|
||||
boolean z = this.isFirstFrame && this.contentLength != -1 && WebSocketWriter.this.buffer.size() > this.contentLength - PlaybackStateCompat.ACTION_PLAY_FROM_URI;
|
||||
long completeSegmentByteCount = WebSocketWriter.this.buffer.completeSegmentByteCount();
|
||||
if (completeSegmentByteCount <= 0 || z) {
|
||||
return;
|
||||
}
|
||||
WebSocketWriter.this.writeMessageFrame(this.formatOpcode, completeSegmentByteCount, this.isFirstFrame, false);
|
||||
this.isFirstFrame = false;
|
||||
}
|
||||
}
|
||||
|
||||
WebSocketWriter(boolean z, BufferedSink bufferedSink, Random random) {
|
||||
if (bufferedSink == null) {
|
||||
throw new NullPointerException("sink == null");
|
||||
}
|
||||
if (random == null) {
|
||||
throw new NullPointerException("random == null");
|
||||
}
|
||||
this.isClient = z;
|
||||
this.sink = bufferedSink;
|
||||
this.sinkBuffer = bufferedSink.buffer();
|
||||
this.random = random;
|
||||
this.maskKey = z ? new byte[4] : null;
|
||||
this.maskCursor = z ? new Buffer.UnsafeCursor() : null;
|
||||
}
|
||||
|
||||
private void writeControlFrame(int i, ByteString byteString) throws IOException {
|
||||
if (this.writerClosed) {
|
||||
throw new IOException("closed");
|
||||
}
|
||||
int size = byteString.size();
|
||||
if (size > 125) {
|
||||
throw new IllegalArgumentException("Payload size must be less than or equal to 125");
|
||||
}
|
||||
this.sinkBuffer.writeByte(i | PeripheralType.SERVO);
|
||||
if (this.isClient) {
|
||||
this.sinkBuffer.writeByte(size | PeripheralType.SERVO);
|
||||
this.random.nextBytes(this.maskKey);
|
||||
this.sinkBuffer.write(this.maskKey);
|
||||
if (size > 0) {
|
||||
long size2 = this.sinkBuffer.size();
|
||||
this.sinkBuffer.write(byteString);
|
||||
this.sinkBuffer.readAndWriteUnsafe(this.maskCursor);
|
||||
this.maskCursor.seek(size2);
|
||||
WebSocketProtocol.toggleMask(this.maskCursor, this.maskKey);
|
||||
this.maskCursor.close();
|
||||
}
|
||||
} else {
|
||||
this.sinkBuffer.writeByte(size);
|
||||
this.sinkBuffer.write(byteString);
|
||||
}
|
||||
this.sink.flush();
|
||||
}
|
||||
|
||||
Sink newMessageSink(int i, long j) {
|
||||
if (this.activeWriter) {
|
||||
throw new IllegalStateException("Another message writer is active. Did you call close()?");
|
||||
}
|
||||
this.activeWriter = true;
|
||||
FrameSink frameSink = this.frameSink;
|
||||
frameSink.formatOpcode = i;
|
||||
frameSink.contentLength = j;
|
||||
frameSink.isFirstFrame = true;
|
||||
frameSink.closed = false;
|
||||
return frameSink;
|
||||
}
|
||||
|
||||
void writeClose(int i, ByteString byteString) throws IOException {
|
||||
ByteString byteString2 = ByteString.EMPTY;
|
||||
if (i != 0 || byteString != null) {
|
||||
if (i != 0) {
|
||||
WebSocketProtocol.validateCloseCode(i);
|
||||
}
|
||||
Buffer buffer = new Buffer();
|
||||
buffer.writeShort(i);
|
||||
if (byteString != null) {
|
||||
buffer.write(byteString);
|
||||
}
|
||||
byteString2 = buffer.readByteString();
|
||||
}
|
||||
try {
|
||||
writeControlFrame(8, byteString2);
|
||||
} finally {
|
||||
this.writerClosed = true;
|
||||
}
|
||||
}
|
||||
|
||||
void writeMessageFrame(int i, long j, boolean z, boolean z2) throws IOException {
|
||||
if (this.writerClosed) {
|
||||
throw new IOException("closed");
|
||||
}
|
||||
if (!z) {
|
||||
i = 0;
|
||||
}
|
||||
if (z2) {
|
||||
i |= PeripheralType.SERVO;
|
||||
}
|
||||
this.sinkBuffer.writeByte(i);
|
||||
int i2 = this.isClient ? PeripheralType.SERVO : 0;
|
||||
if (j <= 125) {
|
||||
this.sinkBuffer.writeByte(((int) j) | i2);
|
||||
} else if (j <= 65535) {
|
||||
this.sinkBuffer.writeByte(i2 | 126);
|
||||
this.sinkBuffer.writeShort((int) j);
|
||||
} else {
|
||||
this.sinkBuffer.writeByte(i2 | 127);
|
||||
this.sinkBuffer.writeLong(j);
|
||||
}
|
||||
if (this.isClient) {
|
||||
this.random.nextBytes(this.maskKey);
|
||||
this.sinkBuffer.write(this.maskKey);
|
||||
if (j > 0) {
|
||||
long size = this.sinkBuffer.size();
|
||||
this.sinkBuffer.write(this.buffer, j);
|
||||
this.sinkBuffer.readAndWriteUnsafe(this.maskCursor);
|
||||
this.maskCursor.seek(size);
|
||||
WebSocketProtocol.toggleMask(this.maskCursor, this.maskKey);
|
||||
this.maskCursor.close();
|
||||
}
|
||||
} else {
|
||||
this.sinkBuffer.write(this.buffer, j);
|
||||
}
|
||||
this.sink.emit();
|
||||
}
|
||||
|
||||
void writePing(ByteString byteString) throws IOException {
|
||||
writeControlFrame(9, byteString);
|
||||
}
|
||||
|
||||
void writePong(ByteString byteString) throws IOException {
|
||||
writeControlFrame(10, byteString);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user