1a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath/*
2a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath * Copyright (C) 2013 Square, Inc.
3a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath *
4a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath * Licensed under the Apache License, Version 2.0 (the "License");
5a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath * you may not use this file except in compliance with the License.
6a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath * You may obtain a copy of the License at
7a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath *
8a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath *      http://www.apache.org/licenses/LICENSE-2.0
9a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath *
10a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath * Unless required by applicable law or agreed to in writing, software
11a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath * distributed under the License is distributed on an "AS IS" BASIS,
12a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath * See the License for the specific language governing permissions and
14a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath * limitations under the License.
15a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath */
16166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathpackage com.squareup.okhttp;
17a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.io.ByteArrayOutputStream;
19a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamathimport java.io.IOException;
203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.net.URL;
21a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamathimport java.util.ArrayList;
22c6bd683320121544811f481709b3fdbcbe9b3866Neil Fullerimport java.util.Iterator;
233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.LinkedHashMap;
24a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamathimport java.util.List;
253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.Map;
26a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamathimport java.util.concurrent.TimeUnit;
27a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
28a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath/**
29a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath * Records received HTTP responses so they can be later retrieved by tests.
30a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath */
31a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamathpublic class RecordingReceiver implements Response.Receiver {
32a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath  public static final long TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(10);
33a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private final Map<Response, ByteArrayOutputStream> inFlightResponses
353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      = new LinkedHashMap<Response, ByteArrayOutputStream>();
36a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath  private final List<RecordedResponse> responses = new ArrayList<RecordedResponse>();
37a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
38a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath  @Override public synchronized void onFailure(Failure failure) {
39a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    responses.add(new RecordedResponse(failure.request(), null, null, failure));
40a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    notifyAll();
41a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath  }
42a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Override public synchronized boolean onResponse(Response response) throws IOException {
443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    ByteArrayOutputStream out = inFlightResponses.get(response);
453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (out == null) {
463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      out = new ByteArrayOutputStream();
473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      inFlightResponses.put(response, out);
483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    byte[] buffer = new byte[1024];
513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Response.Body body = response.body();
523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    while (body.ready()) {
543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      int c = body.byteStream().read(buffer);
553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (c == -1) {
573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        inFlightResponses.remove(response);
583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        responses.add(new RecordedResponse(
593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            response.request(), response, out.toString("UTF-8"), null));
603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        notifyAll();
613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        return true;
623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      out.write(buffer, 0, c);
653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return false;
68a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath  }
69a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
70a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath  /**
71a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath   * Returns the recorded response triggered by {@code request}. Throws if the
72a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath   * response isn't enqueued before the timeout.
73a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath   */
743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public synchronized RecordedResponse await(URL url) throws Exception {
75a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    long timeoutMillis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime()) + TIMEOUT_MILLIS;
76a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    while (true) {
77c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller      for (Iterator<RecordedResponse> i = responses.iterator(); i.hasNext(); ) {
78c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller        RecordedResponse recordedResponse = i.next();
793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        if (recordedResponse.request.url().equals(url)) {
80c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller          i.remove();
81a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath          return recordedResponse;
82a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath        }
83a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath      }
84a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
85a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath      long nowMillis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
86a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath      if (nowMillis >= timeoutMillis) break;
87a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath      wait(timeoutMillis - nowMillis);
88a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    }
89a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    throw new AssertionError("Timed out waiting for response to " + url);
913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public synchronized void assertNoResponse(URL url) throws Exception {
943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    for (RecordedResponse recordedResponse : responses) {
953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (recordedResponse.request.url().equals(url)) {
963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        throw new AssertionError("Expected no response for " + url);
973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
99a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath  }
100a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath}
101