• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 Google 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.mockwebserver;
17 
18 import com.squareup.okhttp.Headers;
19 import com.squareup.okhttp.internal.Internal;
20 import com.squareup.okhttp.internal.framed.Settings;
21 import com.squareup.okhttp.ws.WebSocketListener;
22 import java.util.ArrayList;
23 import java.util.List;
24 import java.util.function.Consumer;
25 import java.util.concurrent.TimeUnit;
26 import okio.Buffer;
27 
28 /** A scripted response to be replayed by the mock web server. */
29 public final class MockResponse implements Cloneable {
30   private static final String CHUNKED_BODY_HEADER = "Transfer-encoding: chunked";
31 
32   private String status = "HTTP/1.1 200 OK";
33   private Headers.Builder headers = new Headers.Builder();
34 
35   private Buffer body;
36 
37   private long throttleBytesPerPeriod = Long.MAX_VALUE;
38   private long throttlePeriodAmount = 1;
39   private TimeUnit throttlePeriodUnit = TimeUnit.SECONDS;
40 
41   private SocketPolicy socketPolicy = SocketPolicy.KEEP_OPEN;
42   private Consumer<SocketPolicy> socketShutdownListener = null;
43 
44   private long bodyDelayAmount = 0;
45   private TimeUnit bodyDelayUnit = TimeUnit.MILLISECONDS;
46 
47   private List<PushPromise> promises = new ArrayList<>();
48   private Settings settings;
49   private WebSocketListener webSocketListener;
50 
51   /** Creates a new mock response with an empty body. */
MockResponse()52   public MockResponse() {
53     setHeader("Content-Length", 0);
54   }
55 
clone()56   @Override public MockResponse clone() {
57     try {
58       MockResponse result = (MockResponse) super.clone();
59       result.headers = headers.build().newBuilder();
60       result.promises = new ArrayList<>(promises);
61       return result;
62     } catch (CloneNotSupportedException e) {
63       throw new AssertionError();
64     }
65   }
66 
67   /** Returns the HTTP response line, such as "HTTP/1.1 200 OK". */
getStatus()68   public String getStatus() {
69     return status;
70   }
71 
setResponseCode(int code)72   public MockResponse setResponseCode(int code) {
73     return setStatus("HTTP/1.1 " + code + " OK");
74   }
75 
setStatus(String status)76   public MockResponse setStatus(String status) {
77     this.status = status;
78     return this;
79   }
80 
81   /** Returns the HTTP headers, such as "Content-Length: 0". */
getHeaders()82   public Headers getHeaders() {
83     return headers.build();
84   }
85 
86   /**
87    * Removes all HTTP headers including any "Content-Length" and
88    * "Transfer-encoding" headers that were added by default.
89    */
clearHeaders()90   public MockResponse clearHeaders() {
91     headers = new Headers.Builder();
92     return this;
93   }
94 
95   /**
96    * Adds {@code header} as an HTTP header. For well-formed HTTP {@code header}
97    * should contain a name followed by a colon and a value.
98    */
addHeader(String header)99   public MockResponse addHeader(String header) {
100     headers.add(header);
101     return this;
102   }
103 
104   /**
105    * Adds a new header with the name and value. This may be used to add multiple
106    * headers with the same name.
107    */
addHeader(String name, Object value)108   public MockResponse addHeader(String name, Object value) {
109     headers.add(name, String.valueOf(value));
110     return this;
111   }
112 
113   /**
114    * Adds a new header with the name and value. This may be used to add multiple
115    * headers with the same name. Unlike {@link #addHeader(String, Object)} this
116    * does not validate the name and value.
117    */
addHeaderLenient(String name, Object value)118   public MockResponse addHeaderLenient(String name, Object value) {
119     Internal.instance.addLenient(headers, name, String.valueOf(value));
120     return this;
121   }
122 
123   /**
124    * Removes all headers named {@code name}, then adds a new header with the
125    * name and value.
126    */
setHeader(String name, Object value)127   public MockResponse setHeader(String name, Object value) {
128     removeHeader(name);
129     return addHeader(name, value);
130   }
131 
132   /** Replaces all headers with those specified in {@code headers}. */
setHeaders(Headers headers)133   public MockResponse setHeaders(Headers headers) {
134     this.headers = headers.newBuilder();
135     return this;
136   }
137 
138   /** Removes all headers named {@code name}. */
removeHeader(String name)139   public MockResponse removeHeader(String name) {
140     headers.removeAll(name);
141     return this;
142   }
143 
144   /** Returns a copy of the raw HTTP payload. */
getBody()145   public Buffer getBody() {
146     return body != null ? body.clone() : null;
147   }
148 
setBody(Buffer body)149   public MockResponse setBody(Buffer body) {
150     setHeader("Content-Length", body.size());
151     this.body = body.clone(); // Defensive copy.
152     return this;
153   }
154 
155   /** Sets the response body to the UTF-8 encoded bytes of {@code body}. */
setBody(String body)156   public MockResponse setBody(String body) {
157     return setBody(new Buffer().writeUtf8(body));
158   }
159 
160   /**
161    * Sets the response body to {@code body}, chunked every {@code maxChunkSize}
162    * bytes.
163    */
setChunkedBody(Buffer body, int maxChunkSize)164   public MockResponse setChunkedBody(Buffer body, int maxChunkSize) {
165     removeHeader("Content-Length");
166     headers.add(CHUNKED_BODY_HEADER);
167 
168     Buffer bytesOut = new Buffer();
169     while (!body.exhausted()) {
170       long chunkSize = Math.min(body.size(), maxChunkSize);
171       bytesOut.writeHexadecimalUnsignedLong(chunkSize);
172       bytesOut.writeUtf8("\r\n");
173       bytesOut.write(body, chunkSize);
174       bytesOut.writeUtf8("\r\n");
175     }
176     bytesOut.writeUtf8("0\r\n\r\n"); // Last chunk + empty trailer + CRLF.
177 
178     this.body = bytesOut;
179     return this;
180   }
181 
182   /**
183    * Sets the response body to the UTF-8 encoded bytes of {@code body}, chunked
184    * every {@code maxChunkSize} bytes.
185    */
setChunkedBody(String body, int maxChunkSize)186   public MockResponse setChunkedBody(String body, int maxChunkSize) {
187     return setChunkedBody(new Buffer().writeUtf8(body), maxChunkSize);
188   }
189 
getSocketPolicy()190   public SocketPolicy getSocketPolicy() {
191     return socketPolicy;
192   }
193 
setSocketPolicy(SocketPolicy socketPolicy)194   public MockResponse setSocketPolicy(SocketPolicy socketPolicy) {
195     this.socketPolicy = socketPolicy;
196     return this;
197   }
198 
199   /**
200    * Sets a listener that can wait until a socket is closed. This is important if the
201    * {@link SocketPolicy} gets set to something that shuts down the socket after a transaction and
202    * that socket may get reused in subsequent calls if they happen too fast.
203    *
204    * @param listener The listener that will be notified when a socket is closed. This could be an
205    *                 instance of {@link SocketShutdownListener}.
206    *
207    * @see SocketPolicy
208    * @see SocketShutdownListener
209    */
setSocketShutdownListener(Consumer<SocketPolicy> listener)210   public MockResponse setSocketShutdownListener(Consumer<SocketPolicy> listener) {
211       this.socketShutdownListener = listener;
212       return this;
213   }
214 
notifyShutdown(SocketPolicy reason)215   void notifyShutdown(SocketPolicy reason) {
216       if (socketShutdownListener != null) {
217           socketShutdownListener.accept(reason);
218       }
219   }
220 
221   /**
222    * Throttles the response body writer to sleep for the given period after each
223    * series of {@code bytesPerPeriod} bytes are written. Use this to simulate
224    * network behavior.
225    */
throttleBody(long bytesPerPeriod, long period, TimeUnit unit)226   public MockResponse throttleBody(long bytesPerPeriod, long period, TimeUnit unit) {
227     this.throttleBytesPerPeriod = bytesPerPeriod;
228     this.throttlePeriodAmount = period;
229     this.throttlePeriodUnit = unit;
230     return this;
231   }
232 
getThrottleBytesPerPeriod()233   public long getThrottleBytesPerPeriod() {
234     return throttleBytesPerPeriod;
235   }
236 
getThrottlePeriod(TimeUnit unit)237   public long getThrottlePeriod(TimeUnit unit) {
238     return unit.convert(throttlePeriodAmount, throttlePeriodUnit);
239   }
240 
241   /**
242    * Set the delayed time of the response body to {@code delay}. This applies to the
243    * response body only; response headers are not affected.
244    */
setBodyDelay(long delay, TimeUnit unit)245   public MockResponse setBodyDelay(long delay, TimeUnit unit) {
246     bodyDelayAmount = delay;
247     bodyDelayUnit = unit;
248     return this;
249   }
250 
getBodyDelay(TimeUnit unit)251   public long getBodyDelay(TimeUnit unit) {
252     return unit.convert(bodyDelayAmount, bodyDelayUnit);
253   }
254 
255   /**
256    * When {@link MockWebServer#setProtocols(java.util.List) protocols}
257    * include {@linkplain com.squareup.okhttp.Protocol#HTTP_2}, this attaches a
258    * pushed stream to this response.
259    */
withPush(PushPromise promise)260   public MockResponse withPush(PushPromise promise) {
261     this.promises.add(promise);
262     return this;
263   }
264 
265   /** Returns the streams the server will push with this response. */
getPushPromises()266   public List<PushPromise> getPushPromises() {
267     return promises;
268   }
269 
270   /**
271    * When {@linkplain MockWebServer#setProtocols(java.util.List) protocols}
272    * include {@linkplain com.squareup.okhttp.Protocol#HTTP_2 HTTP/2}, this
273    * pushes {@code settings} before writing the response.
274    */
withSettings(Settings settings)275   public MockResponse withSettings(Settings settings) {
276     this.settings = settings;
277     return this;
278   }
279 
getSettings()280   public Settings getSettings() {
281     return settings;
282   }
283 
284   /**
285    * Attempts to perform a web socket upgrade on the connection. This will overwrite any previously
286    * set status or body.
287    */
withWebSocketUpgrade(WebSocketListener listener)288   public MockResponse withWebSocketUpgrade(WebSocketListener listener) {
289     setStatus("HTTP/1.1 101 Switching Protocols");
290     setHeader("Connection", "Upgrade");
291     setHeader("Upgrade", "websocket");
292     body = null;
293     webSocketListener = listener;
294     return this;
295   }
296 
getWebSocketListener()297   public WebSocketListener getWebSocketListener() {
298     return webSocketListener;
299   }
300 
toString()301   @Override public String toString() {
302     return status;
303   }
304 }
305