• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 Square, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.squareup.okhttp.ws;
17 
18 import com.squareup.okhttp.Response;
19 import com.squareup.okhttp.internal.ws.WebSocketReader;
20 import java.io.IOException;
21 import java.util.concurrent.BlockingQueue;
22 import java.util.concurrent.LinkedBlockingQueue;
23 import java.util.concurrent.TimeUnit;
24 import okio.Buffer;
25 import okio.BufferedSource;
26 
27 import static com.squareup.okhttp.ws.WebSocket.PayloadType.BINARY;
28 import static com.squareup.okhttp.ws.WebSocket.PayloadType.TEXT;
29 import static org.junit.Assert.assertEquals;
30 import static org.junit.Assert.assertNotNull;
31 import static org.junit.Assert.assertTrue;
32 
33 public final class WebSocketRecorder implements WebSocketReader.FrameCallback, WebSocketListener {
34   public interface MessageDelegate {
onMessage(BufferedSource payload, WebSocket.PayloadType type)35     void onMessage(BufferedSource payload, WebSocket.PayloadType type) throws IOException;
36   }
37 
38   private final BlockingQueue<Object> events = new LinkedBlockingQueue<>();
39   private MessageDelegate delegate;
40 
41   /** Sets a delegate for the next call to {@link #onMessage}. Cleared after invoked. */
setNextMessageDelegate(MessageDelegate delegate)42   public void setNextMessageDelegate(MessageDelegate delegate) {
43     this.delegate = delegate;
44   }
45 
onOpen(WebSocket webSocket, Response response)46   @Override public void onOpen(WebSocket webSocket, Response response) {
47   }
48 
onMessage(BufferedSource source, WebSocket.PayloadType type)49   @Override public void onMessage(BufferedSource source, WebSocket.PayloadType type)
50       throws IOException {
51     if (delegate != null) {
52       delegate.onMessage(source, type);
53       delegate = null;
54     } else {
55       Message message = new Message(type);
56       source.readAll(message.buffer);
57       source.close();
58       events.add(message);
59     }
60   }
61 
onPing(Buffer buffer)62   @Override public void onPing(Buffer buffer) {
63     events.add(new Ping(buffer));
64   }
65 
onPong(Buffer buffer)66   @Override public void onPong(Buffer buffer) {
67     events.add(new Pong(buffer));
68   }
69 
onClose(int code, String reason)70   @Override public void onClose(int code, String reason) {
71     events.add(new Close(code, reason));
72   }
73 
onFailure(IOException e, Response response)74   @Override public void onFailure(IOException e, Response response) {
75     events.add(e);
76   }
77 
nextEvent()78   private Object nextEvent() {
79     try {
80       Object event = events.poll(10, TimeUnit.SECONDS);
81       if (event == null) {
82         throw new AssertionError("Timed out.");
83       }
84       return event;
85     } catch (InterruptedException e) {
86       throw new AssertionError(e);
87     }
88   }
89 
assertTextMessage(String payload)90   public void assertTextMessage(String payload) {
91     Message message = new Message(TEXT);
92     message.buffer.writeUtf8(payload);
93     assertEquals(message, nextEvent());
94   }
95 
assertBinaryMessage(byte[] payload)96   public void assertBinaryMessage(byte[] payload) {
97     Message message = new Message(BINARY);
98     message.buffer.write(payload);
99     assertEquals(message, nextEvent());
100   }
101 
assertPing(Buffer payload)102   public void assertPing(Buffer payload) {
103     assertEquals(new Ping(payload), nextEvent());
104   }
105 
assertPong(Buffer payload)106   public void assertPong(Buffer payload) {
107     assertEquals(new Pong(payload), nextEvent());
108   }
109 
assertClose(int code, String reason)110   public void assertClose(int code, String reason) {
111     assertEquals(new Close(code, reason), nextEvent());
112   }
113 
assertFailure(Class<? extends IOException> cls, String message)114   public void assertFailure(Class<? extends IOException> cls, String message) {
115     Object event = nextEvent();
116     String errorMessage =
117         "Expected [" + cls.getName() + ": " + message + "] but was [" + event + "].";
118     assertNotNull(errorMessage, event);
119     assertEquals(errorMessage, cls, event.getClass());
120     assertEquals(errorMessage, cls.cast(event).getMessage(), message);
121   }
122 
assertExhausted()123   public void assertExhausted() {
124     assertTrue("Remaining events: " + events, events.isEmpty());
125   }
126 
127   private static class Message {
128     public final WebSocket.PayloadType type;
129     public final Buffer buffer = new Buffer();
130 
Message(WebSocket.PayloadType type)131     private Message(WebSocket.PayloadType type) {
132       this.type = type;
133     }
134 
toString()135     @Override public String toString() {
136       return "Message[" + type + " " + buffer + "]";
137     }
138 
hashCode()139     @Override public int hashCode() {
140       return type.hashCode() * 37 + buffer.hashCode();
141     }
142 
equals(Object obj)143     @Override public boolean equals(Object obj) {
144       if (obj instanceof Message) {
145         Message other = (Message) obj;
146         return type == other.type && buffer.equals(other.buffer);
147       }
148       return false;
149     }
150   }
151 
152   private static class Ping {
153     public final Buffer buffer;
154 
Ping(Buffer buffer)155     private Ping(Buffer buffer) {
156       this.buffer = buffer;
157     }
158 
toString()159     @Override public String toString() {
160       return "Ping[" + buffer + "]";
161     }
162 
hashCode()163     @Override public int hashCode() {
164       return buffer.hashCode();
165     }
166 
equals(Object obj)167     @Override public boolean equals(Object obj) {
168       if (obj instanceof Ping) {
169         Ping other = (Ping) obj;
170         return buffer == null ? other.buffer == null : buffer.equals(other.buffer);
171       }
172       return false;
173     }
174   }
175 
176   private static class Pong {
177     public final Buffer buffer;
178 
Pong(Buffer buffer)179     private Pong(Buffer buffer) {
180       this.buffer = buffer;
181     }
182 
toString()183     @Override public String toString() {
184       return "Pong[" + buffer + "]";
185     }
186 
hashCode()187     @Override public int hashCode() {
188       return buffer.hashCode();
189     }
190 
equals(Object obj)191     @Override public boolean equals(Object obj) {
192       if (obj instanceof Pong) {
193         Pong other = (Pong) obj;
194         return buffer == null ? other.buffer == null : buffer.equals(other.buffer);
195       }
196       return false;
197     }
198   }
199 
200   private static class Close {
201     public final int code;
202     public final String reason;
203 
Close(int code, String reason)204     private Close(int code, String reason) {
205       this.code = code;
206       this.reason = reason;
207     }
208 
toString()209     @Override public String toString() {
210       return "Close[" + code + " " + reason + "]";
211     }
212 
hashCode()213     @Override public int hashCode() {
214       return code * 37 + reason.hashCode();
215     }
216 
equals(Object obj)217     @Override public boolean equals(Object obj) {
218       if (obj instanceof Close) {
219         Close other = (Close) obj;
220         return code == other.code && reason.equals(other.reason);
221       }
222       return false;
223     }
224   }
225 }
226