// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.tools; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.junit.Assert.assertThat; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.logging.Handler; import java.util.logging.LogRecord; import javax.json.JsonObject; import javax.json.JsonReader; import javax.json.spi.JsonProvider; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.openstreetmap.josm.TestUtils; import org.openstreetmap.josm.data.Version; import org.openstreetmap.josm.gui.progress.ProgressMonitor; import org.openstreetmap.josm.testutils.JOSMTestRules; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; /** * Tests the {@link HttpClient} using the webservice <a href="https://httpbin.org/">https://httpbin.org/</a>. */ public class HttpClientTest { @Rule @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD") public JOSMTestRules test = new JOSMTestRules().preferences().timeout(15000); private ProgressMonitor progress; private LogRecord captured; private final Handler handler = new Handler() { @Override public void publish(LogRecord record) { captured = record; } @Override public void flush() { } @Override public void close() throws SecurityException { } }; @Before public void setUp() { progress = TestUtils.newTestProgressMonitor(); captured = null; Logging.getLogger().addHandler(handler); Logging.getLogger().setLevel(Logging.LEVEL_DEBUG); } @Test public void testConstructorGetterSetter() throws IOException { final HttpClient client = HttpClient.create(new URL("https://httpbin.org/")); assertThat(client.getURL(), is(new URL("https://httpbin.org/"))); assertThat(client.getRequestMethod(), is("GET")); assertThat(client.getRequestHeader("Accept"), nullValue()); client.setAccept("text/html"); assertThat(client.getRequestHeader("Accept"), is("text/html")); assertThat(client.getRequestHeader("ACCEPT"), is("text/html")); client.setHeaders(Collections.singletonMap("foo", "bar")); assertThat(client.getRequestHeader("foo"), is("bar")); client.setHeaders(Collections.singletonMap("foo", "baz")); assertThat(client.getRequestHeader("foo"), is("baz")); client.setHeaders(Collections.singletonMap("foo", (String) null)); assertThat(client.getRequestHeader("foo"), nullValue()); } @Test public void testGet() throws IOException { final HttpClient.Response response = HttpClient.create(new URL("https://httpbin.org/get?foo=bar")).connect(progress); assertThat(response.getRequestMethod(), is("GET")); assertThat(response.getResponseCode(), is(200)); assertThat(response.getResponseMessage(), is("OK")); assertThat(response.getContentType(), is("application/json")); assertThat(response.getHeaderField("Content-Type"), is("application/json")); assertThat(response.getHeaderField("Content-TYPE"), is("application/json")); assertThat(response.getHeaderFields().get("Content-Type"), is(Collections.singletonList("application/json"))); assertThat(response.getHeaderFields().get("Content-TYPE"), is(Collections.singletonList("application/json"))); try (InputStream in = response.getContent(); JsonReader json = JsonProvider.provider().createReader(in)) { final JsonObject root = json.readObject(); assertThat(root.getJsonObject("args").getString("foo"), is("bar")); assertThat(root.getString("url"), is("https://httpbin.org/get?foo=bar")); } } @Test public void testUserAgent() throws IOException { try (InputStream in = HttpClient.create(new URL("https://httpbin.org/user-agent")).connect(progress).getContent(); JsonReader json = JsonProvider.provider().createReader(in)) { assertThat(json.readObject().getString("user-agent"), is(Version.getInstance().getFullAgentString())); } } @Test public void testFetchUtf8Content() throws IOException { final HttpClient.Response response = HttpClient.create(new URL("https://httpbin.org/encoding/utf8")).connect(progress); assertThat(response.getResponseCode(), is(200)); final String content = response.fetchContent(); assertThat(content, containsString("UTF-8 encoded sample plain-text file")); assertThat(content, containsString("\u2200x\u2208\u211d:")); } @Test public void testPost() throws IOException { final String text = "Hello World!\nGeetings from JOSM, the Java OpenStreetMap Editor"; final HttpClient.Response response = HttpClient.create(new URL("https://httpbin.org/post"), "POST") .setHeader("Content-Type", "text/plain") .setRequestBody(text.getBytes(StandardCharsets.UTF_8)) .setFinishOnCloseOutput(false) // to fix #12583, not sure if it's the best way to do it .connect(progress); assertThat(response.getResponseCode(), is(200)); try (InputStream in = response.getContent(); JsonReader json = JsonProvider.provider().createReader(in)) { assertThat(json.readObject().getString("data"), is(text)); } } @Test public void testPostZero() throws IOException { final HttpClient.Response response = HttpClient.create(new URL("https://httpbin.org/post"), "POST") .setHeader("Content-Type", "text/plain") .setRequestBody("".getBytes(StandardCharsets.UTF_8)) .setFinishOnCloseOutput(false) // to fix #12583, not sure if it's the best way to do it .connect(progress); assertThat(response.getResponseCode(), is(200)); try (InputStream in = response.getContent(); JsonReader json = JsonProvider.provider().createReader(in)) { assertThat(json.readObject().getString("data"), is("")); } } @Test public void testRelativeRedirects() throws IOException { final HttpClient.Response response = HttpClient.create(new URL("https://httpbin.org/relative-redirect/5")).connect(progress); assertThat(response.getResponseCode(), is(200)); assertThat(response.getContentLength() > 100, is(true)); } @Test public void testAbsoluteRedirects() throws IOException { final HttpClient.Response response = HttpClient.create(new URL("https://httpbin.org/absolute-redirect/5")).connect(progress); assertThat(response.getResponseCode(), is(200)); assertThat(response.getContentLength() > 100, is(true)); } @Test(expected = IOException.class) public void testTooMuchRedirects() throws IOException { HttpClient.create(new URL("https://httpbin.org/redirect/5")).setMaxRedirects(4).connect(progress); } @Test public void testHttp418() throws IOException { // https://tools.ietf.org/html/rfc2324 final HttpClient.Response response = HttpClient.create(new URL("https://httpbin.org/status/418")).connect(progress); assertThat(response.getResponseCode(), is(418)); assertThat(response.getResponseMessage(), is("I'M A TEAPOT")); final String content = response.fetchContent(); assertThat(content, containsString("-=[ teapot ]=-")); assertThat(captured.getMessage(), containsString("-=[ teapot ]=-")); assertThat(captured.getLevel(), is(Logging.LEVEL_DEBUG)); } @Test() public void testHttp401() throws IOException { // https://tools.ietf.org/html/rfc2324 final HttpClient.Response response = HttpClient.create(new URL("https://httpbin.org/status/401")).connect(progress); assertThat(response.getResponseCode(), is(401)); assertThat(response.getResponseMessage(), is("UNAUTHORIZED")); final String content = response.fetchContent(); assertThat(content, is("")); assertThat(captured.getMessage(), containsString("Server did not return any body")); assertThat(captured.getLevel(), is(Logging.LEVEL_DEBUG)); } @Test public void testHttp402() throws IOException { // https://tools.ietf.org/html/rfc2324 final HttpClient.Response response = HttpClient.create(new URL("https://httpbin.org/status/402")).connect(progress); assertThat(response.getResponseCode(), is(402)); assertThat(response.getResponseMessage(), is("PAYMENT REQUIRED")); final String content = response.fetchContent(); assertThat(content, containsString("Fuck you, pay me!")); assertThat(captured.getMessage(), containsString("Fuck you, pay me!")); assertThat(captured.getLevel(), is(Logging.LEVEL_DEBUG)); } @Test public void testHttp403() throws IOException { // https://tools.ietf.org/html/rfc2324 final HttpClient.Response response = HttpClient.create(new URL("https://httpbin.org/status/403")).connect(progress); assertThat(response.getResponseCode(), is(403)); assertThat(response.getResponseMessage(), is("FORBIDDEN")); final String content = response.fetchContent(); assertThat(content, is("")); assertThat(captured.getMessage(), containsString("Server did not return any body")); assertThat(captured.getLevel(), is(Logging.LEVEL_DEBUG)); } @Test public void testHttp404() throws IOException { // https://tools.ietf.org/html/rfc2324 final HttpClient.Response response = HttpClient.create(new URL("https://httpbin.org/status/404")).connect(progress); assertThat(response.getResponseCode(), is(404)); assertThat(response.getResponseMessage(), is("NOT FOUND")); final String content = response.fetchContent(); assertThat(content, is("")); assertThat(captured.getMessage(), containsString("Server did not return any body")); assertThat(captured.getLevel(), is(Logging.LEVEL_DEBUG)); } @Test public void testHttp500() throws IOException { // https://tools.ietf.org/html/rfc2324 final HttpClient.Response response = HttpClient.create(new URL("https://httpbin.org/status/500")).connect(progress); assertThat(response.getResponseCode(), is(500)); assertThat(response.getResponseMessage(), is("INTERNAL SERVER ERROR")); final String content = response.fetchContent(); assertThat(content, containsString("")); assertThat(captured.getMessage(), containsString("Server did not return any body")); assertThat(captured.getLevel(), is(Logging.LEVEL_DEBUG)); } @Test public void testRequestInTime() throws IOException { final HttpClient.Response response = HttpClient.create(new URL("https://httpbin.org/delay/3")).setReadTimeout(3500).connect(progress); assertThat(response.getResponseCode(), is(200)); } @Test(expected = IOException.class) public void testTakesTooLong() throws IOException { HttpClient.create(new URL("https://httpbin.org/delay/3")).setReadTimeout(2500).connect(progress); } /** * Test of {@link HttpClient.Response#uncompress} method with Gzip compression. * @throws IOException if any I/O error occurs */ @Test public void testOpenUrlGzip() throws IOException { final URL url = new URL("https://www.openstreetmap.org/trace/1613906/data"); try (BufferedReader x = HttpClient.create(url).connect().uncompress(true).getContentReader()) { Assert.assertTrue(x.readLine().startsWith("<?xml version=")); } } /** * Test of {@link HttpClient.Response#uncompress} method with Bzip compression. * @throws IOException if any I/O error occurs */ @Test public void testOpenUrlBzip() throws IOException { final URL url = new URL("https://www.openstreetmap.org/trace/785544/data"); try (BufferedReader x = HttpClient.create(url).connect().uncompress(true).getContentReader()) { Assert.assertTrue(x.readLine().startsWith("<?xml version=")); } } /** * Test of {@link HttpClient.Response#uncompress} method with Bzip compression. * @throws IOException if any I/O error occurs */ @Test public void testTicket9660() throws IOException { final URL url = new URL("http://www.openstreetmap.org/trace/1350010/data"); try (BufferedReader x = HttpClient.create(url).connect() .uncompress(true).uncompressAccordingToContentDisposition(true).getContentReader()) { Assert.assertTrue(x.readLine().startsWith("<?xml version=")); } } }