• 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 
17 package com.google.mockwebserver;
18 
19 import java.io.ByteArrayInputStream;
20 import java.io.ByteArrayOutputStream;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.UnsupportedEncodingException;
24 import java.util.ArrayList;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.concurrent.TimeUnit;
28 
29 import static java.nio.charset.StandardCharsets.US_ASCII;
30 
31 /**
32  * A scripted response to be replayed by the mock web server.
33  */
34 public final class MockResponse implements Cloneable {
35     private static final String CHUNKED_BODY_HEADER = "Transfer-encoding: chunked";
36 
37     private String status = "HTTP/1.1 200 OK";
38     private List<String> headers = new ArrayList<String>();
39 
40     /** The response body content, or null if {@code bodyStream} is set. */
41     private byte[] body;
42     /** The response body content, or null if {@code body} is set. */
43     private InputStream bodyStream;
44 
45     private int throttleBytesPerPeriod = Integer.MAX_VALUE;
46     private long throttlePeriod = 1;
47     private TimeUnit throttleUnit = TimeUnit.SECONDS;
48 
49     private SocketPolicy socketPolicy = SocketPolicy.KEEP_OPEN;
50 
51     private int bodyDelayTimeMs = 0;
52 
53     /**
54      * Creates a new mock response with an empty body.
55      */
MockResponse()56     public MockResponse() {
57         setBody(new byte[0]);
58     }
59 
clone()60     @Override public MockResponse clone() {
61         try {
62             MockResponse result = (MockResponse) super.clone();
63             result.headers = new ArrayList<String>(result.headers);
64             return result;
65         } catch (CloneNotSupportedException e) {
66             throw new AssertionError();
67         }
68     }
69 
70     /**
71      * Returns the HTTP response line, such as "HTTP/1.1 200 OK".
72      */
getStatus()73     public String getStatus() {
74         return status;
75     }
76 
setResponseCode(int code)77     public MockResponse setResponseCode(int code) {
78         this.status = "HTTP/1.1 " + code + " OK";
79         return this;
80     }
81 
setStatus(String status)82     public MockResponse setStatus(String status) {
83         this.status = status;
84         return this;
85     }
86 
87     /**
88      * Returns the HTTP headers, such as "Content-Length: 0".
89      */
getHeaders()90     public List<String> getHeaders() {
91         return headers;
92     }
93 
94     /**
95      * Removes all HTTP headers including any "Content-Length" and
96      * "Transfer-encoding" headers that were added by default.
97      */
clearHeaders()98     public MockResponse clearHeaders() {
99         headers.clear();
100         return this;
101     }
102 
103     /**
104      * Adds {@code header} as an HTTP header. For well-formed HTTP {@code
105      * header} should contain a name followed by a colon and a value.
106      */
addHeader(String header)107     public MockResponse addHeader(String header) {
108         headers.add(header);
109         return this;
110     }
111 
112     /**
113      * Adds a new header with the name and value. This may be used to add
114      * multiple headers with the same name.
115      */
addHeader(String name, Object value)116     public MockResponse addHeader(String name, Object value) {
117         return addHeader(name + ": " + String.valueOf(value));
118     }
119 
120     /**
121      * Removes all headers named {@code name}, then adds a new header with the
122      * name and value.
123      */
setHeader(String name, Object value)124     public MockResponse setHeader(String name, Object value) {
125         removeHeader(name);
126         return addHeader(name, value);
127     }
128 
129     /**
130      * Removes all headers named {@code name}.
131      */
removeHeader(String name)132     public MockResponse removeHeader(String name) {
133         name += ":";
134         for (Iterator<String> i = headers.iterator(); i.hasNext(); ) {
135             String header = i.next();
136             if (name.regionMatches(true, 0, header, 0, name.length())) {
137                 i.remove();
138             }
139         }
140         return this;
141     }
142 
143     /**
144      * Returns the raw HTTP payload, or null if this response is streamed.
145      */
getBody()146     public byte[] getBody() {
147         return body;
148     }
149 
150     /**
151      * Returns an input stream containing the raw HTTP payload.
152      */
getBodyStream()153     InputStream getBodyStream() {
154         return bodyStream != null ? bodyStream : new ByteArrayInputStream(body);
155     }
156 
setBody(byte[] body)157     public MockResponse setBody(byte[] body) {
158         setHeader("Content-Length", body.length);
159         this.body = body;
160         this.bodyStream = null;
161         return this;
162     }
163 
setBody(InputStream bodyStream, long bodyLength)164     public MockResponse setBody(InputStream bodyStream, long bodyLength) {
165         setHeader("Content-Length", bodyLength);
166         this.body = null;
167         this.bodyStream = bodyStream;
168         return this;
169     }
170 
171     /**
172      * Sets the response body to the UTF-8 encoded bytes of {@code body}.
173      */
setBody(String body)174     public MockResponse setBody(String body) {
175         try {
176             return setBody(body.getBytes("UTF-8"));
177         } catch (UnsupportedEncodingException e) {
178             throw new AssertionError();
179         }
180     }
181 
182     /**
183      * Sets the response body to {@code body}, chunked every {@code
184      * maxChunkSize} bytes.
185      */
setChunkedBody(byte[] body, int maxChunkSize)186     public MockResponse setChunkedBody(byte[] body, int maxChunkSize) {
187         removeHeader("Content-Length");
188         headers.add(CHUNKED_BODY_HEADER);
189 
190         try {
191             ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
192             int pos = 0;
193             while (pos < body.length) {
194                 int chunkSize = Math.min(body.length - pos, maxChunkSize);
195                 bytesOut.write(Integer.toHexString(chunkSize).getBytes(US_ASCII));
196                 bytesOut.write("\r\n".getBytes(US_ASCII));
197                 bytesOut.write(body, pos, chunkSize);
198                 bytesOut.write("\r\n".getBytes(US_ASCII));
199                 pos += chunkSize;
200             }
201             bytesOut.write("0\r\n\r\n".getBytes(US_ASCII)); // last chunk + empty trailer + crlf
202 
203             this.body = bytesOut.toByteArray();
204             return this;
205         } catch (IOException e) {
206             throw new AssertionError(); // In-memory I/O doesn't throw IOExceptions.
207         }
208     }
209 
210     /**
211      * Sets the response body to the UTF-8 encoded bytes of {@code body},
212      * chunked every {@code maxChunkSize} bytes.
213      */
setChunkedBody(String body, int maxChunkSize)214     public MockResponse setChunkedBody(String body, int maxChunkSize) {
215         try {
216             return setChunkedBody(body.getBytes("UTF-8"), maxChunkSize);
217         } catch (UnsupportedEncodingException e) {
218             throw new AssertionError();
219         }
220     }
221 
getSocketPolicy()222     public SocketPolicy getSocketPolicy() {
223         return socketPolicy;
224     }
225 
setSocketPolicy(SocketPolicy socketPolicy)226     public MockResponse setSocketPolicy(SocketPolicy socketPolicy) {
227         this.socketPolicy = socketPolicy;
228         return this;
229     }
230 
231     /**
232      * Throttles the response body writer to sleep for the given period after each
233      * series of {@code bytesPerPeriod} bytes are written. Use this to simulate
234      * network behavior.
235      */
throttleBody(int bytesPerPeriod, long period, TimeUnit unit)236     public MockResponse throttleBody(int bytesPerPeriod, long period, TimeUnit unit) {
237         this.throttleBytesPerPeriod = bytesPerPeriod;
238         this.throttlePeriod = period;
239         this.throttleUnit = unit;
240         return this;
241     }
242 
getThrottleBytesPerPeriod()243     public int getThrottleBytesPerPeriod() {
244         return throttleBytesPerPeriod;
245     }
246 
getThrottlePeriod()247     public long getThrottlePeriod() {
248         return throttlePeriod;
249     }
250 
getThrottleUnit()251     public TimeUnit getThrottleUnit() {
252         return throttleUnit;
253     }
254 
255     /**
256      * Set the delayed time of the response body to {@code delay}. This applies to the
257      * response body only; response headers are not affected.
258      */
setBodyDelayTimeMs(int delay)259     public MockResponse setBodyDelayTimeMs(int delay) {
260         bodyDelayTimeMs = delay;
261         return this;
262     }
263 
getBodyDelayTimeMs()264     public int getBodyDelayTimeMs() {
265         return bodyDelayTimeMs;
266     }
267 
toString()268     @Override public String toString() {
269         return "MockResponse{" + status + "}";
270     }
271 }
272