| /* |
| * Copyright (C) 2011 Google 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 com.google.mockwebserver; |
| |
| import static com.google.mockwebserver.MockWebServer.ASCII; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.UnsupportedEncodingException; |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| /** |
| * A scripted response to be replayed by the mock web server. |
| */ |
| public final class MockResponse implements Cloneable { |
| private static final String EMPTY_BODY_HEADER = "Content-Length: 0"; |
| private static final String CHUNKED_BODY_HEADER = "Transfer-encoding: chunked"; |
| |
| private String status = "HTTP/1.1 200 OK"; |
| private List<String> headers = new ArrayList<String>(); |
| private InputStream body; |
| private long bodyLength; |
| private int bytesPerSecond = Integer.MAX_VALUE; |
| private SocketPolicy socketPolicy = SocketPolicy.KEEP_OPEN; |
| |
| public MockResponse() { |
| headers.add(EMPTY_BODY_HEADER); |
| } |
| |
| @Override public MockResponse clone() { |
| try { |
| MockResponse result = (MockResponse) super.clone(); |
| result.headers = new ArrayList<String>(result.headers); |
| return result; |
| } catch (CloneNotSupportedException e) { |
| throw new AssertionError(); |
| } |
| } |
| |
| /** |
| * Returns the HTTP response line, such as "HTTP/1.1 200 OK". |
| */ |
| public String getStatus() { |
| return status; |
| } |
| |
| public MockResponse setResponseCode(int code) { |
| this.status = "HTTP/1.1 " + code + " OK"; |
| return this; |
| } |
| |
| public MockResponse setStatus(String status) { |
| this.status = status; |
| return this; |
| } |
| |
| /** |
| * Returns the HTTP headers, such as "Content-Length: 0". |
| */ |
| public List<String> getHeaders() { |
| return headers; |
| } |
| |
| public MockResponse clearHeaders() { |
| headers.clear(); |
| return this; |
| } |
| |
| public MockResponse addHeader(String header) { |
| headers.add(header); |
| return this; |
| } |
| |
| public MockResponse addHeader(String name, Object value) { |
| return addHeader(name + ": " + String.valueOf(value)); |
| } |
| |
| public MockResponse setHeader(String name, Object value) { |
| removeHeader(name); |
| return addHeader(name, value); |
| } |
| |
| public MockResponse removeHeader(String name) { |
| name += ": "; |
| for (Iterator<String> i = headers.iterator(); i.hasNext();) { |
| String header = i.next(); |
| if (name.regionMatches(true, 0, header, 0, name.length())) { |
| i.remove(); |
| } |
| } |
| return this; |
| } |
| |
| /** |
| * Returns a {@code byte[]} containing the raw HTTP payload. This is less |
| * efficient than {@link #getBodyStream()}. |
| */ |
| public byte[] getBody() { |
| try { |
| return readFullyNoClose(body); |
| } catch (IOException e) { |
| throw new RuntimeException(e); |
| } |
| } |
| |
| /** |
| * Returns an input stream containing the raw HTTP payload. |
| */ |
| public InputStream getBodyStream() { |
| return body; |
| } |
| |
| /** |
| * Returns length of raw HTTP payload. |
| */ |
| public long getBodyLength() { |
| return bodyLength; |
| } |
| |
| public MockResponse setBody(InputStream body, long bodyLength) { |
| if (this.body == null) { |
| headers.remove(EMPTY_BODY_HEADER); |
| } |
| this.headers.add("Content-Length: " + bodyLength); |
| this.body = body; |
| this.bodyLength = bodyLength; |
| return this; |
| } |
| |
| public MockResponse setBody(byte[] body) { |
| return setBody(new ByteArrayInputStream(body), body.length); |
| } |
| |
| public MockResponse setBody(String body) { |
| try { |
| return setBody(body.getBytes(ASCII)); |
| } catch (UnsupportedEncodingException e) { |
| throw new AssertionError(); |
| } |
| } |
| |
| public MockResponse setChunkedBody(byte[] body, int maxChunkSize) throws IOException { |
| headers.remove(EMPTY_BODY_HEADER); |
| headers.add(CHUNKED_BODY_HEADER); |
| |
| ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); |
| int pos = 0; |
| while (pos < body.length) { |
| int chunkSize = Math.min(body.length - pos, maxChunkSize); |
| bytesOut.write(Integer.toHexString(chunkSize).getBytes(ASCII)); |
| bytesOut.write("\r\n".getBytes(ASCII)); |
| bytesOut.write(body, pos, chunkSize); |
| bytesOut.write("\r\n".getBytes(ASCII)); |
| pos += chunkSize; |
| } |
| bytesOut.write("0\r\n\r\n".getBytes(ASCII)); // last chunk + empty trailer + crlf |
| |
| body = bytesOut.toByteArray(); |
| this.body = new ByteArrayInputStream(body); |
| this.bodyLength = body.length; |
| return this; |
| } |
| |
| public MockResponse setChunkedBody(String body, int maxChunkSize) throws IOException { |
| return setChunkedBody(body.getBytes(ASCII), maxChunkSize); |
| } |
| |
| public SocketPolicy getSocketPolicy() { |
| return socketPolicy; |
| } |
| |
| public MockResponse setSocketPolicy(SocketPolicy socketPolicy) { |
| this.socketPolicy = socketPolicy; |
| return this; |
| } |
| |
| public int getBytesPerSecond() { |
| return bytesPerSecond; |
| } |
| |
| /** |
| * Set simulated network speed, in bytes per second. |
| */ |
| public MockResponse setBytesPerSecond(int bytesPerSecond) { |
| this.bytesPerSecond = bytesPerSecond; |
| return this; |
| } |
| |
| @Override public String toString() { |
| return status; |
| } |
| |
| private static byte[] readFullyNoClose(InputStream in) throws IOException { |
| ByteArrayOutputStream bytes = new ByteArrayOutputStream(); |
| byte[] buffer = new byte[1024]; |
| int count; |
| while ((count = in.read(buffer)) != -1) { |
| bytes.write(buffer, 0, count); |
| } |
| return bytes.toByteArray(); |
| } |
| } |