From 164940f4c11dab4b635e6252714f07a1dc71dd81 Mon Sep 17 00:00:00 2001 From: Jake Wharton Date: Thu, 5 Jul 2018 20:44:22 -0400 Subject: [PATCH] WebSocket WIP --- .../java/retrofit2/BuiltInConverters.java | 62 ++++++++++ .../src/main/java/retrofit2/Converter.java | 14 +++ .../main/java/retrofit2/OkHttpWebSocket.java | 77 ++++++++++++ .../java/retrofit2/OkHttpWebSocketCall.java | 110 ++++++++++++++++++ .../src/main/java/retrofit2/Retrofit.java | 88 ++++++++++++-- .../main/java/retrofit2/ServiceMethod.java | 6 + .../retrofit2/WebSocketServiceMethod.java | 78 +++++++++++++ .../java/retrofit2/internal/WebSocket.java | 27 +++++ .../retrofit2/internal/WebSocketCall.java | 20 ++++ .../retrofit2/internal/WebSocketListener.java | 36 ++++++ 10 files changed, 509 insertions(+), 9 deletions(-) create mode 100644 retrofit/src/main/java/retrofit2/OkHttpWebSocket.java create mode 100644 retrofit/src/main/java/retrofit2/OkHttpWebSocketCall.java create mode 100644 retrofit/src/main/java/retrofit2/WebSocketServiceMethod.java create mode 100644 retrofit/src/main/java/retrofit2/internal/WebSocket.java create mode 100644 retrofit/src/main/java/retrofit2/internal/WebSocketCall.java create mode 100644 retrofit/src/main/java/retrofit2/internal/WebSocketListener.java diff --git a/retrofit/src/main/java/retrofit2/BuiltInConverters.java b/retrofit/src/main/java/retrofit2/BuiltInConverters.java index 86228d5c..a86d4293 100644 --- a/retrofit/src/main/java/retrofit2/BuiltInConverters.java +++ b/retrofit/src/main/java/retrofit2/BuiltInConverters.java @@ -18,8 +18,10 @@ package retrofit2; import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.reflect.Type; +import javax.annotation.Nullable; import okhttp3.RequestBody; import okhttp3.ResponseBody; +import okio.ByteString; import retrofit2.http.Streaming; final class BuiltInConverters extends Converter.Factory { @@ -46,6 +48,30 @@ final class BuiltInConverters extends Converter.Factory { return null; } + @Nullable @Override + public Converter outgoingMessageConverter(Type type, Annotation[] annotations, + Retrofit retrofit) { + if (type == String.class) { + return StringOutgoingMessageConverter.INSTANCE; + } + if (type == ByteString.class) { + return ByteStringOutgoingMessageConverter.INSTANCE; + } + return null; + } + + @Nullable @Override + public Converter incomingMessageConverter(Type type, Annotation[] annotations, + Retrofit retrofit) { + if (type == String.class) { + return StringIncomingMessageConverter.INSTANCE; + } + if (type == ByteString.class) { + return ByteStringIncomingMessageConverter.INSTANCE; + } + return null; + } + static final class VoidResponseBodyConverter implements Converter { static final VoidResponseBodyConverter INSTANCE = new VoidResponseBodyConverter(); @@ -93,4 +119,40 @@ final class BuiltInConverters extends Converter.Factory { return value.toString(); } } + + static final class StringOutgoingMessageConverter implements Converter { + static final StringOutgoingMessageConverter INSTANCE = new StringOutgoingMessageConverter(); + + @Override public RequestBody convert(String value) { + return RequestBody.create(STRING_MESSAGE, value); + } + } + + static final class StringIncomingMessageConverter implements Converter { + static final StringIncomingMessageConverter INSTANCE = new StringIncomingMessageConverter(); + + @Override public String convert(ResponseBody value) throws IOException { + return value.string(); + } + } + + static final class ByteStringOutgoingMessageConverter + implements Converter { + static final ByteStringOutgoingMessageConverter INSTANCE = + new ByteStringOutgoingMessageConverter(); + + @Override public RequestBody convert(ByteString value) { + return RequestBody.create(BYTESTRING_MESSAGE, value); + } + } + + static final class ByteStringIncomingMessageConverter + implements Converter { + static final ByteStringIncomingMessageConverter INSTANCE = + new ByteStringIncomingMessageConverter(); + + @Override public ByteString convert(ResponseBody value) throws IOException { + return value.source().readByteString(); + } + } } diff --git a/retrofit/src/main/java/retrofit2/Converter.java b/retrofit/src/main/java/retrofit2/Converter.java index 37a64f8d..c22fe613 100644 --- a/retrofit/src/main/java/retrofit2/Converter.java +++ b/retrofit/src/main/java/retrofit2/Converter.java @@ -20,6 +20,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import javax.annotation.Nullable; +import okhttp3.MediaType; import okhttp3.RequestBody; import okhttp3.ResponseBody; import retrofit2.http.Body; @@ -39,6 +40,9 @@ import retrofit2.http.QueryMap; * into the {@link Retrofit} instance. */ public interface Converter { + MediaType STRING_MESSAGE = MediaType.parse("application/vnd+retrofit.ws+string"); + MediaType BYTESTRING_MESSAGE = MediaType.parse("application/vnd+retrofit.ws+bytestring"); + T convert(F value) throws IOException; /** Creates {@link Converter} instances based on a type and target usage. */ @@ -77,6 +81,16 @@ public interface Converter { return null; } + public @Nullable Converter outgoingMessageConverter(Type type, + Annotation[] annotations, Retrofit retrofit) { + return null; + } + + public @Nullable Converter incomingMessageConverter(Type type, + Annotation[] annotations, Retrofit retrofit) { + return null; + } + /** * Extract the upper bound of the generic parameter at {@code index} from {@code type}. For * example, index 1 of {@code Map} returns {@code Runnable}. diff --git a/retrofit/src/main/java/retrofit2/OkHttpWebSocket.java b/retrofit/src/main/java/retrofit2/OkHttpWebSocket.java new file mode 100644 index 00000000..2ffcf2dc --- /dev/null +++ b/retrofit/src/main/java/retrofit2/OkHttpWebSocket.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2018 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package retrofit2; + +import java.io.IOException; +import javax.annotation.Nullable; +import okhttp3.MediaType; +import okhttp3.Request; +import okhttp3.RequestBody; +import okio.Buffer; +import retrofit2.internal.WebSocket; + +import static retrofit2.Converter.BYTESTRING_MESSAGE; +import static retrofit2.Converter.STRING_MESSAGE; + +final class OkHttpWebSocket implements WebSocket { + private final okhttp3.WebSocket rawWebSocket; + private final Converter outConverter; + + OkHttpWebSocket(okhttp3.WebSocket rawWebSocket, Converter outConverter) { + this.rawWebSocket = rawWebSocket; + this.outConverter = outConverter; + } + + @Override public Request request() { + return rawWebSocket.request(); + } + + @Override public long queueSize() { + return rawWebSocket.queueSize(); + } + + @Override public boolean send(OutT item) { + Buffer buffer = new Buffer(); + MediaType contentType; + try { + RequestBody body = outConverter.convert(item); + body.writeTo(buffer); + contentType = body.contentType(); + } catch (IOException e) { + rawWebSocket.cancel(); + throw new RuntimeException("Failed to convert: " + item, e); + } + + if (STRING_MESSAGE.equals(contentType)) { + return rawWebSocket.send(buffer.readUtf8()); + } else if (BYTESTRING_MESSAGE.equals(contentType)) { + return rawWebSocket.send(buffer.readByteString()); + } + throw new IllegalStateException("Outgoing message converter " + + outConverter + + " returned RequestBody with invalid content type " + + contentType + + ". Converter.STRING_MESSAGE or Converter.BYTESTRING_MESSAGE are the only valid values."); + } + + @Override public boolean close(int code, @Nullable String reason) { + return rawWebSocket.close(code, reason); + } + + @Override public void cancel() { + rawWebSocket.cancel(); + } +} diff --git a/retrofit/src/main/java/retrofit2/OkHttpWebSocketCall.java b/retrofit/src/main/java/retrofit2/OkHttpWebSocketCall.java new file mode 100644 index 00000000..3be288c4 --- /dev/null +++ b/retrofit/src/main/java/retrofit2/OkHttpWebSocketCall.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2018 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package retrofit2; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicReference; +import javax.annotation.Nullable; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; +import okio.ByteString; +import retrofit2.internal.WebSocket; +import retrofit2.internal.WebSocketCall; +import retrofit2.internal.WebSocketListener; + +import static retrofit2.Converter.BYTESTRING_MESSAGE; +import static retrofit2.Converter.STRING_MESSAGE; + +final class OkHttpWebSocketCall implements WebSocketCall { + private final okhttp3.WebSocket.Factory rawWebSocketFactory; + private final RequestFactory requestFactory; + private final @Nullable Object[] args; + private final Converter inConverter; + private final Converter outConverter; + + OkHttpWebSocketCall(okhttp3.WebSocket.Factory rawWebSocketFactory, RequestFactory requestFactory, + @Nullable Object[] args, Converter inConverter, + Converter outConverter) { + this.rawWebSocketFactory = rawWebSocketFactory; + this.requestFactory = requestFactory; + this.args = args; + this.inConverter = inConverter; + this.outConverter = outConverter; + } + + @Override + public WebSocket connect(final WebSocketListener listener) { + Request request; + try { + request = requestFactory.create(args); + } catch (IOException e) { + // TODO call on failure? but we don't have a WebSocket instance to use! + return null; + } + final AtomicReference> webSocketRef = new AtomicReference<>(); + okhttp3.WebSocket rawWebSocket = + rawWebSocketFactory.newWebSocket(request, new okhttp3.WebSocketListener() { + @Override public void onOpen(okhttp3.WebSocket webSocket, Response response) { + listener.onOpen(webSocketRef.get(), response); + } + + @Override public void onMessage(okhttp3.WebSocket webSocket, String text) { + ResponseBody body = ResponseBody.create(STRING_MESSAGE, text); + + InT message; + try { + message = inConverter.convert(body); + } catch (IOException e) { + // TODO call on failure? + return; + } + listener.onMessage(webSocketRef.get(), message); + } + + @Override public void onMessage(okhttp3.WebSocket webSocket, ByteString bytes) { + // TODO drop toByteArray with OkHttp 3.11: https://github.com/square/okhttp/pull/4115 + ResponseBody body = ResponseBody.create(BYTESTRING_MESSAGE, bytes.toByteArray()); + + InT message; + try { + message = inConverter.convert(body); + } catch (IOException e) { + // TODO call on failure? + return; + } + listener.onMessage(webSocketRef.get(), message); + } + + @Override public void onClosing(okhttp3.WebSocket webSocket, int code, String reason) { + listener.onClosing(webSocketRef.get(), code, reason); + } + + @Override public void onClosed(okhttp3.WebSocket webSocket, int code, String reason) { + listener.onClosed(webSocketRef.get(), code, reason); + } + + @Override public void onFailure(okhttp3.WebSocket webSocket, Throwable t, + @Nullable Response response) { + listener.onFailure(webSocketRef.get(), t, response); + } + }); + WebSocket webSocket = new OkHttpWebSocket<>(rawWebSocket, outConverter); + webSocketRef.set(webSocket); + return webSocket; + } +} diff --git a/retrofit/src/main/java/retrofit2/Retrofit.java b/retrofit/src/main/java/retrofit2/Retrofit.java index 99369089..b4690925 100644 --- a/retrofit/src/main/java/retrofit2/Retrofit.java +++ b/retrofit/src/main/java/retrofit2/Retrofit.java @@ -61,16 +61,19 @@ public final class Retrofit { private final Map> serviceMethodCache = new ConcurrentHashMap<>(); final okhttp3.Call.Factory callFactory; + final okhttp3.WebSocket.Factory webSocketFactory; final HttpUrl baseUrl; final List converterFactories; final List callAdapterFactories; final @Nullable Executor callbackExecutor; final boolean validateEagerly; - Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl, - List converterFactories, List callAdapterFactories, - @Nullable Executor callbackExecutor, boolean validateEagerly) { + Retrofit(okhttp3.Call.Factory callFactory, okhttp3.WebSocket.Factory webSocketFactory, + HttpUrl baseUrl, List converterFactories, + List callAdapterFactories, @Nullable Executor callbackExecutor, + boolean validateEagerly) { this.callFactory = callFactory; + this.webSocketFactory = webSocketFactory; this.baseUrl = baseUrl; this.converterFactories = converterFactories; // Copy+unmodifiable at call site. this.callAdapterFactories = callAdapterFactories; // Copy+unmodifiable at call site. @@ -370,6 +373,52 @@ public final class Retrofit { return (Converter) BuiltInConverters.ToStringConverter.INSTANCE; } + public Converter outgoingMessageConverter(Type type, + Annotation[] annotations) { + checkNotNull(type, "type == null"); + checkNotNull(annotations, "annotations == null"); + + for (int i = 0, count = converterFactories.size(); i < count; i++) { + Converter converter = + converterFactories.get(i).outgoingMessageConverter(type, annotations, this); + if (converter != null) { + //noinspection unchecked + return (Converter) converter; + } + } + + StringBuilder builder = new StringBuilder("Could not locate outgoing message converter for ") + .append(type) + .append(".\n Tried:"); + for (Converter.Factory converterFactory : converterFactories) { + builder.append("\n * ").append(converterFactory.getClass().getName()); + } + throw new IllegalArgumentException(builder.toString()); + } + + public Converter incomingMessageConverter(Type type, + Annotation[] annotations) { + checkNotNull(type, "type == null"); + checkNotNull(annotations, "annotations == null"); + + for (int i = 0, count = converterFactories.size(); i < count; i++) { + Converter converter = + converterFactories.get(i).incomingMessageConverter(type, annotations, this); + if (converter != null) { + //noinspection unchecked + return (Converter) converter; + } + } + + StringBuilder builder = new StringBuilder("Could not locate incoming message converter for ") + .append(type) + .append(".\n Tried:"); + for (Converter.Factory converterFactory : converterFactories) { + builder.append("\n * ").append(converterFactory.getClass().getName()); + } + throw new IllegalArgumentException(builder.toString()); + } + /** * The executor used for {@link Callback} methods on a {@link Call}. This may be {@code null}, * in which case callbacks should be made synchronously on the background thread. @@ -391,6 +440,7 @@ public final class Retrofit { public static final class Builder { private final Platform platform; private @Nullable okhttp3.Call.Factory callFactory; + private @Nullable okhttp3.WebSocket.Factory webSocketFactory; private HttpUrl baseUrl; private final List converterFactories = new ArrayList<>(); private final List callAdapterFactories = new ArrayList<>(); @@ -408,6 +458,7 @@ public final class Retrofit { Builder(Retrofit retrofit) { platform = Platform.get(); callFactory = retrofit.callFactory; + webSocketFactory = retrofit.webSocketFactory; baseUrl = retrofit.baseUrl; converterFactories.addAll(retrofit.converterFactories); @@ -425,10 +476,11 @@ public final class Retrofit { /** * The HTTP client used for requests. *

- * This is a convenience method for calling {@link #callFactory}. + * This is a convenience method for calling {@link #callFactory} and {@link #webSocketFactory}. */ public Builder client(OkHttpClient client) { - return callFactory(checkNotNull(client, "client == null")); + checkNotNull(client, "client == null"); + return callFactory(client).webSocketFactory(client); } /** @@ -441,6 +493,16 @@ public final class Retrofit { return this; } + /** + * Specify a custom call factory for creating {@link Call} instances. + *

+ * Note: Calling {@link #client} automatically sets this value. + */ + public Builder webSocketFactory(okhttp3.WebSocket.Factory factory) { + this.webSocketFactory = checkNotNull(factory, "factory == null"); + return this; + } + /** * Set the API base URL. * @@ -573,8 +635,15 @@ public final class Retrofit { } okhttp3.Call.Factory callFactory = this.callFactory; - if (callFactory == null) { - callFactory = new OkHttpClient(); + okhttp3.WebSocket.Factory webSocketFactory = this.webSocketFactory; + if (callFactory == null || webSocketFactory == null) { + OkHttpClient client = new OkHttpClient(); + if (callFactory == null) { + callFactory = client; + } + if (webSocketFactory == null) { + webSocketFactory = client; + } } Executor callbackExecutor = this.callbackExecutor; @@ -595,8 +664,9 @@ public final class Retrofit { converterFactories.add(new BuiltInConverters()); converterFactories.addAll(this.converterFactories); - return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories), - unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly); + return new Retrofit(callFactory, webSocketFactory, baseUrl, + unmodifiableList(converterFactories), unmodifiableList(callAdapterFactories), + callbackExecutor, validateEagerly); } } } diff --git a/retrofit/src/main/java/retrofit2/ServiceMethod.java b/retrofit/src/main/java/retrofit2/ServiceMethod.java index 2ae44628..dcc56485 100644 --- a/retrofit/src/main/java/retrofit2/ServiceMethod.java +++ b/retrofit/src/main/java/retrofit2/ServiceMethod.java @@ -18,6 +18,7 @@ package retrofit2; import java.lang.reflect.Method; import java.lang.reflect.Type; import javax.annotation.Nullable; +import retrofit2.internal.WebSocket; import static retrofit2.Utils.methodError; @@ -32,6 +33,11 @@ abstract class ServiceMethod { throw methodError(method, "Service methods cannot return void."); } + if (Utils.getRawType(returnType) == WebSocket.class) { + //noinspection unchecked Return type checked by enclosing conditional. + return (ServiceMethod) new WebSocketServiceMethod.Builder(retrofit, method).build(); + } + return new HttpServiceMethod.Builder(retrofit, method).build(); } diff --git a/retrofit/src/main/java/retrofit2/WebSocketServiceMethod.java b/retrofit/src/main/java/retrofit2/WebSocketServiceMethod.java new file mode 100644 index 00000000..adec4138 --- /dev/null +++ b/retrofit/src/main/java/retrofit2/WebSocketServiceMethod.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2018 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package retrofit2; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import javax.annotation.Nullable; +import okhttp3.RequestBody; +import okhttp3.ResponseBody; +import okhttp3.WebSocket; +import retrofit2.internal.WebSocketCall; + +final class WebSocketServiceMethod extends ServiceMethod> { + private final okhttp3.WebSocket.Factory rawWebSocketFactory; + private final RequestFactory requestFactory; + private final Converter inConverter; + private final Converter outConverter; + + WebSocketServiceMethod(WebSocket.Factory rawWebSocketFactory, RequestFactory requestFactory, + Converter inConverter, Converter outConverter) { + this.rawWebSocketFactory = rawWebSocketFactory; + this.requestFactory = requestFactory; + this.inConverter = inConverter; + this.outConverter = outConverter; + } + + @Override WebSocketCall invoke(@Nullable Object[] args) { + return new OkHttpWebSocketCall<>(rawWebSocketFactory, requestFactory, args, inConverter, + outConverter); + } + + static final class Builder { + private final Retrofit retrofit; + private final Method method; + + Builder(Retrofit retrofit, Method method) { + this.retrofit = retrofit; + this.method = method; + } + + ServiceMethod> build() { + Type returnType = method.getGenericReturnType(); + if (!(returnType instanceof ParameterizedType)) { + throw Utils.methodError(method, ""); // TODO error message + } + ParameterizedType parameterizedReturnType = (ParameterizedType) returnType; + Type inType = Utils.getParameterUpperBound(0, parameterizedReturnType); + Type outType = Utils.getParameterUpperBound(1, parameterizedReturnType); + + Annotation[] methodAnnotations = method.getAnnotations(); + + Converter inConverter = + retrofit.incomingMessageConverter(inType, methodAnnotations); + Converter outConverter = + retrofit.outgoingMessageConverter(outType, methodAnnotations); + + RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method); + + return new WebSocketServiceMethod(retrofit.webSocketFactory, requestFactory, inConverter, + outConverter); + } + } +} diff --git a/retrofit/src/main/java/retrofit2/internal/WebSocket.java b/retrofit/src/main/java/retrofit2/internal/WebSocket.java new file mode 100644 index 00000000..0a8df4e2 --- /dev/null +++ b/retrofit/src/main/java/retrofit2/internal/WebSocket.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2018 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package retrofit2.internal; + +import javax.annotation.Nullable; +import okhttp3.Request; + +public interface WebSocket { + Request request(); + long queueSize(); + boolean send(OutT item); + boolean close(int code, @Nullable String reason); + void cancel(); +} diff --git a/retrofit/src/main/java/retrofit2/internal/WebSocketCall.java b/retrofit/src/main/java/retrofit2/internal/WebSocketCall.java new file mode 100644 index 00000000..919b8001 --- /dev/null +++ b/retrofit/src/main/java/retrofit2/internal/WebSocketCall.java @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2018 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package retrofit2.internal; + +public interface WebSocketCall { + WebSocket connect(WebSocketListener listener); +} diff --git a/retrofit/src/main/java/retrofit2/internal/WebSocketListener.java b/retrofit/src/main/java/retrofit2/internal/WebSocketListener.java new file mode 100644 index 00000000..5f7233ce --- /dev/null +++ b/retrofit/src/main/java/retrofit2/internal/WebSocketListener.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2018 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package retrofit2.internal; + +import javax.annotation.Nullable; +import okhttp3.Response; + +public abstract class WebSocketListener { + public void onOpen(WebSocket webSocket, Response response) { + } + + public void onMessage(WebSocket webSocket, InT item) { + } + + public void onClosing(WebSocket webSocket, int code, String reason) { + } + + public void onClosed(WebSocket webSocket, int code, String reason) { + } + + public void onFailure(WebSocket webSocket, Throwable t, @Nullable Response response) { + } +} -- GitLab