// Copyright 2012 Google Inc. All Rights Reserved. // // 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.api.ads.common.lib.testing; import com.google.api.client.http.HttpTransport; import com.google.api.client.http.LowLevelHttpRequest; import com.google.api.client.http.LowLevelHttpResponse; import com.google.api.client.testing.http.HttpTesting; import com.google.api.client.testing.http.MockHttpTransport; import com.google.api.client.testing.http.MockLowLevelHttpRequest; import com.google.api.client.testing.http.MockLowLevelHttpResponse; import com.google.common.io.ByteSource; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.ConnectException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.Deque; import java.util.LinkedList; import java.util.List; import java.util.zip.GZIPInputStream; /** * HTTP server used to verify requests and send mocked responses. Unlike {@link TestHttpServer}, * this server does not actually open a port and listen for requests. Instead, it uses a mock * {@link HttpTransport}. */ public class MockHttpServer { private final HttpTransport httpTransport; private final Deque<ActualResponse> actualResponses = new LinkedList<>(); private final Deque<MockResponse> mockResponses = new LinkedList<>(); public MockHttpServer() { this.httpTransport = new MockHttpTransport() { @Override public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { return new MockLowLevelHttpRequest(url) { @Override public LowLevelHttpResponse execute() throws IOException { return MockHttpServer.this.execute(this); } }; } }; } private LowLevelHttpResponse execute(MockLowLevelHttpRequest request) throws IOException { ActualResponse actualResponse = new ActualResponse(request.getHeaders()); actualResponses.add(actualResponse); MockResponse mockResponse; if (mockResponses.isEmpty()) { mockResponse = new MockResponse("No mock response body set", 500); } else { mockResponse = mockResponses.pop(); } // Read the raw bytes from the request. ByteArrayOutputStream byteOutStream = new ByteArrayOutputStream(); request.getStreamingContent().writeTo(byteOutStream); final byte[] rawRequestBytes = byteOutStream.toByteArray(); // Inflate the raw bytes if they are in gzip format. boolean isGzipFormat = false; List<String> contentEncodingValues = request.getHeaders().get("Content-Encoding"); if (contentEncodingValues != null && !contentEncodingValues.isEmpty()) { isGzipFormat = "gzip".equals(contentEncodingValues.get(0)); } byte[] requestBytes; if (isGzipFormat) { requestBytes = new ByteSource() { @Override public InputStream openStream() throws IOException { return new GZIPInputStream(ByteSource.wrap(rawRequestBytes).openStream()); } }.read(); } else { requestBytes = rawRequestBytes; } // Convert the (possibly inflated) request bytes to a string. String requestBody = ByteSource.wrap(requestBytes).asCharSource(StandardCharsets.UTF_8).read(); actualResponse.setRequestBody(requestBody); if (mockResponse.isValidateUrlMatches() && !getServerUrl().equals(request.getUrl())) { throw new ConnectException( String.format( "Request URL does not match.%n Expected: %s%n Actual: %s%n", getServerUrl(), request.getUrl())); } MockLowLevelHttpResponse response = new MockLowLevelHttpResponse(); // Add the Location response header, which is required by some tests such as // BatchJobUploaderTest. response.addHeader("Location", getServerUrl()); response.setStatusCode(mockResponse.getHttpStatus()); response.setContentType(mockResponse.getContentType()); response.setContent(mockResponse.getBody()); return response; } public void setMockResponse(MockResponse mockResponse) { this.mockResponses.add(mockResponse); } public void setMockResponses(Collection<MockResponse> mockResponses) { this.mockResponses.addAll(mockResponses); } public ActualResponse getLastResponse() { return this.actualResponses.getLast(); } public List<ActualResponse> getAllResponses() { return new ArrayList<>(this.actualResponses); } /** * Gets the server URL with port. */ public String getServerUrl() { return HttpTesting.SIMPLE_URL; } /** * Gets the (mock) {@link HttpTransport}. */ public HttpTransport getHttpTransport() { return httpTransport; } }