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 17 package tests.http; 18 19 import java.io.ByteArrayOutputStream; 20 import java.io.IOException; 21 import java.io.UnsupportedEncodingException; 22 import java.util.ArrayList; 23 import java.util.List; 24 import static tests.http.MockWebServer.ASCII; 25 26 /** 27 * A scripted response to be replayed by the mock web server. 28 */ 29 public class MockResponse implements Cloneable { 30 private static final String RFC_1123_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz"; 31 private static final String EMPTY_BODY_HEADER = "Content-Length: 0"; 32 private static final String CHUNKED_BODY_HEADER = "Transfer-encoding: chunked"; 33 private static final byte[] EMPTY_BODY = new byte[0]; 34 35 private String status = "HTTP/1.1 200 OK"; 36 private List<String> headers = new ArrayList<String>(); 37 private byte[] body = EMPTY_BODY; 38 private SocketPolicy socketPolicy = SocketPolicy.KEEP_OPEN; 39 40 public MockResponse() { 41 headers.add(EMPTY_BODY_HEADER); 42 } 43 44 @Override public MockResponse clone() { 45 try { 46 MockResponse result = (MockResponse) super.clone(); 47 result.headers = new ArrayList<String>(result.headers); 48 return result; 49 } catch (CloneNotSupportedException e) { 50 throw new AssertionError(); 51 } 52 } 53 54 /** 55 * Returns the HTTP response line, such as "HTTP/1.1 200 OK". 56 */ 57 public String getStatus() { 58 return status; 59 } 60 61 public MockResponse setResponseCode(int code) { 62 this.status = "HTTP/1.1 " + code + " OK"; 63 return this; 64 } 65 66 public MockResponse setStatus(String status) { 67 this.status = status; 68 return this; 69 } 70 71 /** 72 * Returns the HTTP headers, such as "Content-Length: 0". 73 */ 74 public List<String> getHeaders() { 75 return headers; 76 } 77 78 public MockResponse clearHeaders() { 79 headers.clear(); 80 return this; 81 } 82 83 public MockResponse addHeader(String header) { 84 headers.add(header); 85 return this; 86 } 87 88 /** 89 * Returns an input stream containing the raw HTTP payload. 90 */ 91 public byte[] getBody() { 92 return body; 93 } 94 95 public MockResponse setBody(byte[] body) { 96 if (this.body == EMPTY_BODY) { 97 headers.remove(EMPTY_BODY_HEADER); 98 } 99 this.headers.add("Content-Length: " + body.length); 100 this.body = body; 101 return this; 102 } 103 104 public MockResponse setBody(String body) { 105 try { 106 return setBody(body.getBytes(ASCII)); 107 } catch (UnsupportedEncodingException e) { 108 throw new AssertionError(); 109 } 110 } 111 112 public MockResponse setChunkedBody(byte[] body, int maxChunkSize) throws IOException { 113 headers.remove(EMPTY_BODY_HEADER); 114 headers.add(CHUNKED_BODY_HEADER); 115 116 ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); 117 int pos = 0; 118 while (pos < body.length) { 119 int chunkSize = Math.min(body.length - pos, maxChunkSize); 120 bytesOut.write(Integer.toHexString(chunkSize).getBytes(ASCII)); 121 bytesOut.write("\r\n".getBytes(ASCII)); 122 bytesOut.write(body, pos, chunkSize); 123 bytesOut.write("\r\n".getBytes(ASCII)); 124 pos += chunkSize; 125 } 126 bytesOut.write("0\r\n\r\n".getBytes(ASCII)); // last chunk + empty trailer + crlf 127 this.body = bytesOut.toByteArray(); 128 return this; 129 } 130 131 public MockResponse setChunkedBody(String body, int maxChunkSize) throws IOException { 132 return setChunkedBody(body.getBytes(ASCII), maxChunkSize); 133 } 134 135 public SocketPolicy getSocketPolicy() { 136 return socketPolicy; 137 } 138 139 public MockResponse setSocketPolicy(SocketPolicy socketPolicy) { 140 this.socketPolicy = socketPolicy; 141 return this; 142 } 143 144 @Override public String toString() { 145 return status; 146 } 147 } 148