/* * Copyright 2002-2017 the original author or authors. * * 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 org.springframework.web.client; import java.io.EOFException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Collections; import okhttp3.mockwebserver.Dispatcher; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import okhttp3.mockwebserver.RecordedRequest; import okio.Buffer; import org.hamcrest.Matchers; import org.junit.After; import org.junit.Before; import org.springframework.http.MediaType; import static org.junit.Assert.*; /** * @author Brian Clozel */ public class AbstractMockWebServerTestCase { protected static final MediaType textContentType = new MediaType("text", "plain", Collections.singletonMap("charset", "UTF-8")); protected static final String helloWorld = "H\u00e9llo W\u00f6rld"; private MockWebServer server; protected int port; protected String baseUrl; @Before public void setUp() throws Exception { this.server = new MockWebServer(); this.server.setDispatcher(new TestDispatcher()); this.server.start(); this.port = this.server.getPort(); this.baseUrl = "http://localhost:" + this.port; } @After public void tearDown() throws Exception { this.server.shutdown(); } private MockResponse getRequest(RecordedRequest request, byte[] body, String contentType) { if (request.getMethod().equals("OPTIONS")) { return new MockResponse().setResponseCode(200).setHeader("Allow", "GET, OPTIONS, HEAD, TRACE"); } Buffer buf = new Buffer(); buf.write(body); MockResponse response = new MockResponse() .setHeader("Content-Length", body.length) .setBody(buf) .setResponseCode(200); if (contentType != null) { response = response.setHeader("Content-Type", contentType); } return response; } private MockResponse postRequest(RecordedRequest request, String expectedRequestContent, String location, String contentType, byte[] responseBody) { assertEquals(1, request.getHeaders().values("Content-Length").size()); assertTrue("Invalid request content-length", Integer.parseInt(request.getHeader("Content-Length")) > 0); String requestContentType = request.getHeader("Content-Type"); assertNotNull("No content-type", requestContentType); Charset charset = StandardCharsets.ISO_8859_1; if (requestContentType.contains("charset=")) { String charsetName = requestContentType.split("charset=")[1]; charset = Charset.forName(charsetName); } assertEquals("Invalid request body", expectedRequestContent, request.getBody().readString(charset)); Buffer buf = new Buffer(); buf.write(responseBody); return new MockResponse() .setHeader("Location", baseUrl + location) .setHeader("Content-Type", contentType) .setHeader("Content-Length", responseBody.length) .setBody(buf) .setResponseCode(201); } private MockResponse jsonPostRequest(RecordedRequest request, String location, String contentType) { if (request.getBodySize() > 0) { assertTrue("Invalid request content-length", Integer.parseInt(request.getHeader("Content-Length")) > 0); assertNotNull("No content-type", request.getHeader("Content-Type")); } return new MockResponse() .setHeader("Location", baseUrl + location) .setHeader("Content-Type", contentType) .setHeader("Content-Length", request.getBody().size()) .setBody(request.getBody()) .setResponseCode(201); } private MockResponse multipartRequest(RecordedRequest request) { MediaType mediaType = MediaType.parseMediaType(request.getHeader("Content-Type")); assertTrue(mediaType.isCompatibleWith(MediaType.MULTIPART_FORM_DATA)); String boundary = mediaType.getParameter("boundary"); Buffer body = request.getBody(); try { assertPart(body, "form-data", boundary, "name 1", "text/plain", "value 1"); assertPart(body, "form-data", boundary, "name 2", "text/plain", "value 2+1"); assertPart(body, "form-data", boundary, "name 2", "text/plain", "value 2+2"); assertFilePart(body, "form-data", boundary, "logo", "logo.jpg", "image/jpeg"); } catch (EOFException ex) { throw new IllegalStateException(ex); } return new MockResponse().setResponseCode(200); } private void assertPart(Buffer buffer, String disposition, String boundary, String name, String contentType, String value) throws EOFException { assertTrue(buffer.readUtf8Line().contains("--" + boundary)); String line = buffer.readUtf8Line(); assertTrue(line.contains("Content-Disposition: "+ disposition)); assertTrue(line.contains("name=\""+ name + "\"")); assertTrue(buffer.readUtf8Line().startsWith("Content-Type: "+contentType)); assertTrue(buffer.readUtf8Line().equals("Content-Length: " + value.length())); assertTrue(buffer.readUtf8Line().equals("")); assertTrue(buffer.readUtf8Line().equals(value)); } private void assertFilePart(Buffer buffer, String disposition, String boundary, String name, String filename, String contentType) throws EOFException { assertTrue(buffer.readUtf8Line().contains("--" + boundary)); String line = buffer.readUtf8Line(); assertTrue(line.contains("Content-Disposition: "+ disposition)); assertTrue(line.contains("name=\""+ name + "\"")); assertTrue(line.contains("filename=\""+ filename + "\"")); assertTrue(buffer.readUtf8Line().startsWith("Content-Type: "+contentType)); assertTrue(buffer.readUtf8Line().startsWith("Content-Length: ")); assertTrue(buffer.readUtf8Line().equals("")); assertNotNull(buffer.readUtf8Line()); } private MockResponse formRequest(RecordedRequest request) { assertEquals("application/x-www-form-urlencoded", request.getHeader("Content-Type")); String body = request.getBody().readUtf8(); assertThat(body, Matchers.containsString("name+1=value+1")); assertThat(body, Matchers.containsString("name+2=value+2%2B1")); assertThat(body, Matchers.containsString("name+2=value+2%2B2")); return new MockResponse().setResponseCode(200); } private MockResponse patchRequest(RecordedRequest request, String expectedRequestContent, String contentType, byte[] responseBody) { assertEquals("PATCH", request.getMethod()); assertTrue("Invalid request content-length", Integer.parseInt(request.getHeader("Content-Length")) > 0); String requestContentType = request.getHeader("Content-Type"); assertNotNull("No content-type", requestContentType); Charset charset = StandardCharsets.ISO_8859_1; if (requestContentType.contains("charset=")) { String charsetName = requestContentType.split("charset=")[1]; charset = Charset.forName(charsetName); } assertEquals("Invalid request body", expectedRequestContent, request.getBody().readString(charset)); Buffer buf = new Buffer(); buf.write(responseBody); return new MockResponse().setResponseCode(201) .setHeader("Content-Length", responseBody.length) .setHeader("Content-Type", contentType) .setBody(buf); } private MockResponse putRequest(RecordedRequest request, String expectedRequestContent) { assertTrue("Invalid request content-length", Integer.parseInt(request.getHeader("Content-Length")) > 0); String requestContentType = request.getHeader("Content-Type"); assertNotNull("No content-type", requestContentType); Charset charset = StandardCharsets.ISO_8859_1; if (requestContentType.contains("charset=")) { String charsetName = requestContentType.split("charset=")[1]; charset = Charset.forName(charsetName); } assertEquals("Invalid request body", expectedRequestContent, request.getBody().readString(charset)); return new MockResponse().setResponseCode(202); } protected class TestDispatcher extends Dispatcher { @Override public MockResponse dispatch(RecordedRequest request) throws InterruptedException { try { byte[] helloWorldBytes = helloWorld.getBytes(StandardCharsets.UTF_8); if (request.getPath().equals("/get")) { return getRequest(request, helloWorldBytes, textContentType.toString()); } else if (request.getPath().equals("/get/nothing")) { return getRequest(request, new byte[0], textContentType.toString()); } else if (request.getPath().equals("/get/nocontenttype")) { return getRequest(request, helloWorldBytes, null); } else if (request.getPath().equals("/post")) { return postRequest(request, helloWorld, "/post/1", textContentType.toString(), helloWorldBytes); } else if (request.getPath().equals("/jsonpost")) { return jsonPostRequest(request, "/jsonpost/1", "application/json; charset=utf-8"); } else if (request.getPath().equals("/status/nocontent")) { return new MockResponse().setResponseCode(204); } else if (request.getPath().equals("/status/notmodified")) { return new MockResponse().setResponseCode(304); } else if (request.getPath().equals("/status/notfound")) { return new MockResponse().setResponseCode(404); } else if (request.getPath().equals("/status/server")) { return new MockResponse().setResponseCode(500); } else if (request.getPath().contains("/uri/")) { return new MockResponse().setBody(request.getPath()).setHeader("Content-Type", "text/plain"); } else if (request.getPath().equals("/multipart")) { return multipartRequest(request); } else if (request.getPath().equals("/form")) { return formRequest(request); } else if (request.getPath().equals("/delete")) { return new MockResponse().setResponseCode(200); } else if (request.getPath().equals("/patch")) { return patchRequest(request, helloWorld, textContentType.toString(), helloWorldBytes); } else if (request.getPath().equals("/put")) { return putRequest(request, helloWorld); } return new MockResponse().setResponseCode(404); } catch (Throwable ex) { return new MockResponse().setResponseCode(500).setBody(ex.toString()); } } } }