MockResponse.java revision 40ef0f49ea9fa7c39eb0018fdb4df4b73a11a77d
1/*
2 * Copyright (C) 2010 The Android Open Source Project
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
17package coretestutils.http;
18
19import static coretestutils.http.MockWebServer.ASCII;
20
21import java.io.ByteArrayInputStream;
22import java.io.ByteArrayOutputStream;
23import java.io.File;
24import java.io.FileInputStream;
25import java.io.FileNotFoundException;
26import java.io.InputStream;
27import java.io.IOException;
28import java.io.UnsupportedEncodingException;
29import java.util.ArrayList;
30import java.util.HashMap;
31import java.util.List;
32import java.util.Map;
33
34import android.util.Log;
35
36/**
37 * A scripted response to be replayed by the mock web server.
38 */
39public class MockResponse {
40    private static final byte[] EMPTY_BODY = new byte[0];
41    static final String LOG_TAG = "coretestutils.http.MockResponse";
42
43    private String status = "HTTP/1.1 200 OK";
44    private Map<String, String> headers = new HashMap<String, String>();
45    private byte[] body = EMPTY_BODY;
46    private boolean closeConnectionAfter = false;
47    private String closeConnectionAfterHeader = null;
48    private int closeConnectionAfterXBytes = -1;
49    private int pauseConnectionAfterXBytes = -1;
50    private File bodyExternalFile = null;
51
52    public MockResponse() {
53        addHeader("Content-Length", 0);
54    }
55
56    /**
57     * Returns the HTTP response line, such as "HTTP/1.1 200 OK".
58     */
59    public String getStatus() {
60        return status;
61    }
62
63    public MockResponse setResponseCode(int code) {
64        this.status = "HTTP/1.1 " + code + " OK";
65        return this;
66    }
67
68    /**
69     * Returns the HTTP headers, such as "Content-Length: 0".
70     */
71    public List<String> getHeaders() {
72        List<String> headerStrings = new ArrayList<String>();
73        for (String header : headers.keySet()) {
74            headerStrings.add(header + ": " + headers.get(header));
75        }
76        return headerStrings;
77    }
78
79    public MockResponse addHeader(String header, String value) {
80        headers.put(header.toLowerCase(), value);
81        return this;
82    }
83
84    public MockResponse addHeader(String header, long value) {
85        return addHeader(header, Long.toString(value));
86    }
87
88    public MockResponse removeHeader(String header) {
89        headers.remove(header.toLowerCase());
90        return this;
91    }
92
93    /**
94     * Returns true if the body should come from an external file, false otherwise.
95     */
96    private boolean bodyIsExternal() {
97        return bodyExternalFile != null;
98    }
99
100    /**
101     * Returns an input stream containing the raw HTTP payload.
102     */
103    public InputStream getBody() {
104        if (bodyIsExternal()) {
105            try {
106                return new FileInputStream(bodyExternalFile);
107            } catch (FileNotFoundException e) {
108                Log.e(LOG_TAG, "File not found: " + bodyExternalFile.getAbsolutePath());
109            }
110        }
111        return new ByteArrayInputStream(this.body);
112    }
113
114    public MockResponse setBody(File body) {
115        addHeader("Content-Length", body.length());
116        this.bodyExternalFile = body;
117        return this;
118    }
119
120    public MockResponse setBody(byte[] body) {
121        addHeader("Content-Length", body.length);
122        this.body = body;
123        return this;
124    }
125
126    public MockResponse setBody(String body) {
127        try {
128            return setBody(body.getBytes(ASCII));
129        } catch (UnsupportedEncodingException e) {
130            throw new AssertionError();
131        }
132    }
133
134    /**
135     * Sets the body as chunked.
136     *
137     * Currently chunked body is not supported for external files as bodies.
138     */
139    public MockResponse setChunkedBody(byte[] body, int maxChunkSize) throws IOException {
140        addHeader("Transfer-encoding", "chunked");
141
142        ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
143        int pos = 0;
144        while (pos < body.length) {
145            int chunkSize = Math.min(body.length - pos, maxChunkSize);
146            bytesOut.write(Integer.toHexString(chunkSize).getBytes(ASCII));
147            bytesOut.write("\r\n".getBytes(ASCII));
148            bytesOut.write(body, pos, chunkSize);
149            bytesOut.write("\r\n".getBytes(ASCII));
150            pos += chunkSize;
151        }
152        bytesOut.write("0\r\n".getBytes(ASCII));
153        this.body = bytesOut.toByteArray();
154        return this;
155    }
156
157    public MockResponse setChunkedBody(String body, int maxChunkSize) throws IOException {
158        return setChunkedBody(body.getBytes(ASCII), maxChunkSize);
159    }
160
161    @Override public String toString() {
162        return status;
163    }
164
165    public boolean shouldCloseConnectionAfter() {
166        return closeConnectionAfter;
167    }
168
169    public MockResponse setCloseConnectionAfter(boolean closeConnectionAfter) {
170        this.closeConnectionAfter = closeConnectionAfter;
171        return this;
172    }
173
174    /**
175     * Sets the header after which sending the server should close the connection.
176     */
177    public MockResponse setCloseConnectionAfterHeader(String header) {
178        closeConnectionAfterHeader = header;
179        setCloseConnectionAfter(true);
180        return this;
181    }
182
183    /**
184     * Returns the header after which sending the server should close the connection.
185     */
186    public String getCloseConnectionAfterHeader() {
187        return closeConnectionAfterHeader;
188    }
189
190    /**
191     * Sets the number of bytes in the body to send before which the server should close the
192     * connection. Set to -1 to unset and send the entire body (default).
193     */
194    public MockResponse setCloseConnectionAfterXBytes(int position) {
195        closeConnectionAfterXBytes = position;
196        setCloseConnectionAfter(true);
197        return this;
198    }
199
200    /**
201     * Returns the number of bytes in the body to send before which the server should close the
202     * connection. Returns -1 if the entire body should be sent (default).
203     */
204    public int getCloseConnectionAfterXBytes() {
205        return closeConnectionAfterXBytes;
206    }
207
208    /**
209     * Sets the number of bytes in the body to send before which the server should pause the
210     * connection (stalls in sending data). Only one pause per response is supported.
211     * Set to -1 to unset pausing (default).
212     */
213    public MockResponse setPauseConnectionAfterXBytes(int position) {
214        pauseConnectionAfterXBytes = position;
215        return this;
216    }
217
218    /**
219     * Returns the number of bytes in the body to send before which the server should pause the
220     * connection (stalls in sending data). (Returns -1 if it should not pause).
221     */
222    public int getPauseConnectionAfterXBytes() {
223        return pauseConnectionAfterXBytes;
224    }
225
226    /**
227     * Returns true if this response is flagged to pause the connection mid-stream, false otherwise
228     */
229    public boolean getShouldPause() {
230        return (pauseConnectionAfterXBytes != -1);
231    }
232
233    /**
234     * Returns true if this response is flagged to close the connection mid-stream, false otherwise
235     */
236    public boolean getShouldClose() {
237        return (closeConnectionAfterXBytes != -1);
238    }
239}
240