/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonarqube.ws.client;
import java.io.File;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.util.List;
import javax.net.ssl.SSLSocketFactory;
import okhttp3.ConnectionSpec;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.sonarqube.ws.MediaTypes;
import static okhttp3.Credentials.basic;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.sonarqube.ws.client.HttpConnector.newBuilder;
public class HttpConnectorTest {
@Rule
public TemporaryFolder temp = new TemporaryFolder();
@Rule
public ExpectedException expectedException = ExpectedException.none();
private MockWebServer server;
private String serverUrl;
private HttpConnector underTest;
@Before
public void setUp() throws Exception {
server = new MockWebServer();
server.start();
serverUrl = server.url("").url().toString();
}
@Test
public void test_default_settings() throws Exception {
answerHelloWorld();
underTest = HttpConnector.newBuilder().url(serverUrl).build();
assertThat(underTest.baseUrl()).isEqualTo(serverUrl);
GetRequest request = new GetRequest("api/issues/search").setMediaType(MediaTypes.PROTOBUF);
WsResponse response = underTest.call(request);
// verify default timeouts on client
assertThat(underTest.okHttpClient().connectTimeoutMillis()).isEqualTo(HttpConnector.DEFAULT_CONNECT_TIMEOUT_MILLISECONDS);
assertThat(underTest.okHttpClient().readTimeoutMillis()).isEqualTo(HttpConnector.DEFAULT_READ_TIMEOUT_MILLISECONDS);
// verify response
assertThat(response.hasContent()).isTrue();
assertThat(response.content()).isEqualTo("hello, world!");
// verify the request received by server
RecordedRequest recordedRequest = server.takeRequest();
assertThat(recordedRequest.getMethod()).isEqualTo("GET");
assertThat(recordedRequest.getPath()).isEqualTo("/api/issues/search");
assertThat(recordedRequest.getHeader("Accept")).isEqualTo(MediaTypes.PROTOBUF);
assertThat(recordedRequest.getHeader("Accept-Charset")).isEqualTo("UTF-8");
assertThat(recordedRequest.getHeader("User-Agent")).startsWith("okhttp/");
// compression is handled by OkHttp
assertThat(recordedRequest.getHeader("Accept-Encoding")).isEqualTo("gzip");
}
@Test
public void use_basic_authentication() throws Exception {
answerHelloWorld();
underTest = HttpConnector.newBuilder()
.url(serverUrl)
.credentials("theLogin", "thePassword")
.build();
GetRequest request = new GetRequest("api/issues/search");
underTest.call(request);
RecordedRequest recordedRequest = server.takeRequest();
assertThat(recordedRequest.getHeader("Authorization")).isEqualTo(basic("theLogin", "thePassword"));
}
@Test
public void use_basic_authentication_with_null_password() throws Exception {
answerHelloWorld();
underTest = HttpConnector.newBuilder()
.url(serverUrl)
.credentials("theLogin", null)
.build();
GetRequest request = new GetRequest("api/issues/search");
underTest.call(request);
RecordedRequest recordedRequest = server.takeRequest();
assertThat(recordedRequest.getHeader("Authorization")).isEqualTo(basic("theLogin", ""));
}
/**
* Access token replaces the couple {login,password} and is sent through
* the login field
*/
@Test
public void use_access_token() throws Exception {
answerHelloWorld();
underTest = HttpConnector.newBuilder()
.url(serverUrl)
.token("theToken")
.build();
GetRequest request = new GetRequest("api/issues/search");
underTest.call(request);
RecordedRequest recordedRequest = server.takeRequest();
assertThat(recordedRequest.getHeader("Authorization")).isEqualTo(basic("theToken", ""));
}
@Test
public void use_proxy_authentication() throws Exception {
MockWebServer proxy = new MockWebServer();
proxy.start();
underTest = HttpConnector.newBuilder()
.url(serverUrl)
.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxy.getHostName(), proxy.getPort())))
.proxyCredentials("theProxyLogin", "theProxyPassword")
.build();
GetRequest request = new GetRequest("api/issues/search");
proxy.enqueue(new MockResponse().setResponseCode(407));
proxy.enqueue(new MockResponse().setBody("OK!"));
underTest.call(request);
RecordedRequest recordedRequest = proxy.takeRequest();
assertThat(recordedRequest.getHeader("Proxy-Authorization")).isNull();
recordedRequest = proxy.takeRequest();
assertThat(recordedRequest.getHeader("Proxy-Authorization")).isEqualTo(basic("theProxyLogin", "theProxyPassword"));
}
@Test
public void override_timeouts() {
underTest = HttpConnector.newBuilder()
.url(serverUrl)
.readTimeoutMilliseconds(42)
.connectTimeoutMilliseconds(74)
.build();
assertThat(underTest.okHttpClient().readTimeoutMillis()).isEqualTo(42);
assertThat(underTest.okHttpClient().connectTimeoutMillis()).isEqualTo(74);
}
@Test
public void send_user_agent() throws Exception {
answerHelloWorld();
underTest = HttpConnector.newBuilder()
.url(serverUrl)
.userAgent("Maven Plugin/2.3")
.build();
underTest.call(new GetRequest("api/issues/search"));
RecordedRequest recordedRequest = server.takeRequest();
assertThat(recordedRequest.getHeader("User-Agent")).isEqualTo("Maven Plugin/2.3");
}
@Test
public void fail_if_unknown_implementation_of_request() {
underTest = HttpConnector.newBuilder().url(serverUrl).build();
try {
underTest.call(mock(WsRequest.class));
fail();
} catch (IllegalArgumentException e) {
assertThat(e).hasMessageContaining("Unsupported implementation: ");
}
}
@Test
public void fail_if_malformed_URL() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Malformed URL: 'wrong URL'");
underTest = newBuilder().url("wrong URL").build();
}
@Test
public void send_post_request() throws Exception {
answerHelloWorld();
PostRequest request = new PostRequest("api/issues/search")
.setParam("severity", "MAJOR")
.setMediaType(MediaTypes.PROTOBUF);
underTest = HttpConnector.newBuilder().url(serverUrl).build();
WsResponse response = underTest.call(request);
// verify response
assertThat(response.hasContent()).isTrue();
assertThat(response.content()).isEqualTo("hello, world!");
// verify the request received by server
RecordedRequest recordedRequest = server.takeRequest();
assertThat(recordedRequest.getMethod()).isEqualTo("POST");
assertThat(recordedRequest.getPath()).isEqualTo("/api/issues/search");
assertThat(recordedRequest.getBody().readUtf8()).isEqualTo("severity=MAJOR");
}
@Test
public void upload_file() throws Exception {
answerHelloWorld();
File reportFile = temp.newFile();
FileUtils.write(reportFile, "the report content");
PostRequest request = new PostRequest("api/report/upload")
.setParam("project", "theKey")
.setPart("report", new PostRequest.Part(MediaTypes.TXT, reportFile))
.setMediaType(MediaTypes.PROTOBUF);
underTest = HttpConnector.newBuilder().url(serverUrl).build();
WsResponse response = underTest.call(request);
assertThat(response.hasContent()).isTrue();
RecordedRequest recordedRequest = server.takeRequest();
assertThat(recordedRequest.getMethod()).isEqualTo("POST");
assertThat(recordedRequest.getPath()).isEqualTo("/api/report/upload?project=theKey");
String body = IOUtils.toString(recordedRequest.getBody().inputStream());
assertThat(body)
.contains("Content-Disposition: form-data; name=\"report\"")
.contains("Content-Type: text/plain")
.contains("the report content");
}
@Test
public void http_error() throws Exception {
server.enqueue(new MockResponse().setResponseCode(404));
PostRequest request = new PostRequest("api/issues/search");
underTest = HttpConnector.newBuilder().url(serverUrl).build();
WsResponse wsResponse = underTest.call(request);
assertThat(wsResponse.code()).isEqualTo(404);
}
@Test
public void support_base_url_ending_with_slash() throws Exception {
assertThat(serverUrl).endsWith("/");
underTest = HttpConnector.newBuilder().url(StringUtils.removeEnd(serverUrl, "/")).build();
GetRequest request = new GetRequest("api/issues/search");
answerHelloWorld();
WsResponse response = underTest.call(request);
assertThat(response.hasContent()).isTrue();
}
@Test
public void support_base_url_with_context() {
// just to be sure
assertThat(serverUrl).endsWith("/");
underTest = HttpConnector.newBuilder().url(serverUrl + "sonar").build();
GetRequest request = new GetRequest("api/issues/search");
answerHelloWorld();
assertThat(underTest.call(request).requestUrl()).isEqualTo(serverUrl + "sonar/api/issues/search");
request = new GetRequest("/api/issues/search");
answerHelloWorld();
assertThat(underTest.call(request).requestUrl()).isEqualTo(serverUrl + "sonar/api/issues/search");
}
@Test
public void support_tls_versions_of_java8() {
underTest = HttpConnector.newBuilder().url(serverUrl).build();
assertTlsAndClearTextSpecifications(underTest);
assertThat(underTest.okHttpClient().sslSocketFactory()).isInstanceOf(SSLSocketFactory.getDefault().getClass());
}
private void assertTlsAndClearTextSpecifications(HttpConnector underTest) {
List<ConnectionSpec> connectionSpecs = underTest.okHttpClient().connectionSpecs();
assertThat(connectionSpecs).hasSize(2);
// TLS. tlsVersions()==null means all TLS versions
assertThat(connectionSpecs.get(0).tlsVersions()).isNull();
assertThat(connectionSpecs.get(0).isTls()).isTrue();
// HTTP
assertThat(connectionSpecs.get(1).tlsVersions()).isNull();
assertThat(connectionSpecs.get(1).isTls()).isFalse();
}
private void answerHelloWorld() {
server.enqueue(new MockResponse().setBody("hello, world!"));
}
}