summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Wilson <jessewilson@google.com>2010-05-10 14:11:57 -0700
committerAndroid (Google) Code Review <android-gerrit@google.com>2010-05-10 14:11:57 -0700
commitd6f5e66e31388c2777da33c30fb2194ff5b427be (patch)
tree9905758b91ad38822451e5468d7e3ee8131c0169
parent259d900b126b15c348fb90b0abddffed292e50ce (diff)
parentb1b5baac449d2725002338735f4db34bec8fd001 (diff)
downloadlibcore-d6f5e66e31388c2777da33c30fb2194ff5b427be.zip
libcore-d6f5e66e31388c2777da33c30fb2194ff5b427be.tar.gz
libcore-d6f5e66e31388c2777da33c30fb2194ff5b427be.tar.bz2
Merge "New MockWebServer for HTTP testing." into dalvik-dev
-rw-r--r--luni/src/test/java/java/net/URLConnectionTest.java183
-rw-r--r--support/src/test/java/tests/http/MockResponse.java117
-rw-r--r--support/src/test/java/tests/http/MockWebServer.java274
-rw-r--r--support/src/test/java/tests/http/RecordedRequest.java85
4 files changed, 531 insertions, 128 deletions
diff --git a/luni/src/test/java/java/net/URLConnectionTest.java b/luni/src/test/java/java/net/URLConnectionTest.java
index d634f6b..18915c4 100644
--- a/luni/src/test/java/java/net/URLConnectionTest.java
+++ b/luni/src/test/java/java/net/URLConnectionTest.java
@@ -16,37 +16,22 @@
package java.net;
+import tests.http.MockResponse;
+import tests.http.MockWebServer;
+import tests.http.RecordedRequest;
+
import java.io.BufferedReader;
import java.io.IOException;
-import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
-import java.util.UUID;
-import java.util.concurrent.atomic.AtomicInteger;
-import tests.support.Support_TestWebServer;
public class URLConnectionTest extends junit.framework.TestCase {
- private int mPort;
- private Support_TestWebServer mServer;
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- mServer = new Support_TestWebServer();
- mPort = mServer.initServer(0, true);
- }
-
- @Override
- public void tearDown() throws Exception {
- super.tearDown();
- mServer.close();
- }
-
- private String readFirstLine() throws Exception {
- URLConnection connection = new URL("http://localhost:" + mPort + "/test1").openConnection();
+
+ private String readFirstLine(MockWebServer server) throws Exception {
+ URLConnection connection = server.getUrl("/").openConnection();
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String result = in.readLine();
in.close();
@@ -57,22 +42,38 @@ public class URLConnectionTest extends junit.framework.TestCase {
// recycled connection doesn't get the unread tail of the first request's response.
// http://code.google.com/p/android/issues/detail?id=2939
public void test_2939() throws Exception {
- mServer.setChunked(true);
- mServer.setMaxChunkSize(8);
- assertTrue(readFirstLine().equals("<html>"));
- assertTrue(readFirstLine().equals("<html>"));
- assertEquals(1, mServer.getNumAcceptedConnections());
+ MockResponse response = new MockResponse().setChunkedBody("ABCDE\nFGHIJ\nKLMNO\nPQR", 8);
+
+ MockWebServer server = new MockWebServer();
+ server.enqueue(response);
+ server.enqueue(response);
+ server.play();
+
+ assertTrue(readFirstLine(server).equals("ABCDE"));
+ assertEquals(0, server.takeRequest().getSequenceNumber());
+ assertTrue(readFirstLine(server).equals("ABCDE"));
+ assertEquals(1, server.takeRequest().getSequenceNumber());
}
public void testConnectionsArePooled() throws Exception {
- readFirstLine();
- readFirstLine();
- readFirstLine();
- assertEquals(1, mServer.getNumAcceptedConnections());
+ MockResponse response = new MockResponse().setBody("ABCDEFGHIJKLMNOPQR");
+
+ MockWebServer server = new MockWebServer();
+ server.enqueue(response);
+ server.enqueue(response);
+ server.enqueue(response);
+ server.play();
+
+ readFirstLine(server);
+ assertEquals(0, server.takeRequest().getSequenceNumber());
+ readFirstLine(server);
+ assertEquals(1, server.takeRequest().getSequenceNumber());
+ readFirstLine(server);
+ assertEquals(2, server.takeRequest().getSequenceNumber());
}
- enum UploadKind { CHUNKED, FIXED_LENGTH };
- enum WriteKind { BYTE_BY_BYTE, SMALL_BUFFERS, LARGE_BUFFERS };
+ enum UploadKind { CHUNKED, FIXED_LENGTH }
+ enum WriteKind { BYTE_BY_BYTE, SMALL_BUFFERS, LARGE_BUFFERS }
public void test_chunkedUpload_byteByByte() throws Exception {
doUpload(UploadKind.CHUNKED, WriteKind.BYTE_BY_BYTE);
@@ -100,10 +101,12 @@ public class URLConnectionTest extends junit.framework.TestCase {
private void doUpload(UploadKind uploadKind, WriteKind writeKind) throws Exception {
int n = 512*1024;
- AtomicInteger total = new AtomicInteger(0);
- ServerSocket ss = startSinkServer(total);
- URL url = new URL("http://localhost:" + ss.getLocalPort() + "/" + UUID.randomUUID());
- HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ MockWebServer server = new MockWebServer();
+ server.setBodyLimit(0);
+ server.enqueue(new MockResponse());
+ server.play();
+
+ HttpURLConnection conn = (HttpURLConnection) server.getUrl("/").openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("POST");
if (uploadKind == UploadKind.CHUNKED) {
@@ -125,66 +128,13 @@ public class URLConnectionTest extends junit.framework.TestCase {
}
out.close();
assertEquals(200, conn.getResponseCode());
- assertEquals(uploadKind == UploadKind.CHUNKED ? -1 : n, total.get());
- }
-
- private ServerSocket startSinkServer(final AtomicInteger totalByteCount) throws Exception {
- final ServerSocket ss = new ServerSocket(0);
- ss.setReuseAddress(true);
- Thread t = new Thread(new Runnable() {
- public void run() {
- try {
- Socket s = ss.accept();
- BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
- int contentLength = -1;
- String line;
- int emptyLineCount = 0;
- // read the headers
- while ((line = in.readLine()) != null) {
- if (contentLength == -1 && line.toLowerCase().startsWith("content-length: ")) {
- contentLength = Integer.parseInt(line.substring(16));
- }
- if (line.isEmpty()) {
- ++emptyLineCount;
- // If we had a content length, the first empty line we see marks the
- // start of the payload. The loop below then skips over that.
- // If we didn't get a content length, we're using chunked encoding.
- // The first empty line again marks the start of the payload, and the
- // second empty line is a consequence of both the last chunk ending
- // CRLF and the chunked-body itself ending with a CRLF. (The fact that
- // a chunk of size 0 is used to mark the end isn't sufficient because
- // there may also be a "trailer": header fields deferred until after
- // the payload.)
- if (contentLength != -1 || emptyLineCount == 2) {
- break;
- }
- }
- }
- // Skip the payload in the setFixedLengthStreamingMode case.
- // In the chunked case, we read all the chunked data in the loop above.
- long left = contentLength;
- while (left > 0) {
- left -= in.skip(left);
- }
- // Send a response to unblock the client.
- totalByteCount.set(contentLength);
- OutputStream out = s.getOutputStream();
- out.write("HTTP/1.1 200 OK\r\n\r\n".getBytes());
- out.flush();
- out.close();
- // Check there wasn't junk at the end.
- try {
- assertEquals(-1, in.read());
- } catch (SocketException expected) {
- // The client already closed the connection.
- }
- } catch (Exception ex) {
- throw new RuntimeException("server died unexpectedly", ex);
- }
- }
- });
- t.start();
- return ss;
+ RecordedRequest request = server.takeRequest();
+ assertEquals(n, request.getBodySize());
+ if (uploadKind == UploadKind.CHUNKED) {
+ assertTrue(request.getChunkSizes().size() > 0);
+ } else {
+ assertTrue(request.getChunkSizes().isEmpty());
+ }
}
public void test_responseCaching() throws Exception {
@@ -239,41 +189,18 @@ public class URLConnectionTest extends junit.framework.TestCase {
didPut = true;
return null;
}
- };
- ServerSocket ss = startResponseCodeServer(responseCode);
- URL url = new URL("http://localhost:" + ss.getLocalPort() + "/");
+ }
+ MockWebServer server = new MockWebServer();
+ server.enqueue(new MockResponse()
+ .setResponseCode(responseCode)
+ .addHeader("WWW-Authenticate: challenge"));
+ server.play();
+
MyResponseCache cache = new MyResponseCache();
ResponseCache.setDefault(cache);
- HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ HttpURLConnection conn = (HttpURLConnection) server.getUrl("/").openConnection();
assertEquals(responseCode, conn.getResponseCode());
assertEquals(Integer.toString(responseCode), shouldPut, cache.didPut);
- }
- private ServerSocket startResponseCodeServer(final int responseCode) throws Exception {
- final ServerSocket ss = new ServerSocket(0);
- ss.setReuseAddress(true);
- Thread t = new Thread(new Runnable() {
- public void run() {
- try {
- Socket s = ss.accept();
- // Read the request.
- BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
- String line;
- while ((line = in.readLine()) != null && !line.isEmpty()) {
- }
- // Send a response.
- OutputStream out = s.getOutputStream();
- out.write(String.format("HTTP/1.1 %d OK\r\n" +
- "Content-Length: 0\r\n" +
- "WWW-Authenticate: challenge\r\n\r\n", responseCode).getBytes());
- out.flush();
- out.close();
- } catch (Exception ex) {
- throw new RuntimeException("server died unexpectedly", ex);
- }
- }
- });
- t.start();
- return ss;
}
}
diff --git a/support/src/test/java/tests/http/MockResponse.java b/support/src/test/java/tests/http/MockResponse.java
new file mode 100644
index 0000000..9893e2f
--- /dev/null
+++ b/support/src/test/java/tests/http/MockResponse.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * 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 tests.http;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.List;
+
+import static tests.http.MockWebServer.ASCII;
+
+/**
+ * A scripted response to be replayed by the mock web server.
+ */
+public class MockResponse {
+ private static final String EMPTY_BODY_HEADER = "Content-Length: 0";
+ private static final String CHUNKED_BODY_HEADER = "Transfer-encoding: chunked";
+ private static final byte[] EMPTY_BODY = new byte[0];
+
+ private String status = "HTTP/1.1 200 OK";
+ private List<String> headers = new ArrayList<String>();
+ private byte[] body = EMPTY_BODY;
+
+ public MockResponse() {
+ headers.add(EMPTY_BODY_HEADER);
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Returns the HTTP headers, such as "Content-Length: 0".
+ */
+ public List<String> getHeaders() {
+ return headers;
+ }
+
+ public MockResponse addHeader(String header) {
+ headers.add(header);
+ return this;
+ }
+
+ /**
+ * Returns an input stream containing the raw HTTP payload.
+ */
+ public byte[] getBody() {
+ return body;
+ }
+
+ public MockResponse setBody(byte[] body) {
+ if (this.body == EMPTY_BODY) {
+ headers.remove(EMPTY_BODY_HEADER);
+ }
+ this.headers.add("Content-Length: " + body.length);
+ this.body = body;
+ return this;
+ }
+
+ 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".getBytes(ASCII));
+ this.body = bytesOut.toByteArray();
+ return this;
+ }
+
+ public MockResponse setChunkedBody(String body, int maxChunkSize) throws IOException {
+ return setChunkedBody(body.getBytes(ASCII), maxChunkSize);
+ }
+
+ @Override public String toString() {
+ return status;
+ }
+}
diff --git a/support/src/test/java/tests/http/MockWebServer.java b/support/src/test/java/tests/http/MockWebServer.java
new file mode 100644
index 0000000..ea7ff84
--- /dev/null
+++ b/support/src/test/java/tests/http/MockWebServer.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * 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 tests.http;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.MalformedURLException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingDeque;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/**
+ * A scriptable web server. Callers supply canned responses and the server
+ * replays them upon request in sequence.
+ */
+public final class MockWebServer {
+
+ static final String ASCII = "US-ASCII";
+
+ /** sentinel object to shut down the server */
+ private static final MockResponse NO_MORE_REQUESTS = new MockResponse();
+
+ private final BlockingQueue<RecordedRequest> requestQueue
+ = new LinkedBlockingQueue<RecordedRequest>();
+ private final BlockingQueue<MockResponse> responseQueue
+ = new LinkedBlockingDeque<MockResponse>();
+ private int bodyLimit = Integer.MAX_VALUE;
+ private final ExecutorService executor = Executors.newCachedThreadPool();
+
+ private int port = -1;
+
+ public int getPort() {
+ if (port == -1) {
+ throw new IllegalStateException("Cannot retrieve port before calling play()");
+ }
+ return port;
+ }
+
+ /**
+ * Returns a URL for connecting to this server.
+ *
+ * @param path the request path, such as "/".
+ */
+ public URL getUrl(String path) throws MalformedURLException {
+ return new URL("http://localhost:" + getPort() + path);
+ }
+
+ /**
+ * Sets the number of bytes of the POST body to keep in memory to the given
+ * limit.
+ */
+ public void setBodyLimit(int maxBodyLength) {
+ this.bodyLimit = maxBodyLength;
+ }
+
+ public void enqueue(MockResponse response) {
+ if (port != -1) {
+ throw new IllegalStateException("Cannot enqueue responses after calling play().");
+ }
+ responseQueue.add(response);
+ }
+
+ /**
+ * Awaits the next HTTP request, removes it, and returns it. Callers should
+ * use this to verify the request sent was as intended.
+ */
+ public RecordedRequest takeRequest() throws InterruptedException {
+ return requestQueue.take();
+ }
+
+ /**
+ * Starts the server, serves all enqueued requests, and shuts the server
+ * down.
+ */
+ public void play() throws IOException {
+ responseQueue.add(NO_MORE_REQUESTS);
+ final ServerSocket ss = new ServerSocket(0);
+ ss.setReuseAddress(true);
+ port = ss.getLocalPort();
+ executor.submit(new Callable<Void>() {
+ public Void call() throws Exception {
+ while (true) {
+ if (responseQueue.peek() == NO_MORE_REQUESTS) {
+ ss.close();
+ executor.shutdown();
+ return null;
+ }
+
+ serveConnection(ss.accept());
+ }
+ }
+ });
+ }
+
+ private void serveConnection(final Socket s) {
+ executor.submit(new Callable<Void>() {
+ public Void call() throws Exception {
+ InputStream in = new BufferedInputStream(s.getInputStream());
+ OutputStream out = new BufferedOutputStream(s.getOutputStream());
+
+ int sequenceNumber = 0;
+ while (true) {
+ RecordedRequest request = readRequest(in, sequenceNumber);
+ if (request == null) {
+ if (sequenceNumber == 0) {
+ throw new IllegalStateException("Connection without any request!");
+ } else {
+ break;
+ }
+ }
+ requestQueue.add(request);
+ writeResponse(out, computeResponse(request));
+ sequenceNumber++;
+ }
+
+ in.close();
+ out.close();
+ return null;
+ }
+ });
+ }
+
+ /**
+ * @param sequenceNumber the index of this request on this connection.
+ */
+ private RecordedRequest readRequest(InputStream in, int sequenceNumber) throws IOException {
+ String request = readAsciiUntilCrlf(in);
+ if (request.isEmpty()) {
+ return null; // end of data; no more requests
+ }
+
+ List<String> headers = new ArrayList<String>();
+ int contentLength = -1;
+ boolean chunked = false;
+ String header;
+ while (!(header = readAsciiUntilCrlf(in)).isEmpty()) {
+ headers.add(header);
+ String lowercaseHeader = header.toLowerCase();
+ if (contentLength == -1 && lowercaseHeader.startsWith("content-length:")) {
+ contentLength = Integer.parseInt(header.substring(15).trim());
+ }
+ if (lowercaseHeader.startsWith("transfer-encoding:") &&
+ lowercaseHeader.substring(18).trim().equals("chunked")) {
+ chunked = true;
+ }
+ }
+
+ TruncatingOutputStream requestBody = new TruncatingOutputStream();
+ List<Integer> chunkSizes = new ArrayList<Integer>();
+ if (contentLength != -1) {
+ transfer(contentLength, in, requestBody);
+ } else if (chunked) {
+ while (true) {
+ int chunkSize = Integer.parseInt(readAsciiUntilCrlf(in).trim(), 16);
+ if (chunkSize == 0) {
+ readEmptyLine(in);
+ break;
+ }
+ chunkSizes.add(chunkSize);
+ transfer(chunkSize, in, requestBody);
+ readEmptyLine(in);
+ }
+ }
+
+ return new RecordedRequest(request, headers, chunkSizes,
+ requestBody.numBytesReceived, requestBody.toByteArray(), sequenceNumber);
+ }
+
+ /**
+ * Returns a response to satisfy {@code request}.
+ */
+ private MockResponse computeResponse(RecordedRequest request) throws InterruptedException {
+ if (responseQueue.peek() == NO_MORE_REQUESTS) {
+ throw new IllegalStateException("Unexpected request: " + request);
+ }
+ return responseQueue.take();
+ }
+
+ private void writeResponse(OutputStream out, MockResponse response) throws IOException {
+ out.write((response.getStatus() + "\r\n").getBytes(ASCII));
+ for (String header : response.getHeaders()) {
+ out.write((header + "\r\n").getBytes(ASCII));
+ }
+ out.write(("\r\n").getBytes(ASCII));
+ out.write(response.getBody());
+ out.write(("\r\n").getBytes(ASCII));
+ out.flush();
+ }
+
+ /**
+ * Transfer bytes from {@code in} to {@code out} until either {@code length}
+ * bytes have been transferred or {@code in} is exhausted.
+ */
+ private void transfer(int length, InputStream in, OutputStream out) throws IOException {
+ byte[] buffer = new byte[1024];
+ while (length > 0) {
+ int count = in.read(buffer, 0, Math.min(buffer.length, length));
+ if (count == -1) {
+ return;
+ }
+ out.write(buffer, 0, count);
+ length -= count;
+ }
+ }
+
+ /**
+ * Returns the text from {@code in} until the next "\r\n", or null if
+ * {@code in} is exhausted.
+ */
+ private String readAsciiUntilCrlf(InputStream in) throws IOException {
+ StringBuilder builder = new StringBuilder();
+ while (true) {
+ int c = in.read();
+ if (c == '\n' && builder.length() > 0 && builder.charAt(builder.length() - 1) == '\r') {
+ builder.deleteCharAt(builder.length() - 1);
+ return builder.toString();
+ } else if (c == -1) {
+ return builder.toString();
+ } else {
+ builder.append((char) c);
+ }
+ }
+ }
+
+ private void readEmptyLine(InputStream in) throws IOException {
+ String line = readAsciiUntilCrlf(in);
+ if (!line.isEmpty()) {
+ throw new IllegalStateException("Expected empty but was: " + line);
+ }
+ }
+
+ /**
+ * An output stream that drops data after bodyLimit bytes.
+ */
+ private class TruncatingOutputStream extends ByteArrayOutputStream {
+ private int numBytesReceived = 0;
+ @Override public void write(byte[] buffer, int offset, int len) {
+ numBytesReceived += len;
+ super.write(buffer, offset, Math.min(len, bodyLimit - count));
+ }
+ @Override public void write(int oneByte) {
+ numBytesReceived++;
+ if (count < bodyLimit) {
+ super.write(oneByte);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/support/src/test/java/tests/http/RecordedRequest.java b/support/src/test/java/tests/http/RecordedRequest.java
new file mode 100644
index 0000000..c805006
--- /dev/null
+++ b/support/src/test/java/tests/http/RecordedRequest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * 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 tests.http;
+
+import java.util.List;
+
+/**
+ * An HTTP request that came into the mock web server.
+ */
+public final class RecordedRequest {
+ private final String requestLine;
+ private final List<String> headers;
+ private final List<Integer> chunkSizes;
+ private final int bodySize;
+ private final byte[] body;
+ private final int sequenceNumber;
+
+ RecordedRequest(String requestLine, List<String> headers, List<Integer> chunkSizes,
+ int bodySize, byte[] body, int sequenceNumber) {
+ this.requestLine = requestLine;
+ this.headers = headers;
+ this.chunkSizes = chunkSizes;
+ this.bodySize = bodySize;
+ this.body = body;
+ this.sequenceNumber = sequenceNumber;
+ }
+
+ public String getRequestLine() {
+ return requestLine;
+ }
+
+ public List<String> getHeaders() {
+ return headers;
+ }
+
+ /**
+ * Returns the sizes of the chunks of this request's body, or an empty list
+ * if the request's body was empty or unchunked.
+ */
+ public List<Integer> getChunkSizes() {
+ return chunkSizes;
+ }
+
+ /**
+ * Returns the total size of the body of this POST request (before
+ * truncation).
+ */
+ public int getBodySize() {
+ return bodySize;
+ }
+
+ /**
+ * Returns the body of this POST request. This may be truncated.
+ */
+ public byte[] getBody() {
+ return body;
+ }
+
+ /**
+ * Returns the index of this request on its HTTP connection. Since a single
+ * HTTP connection may serve multiple requests, each request is assigned its
+ * own sequence number.
+ */
+ public int getSequenceNumber() {
+ return sequenceNumber;
+ }
+
+ @Override public String toString() {
+ return requestLine;
+ }
+}