mirror of
https://github.com/SonarSource/sonarqube.git
synced 2024-08-13 02:10:35 +02:00
SONAR-9301 Support HTTP redirect on scanner side
This commit is contained in:
parent
f8c603b07c
commit
ae7c09e3fb
95
it/it-tests/src/test/java/it/analysis/RedirectTest.java
Normal file
95
it/it-tests/src/test/java/it/analysis/RedirectTest.java
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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 it.analysis;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.net.InetAddress;
|
||||
|
||||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.server.handler.MovedContextHandler;
|
||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sonar.orchestrator.Orchestrator;
|
||||
import com.sonar.orchestrator.build.BuildResult;
|
||||
import com.sonar.orchestrator.build.SonarScanner;
|
||||
import com.sonar.orchestrator.util.NetworkUtils;
|
||||
|
||||
import it.Category3Suite;
|
||||
import util.ItUtils;
|
||||
|
||||
public class RedirectTest {
|
||||
@ClassRule
|
||||
public static Orchestrator orchestrator = Category3Suite.ORCHESTRATOR;
|
||||
|
||||
private static Server server;
|
||||
private static int redirectPort;
|
||||
|
||||
@Before
|
||||
public static void beforeClass() throws Exception {
|
||||
orchestrator.resetData();
|
||||
redirectPort = NetworkUtils.getNextAvailablePort(InetAddress.getLoopbackAddress());
|
||||
|
||||
QueuedThreadPool threadPool = new QueuedThreadPool();
|
||||
threadPool.setMaxThreads(500);
|
||||
|
||||
server = new Server(threadPool);
|
||||
// HTTP Configuration
|
||||
HttpConfiguration httpConfig = new HttpConfiguration();
|
||||
httpConfig.setSendServerVersion(true);
|
||||
httpConfig.setSendDateHeader(false);
|
||||
|
||||
// Moved handler
|
||||
MovedContextHandler movedContextHandler = new MovedContextHandler();
|
||||
movedContextHandler.setPermanent(true);
|
||||
movedContextHandler.setNewContextURL(orchestrator.getServer().getUrl());
|
||||
server.setHandler(movedContextHandler);
|
||||
|
||||
// http connector
|
||||
ServerConnector http = new ServerConnector(server, new HttpConnectionFactory(httpConfig));
|
||||
http.setPort(redirectPort);
|
||||
server.addConnector(http);
|
||||
server.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public static void after() throws Exception {
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRedirect() {
|
||||
SonarScanner sonarScanner = SonarScanner.create(ItUtils.projectDir("shared/xoo-sample"))
|
||||
.setScannerVersion("2.7")
|
||||
.setProperty("sonar.host.url", "http://localhost:" + redirectPort);
|
||||
BuildResult buildResult = orchestrator.executeBuild(sonarScanner);
|
||||
|
||||
// logs show original URL
|
||||
assertThat(buildResult.getLogs()).contains("ANALYSIS SUCCESSFUL, you can browse " + "http://localhost:" + redirectPort);
|
||||
|
||||
}
|
||||
}
|
@ -41,6 +41,11 @@ import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static com.google.common.base.Strings.nullToEmpty;
|
||||
import static java.lang.String.format;
|
||||
|
||||
import static java.net.HttpURLConnection.HTTP_MOVED_PERM;
|
||||
import static java.net.HttpURLConnection.HTTP_MOVED_TEMP;
|
||||
import static okhttp3.internal.http.StatusLine.HTTP_PERM_REDIRECT;
|
||||
import static okhttp3.internal.http.StatusLine.HTTP_TEMP_REDIRECT;
|
||||
|
||||
/**
|
||||
* Connect to any SonarQube server available through HTTP or HTTPS.
|
||||
* <p>TLS 1.0, 1.1 and 1.2 are supported on both Java 7 and 8. SSLv3 is not supported.</p>
|
||||
@ -58,6 +63,7 @@ public class HttpConnector implements WsConnector {
|
||||
private final HttpUrl baseUrl;
|
||||
private final String credentials;
|
||||
private final OkHttpClient okHttpClient;
|
||||
private final OkHttpClient noRedirectOkHttpClient;
|
||||
|
||||
private HttpConnector(Builder builder) {
|
||||
this.baseUrl = HttpUrl.parse(builder.url.endsWith("/") ? builder.url : format("%s/", builder.url));
|
||||
@ -82,6 +88,14 @@ public class HttpConnector implements WsConnector {
|
||||
okHttpClientBuilder.setSSLSocketFactory(builder.sslSocketFactory);
|
||||
okHttpClientBuilder.setTrustManager(builder.sslTrustManager);
|
||||
this.okHttpClient = okHttpClientBuilder.build();
|
||||
this.noRedirectOkHttpClient = newClientWithoutRedirect(this.okHttpClient);
|
||||
}
|
||||
|
||||
private static OkHttpClient newClientWithoutRedirect(OkHttpClient client) {
|
||||
return client.newBuilder()
|
||||
.followRedirects(false)
|
||||
.followSslRedirects(false)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -109,7 +123,7 @@ public class HttpConnector implements WsConnector {
|
||||
completeUrlQueryParameters(getRequest, urlBuilder);
|
||||
|
||||
Request.Builder okRequestBuilder = prepareOkRequestBuilder(getRequest, urlBuilder).get();
|
||||
return doCall(okRequestBuilder.build());
|
||||
return new OkHttpResponse(doCall(okHttpClient, okRequestBuilder.build()));
|
||||
}
|
||||
|
||||
private WsResponse post(PostRequest postRequest) {
|
||||
@ -139,8 +153,10 @@ public class HttpConnector implements WsConnector {
|
||||
});
|
||||
body = bodyBuilder.build();
|
||||
}
|
||||
Request.Builder reqBuilder = prepareOkRequestBuilder(postRequest, urlBuilder);
|
||||
return doCall(reqBuilder.post(body).build());
|
||||
Request.Builder okRequestBuilder = prepareOkRequestBuilder(postRequest, urlBuilder).post(body);
|
||||
Response response = doCall(noRedirectOkHttpClient, okRequestBuilder.build());
|
||||
response = checkRedirect(response);
|
||||
return new OkHttpResponse(response);
|
||||
}
|
||||
|
||||
private HttpUrl.Builder prepareUrlBuilder(WsRequest wsRequest) {
|
||||
@ -150,7 +166,7 @@ public class HttpConnector implements WsConnector {
|
||||
.newBuilder();
|
||||
}
|
||||
|
||||
private static void completeUrlQueryParameters(BaseRequest request, HttpUrl.Builder urlBuilder) {
|
||||
private static void completeUrlQueryParameters(BaseRequest<?> request, HttpUrl.Builder urlBuilder) {
|
||||
request.getParameters().getKeys()
|
||||
.forEach(key -> request.getParameters().getValues(key)
|
||||
.forEach(value -> urlBuilder.addQueryParameter(key, value)));
|
||||
@ -167,16 +183,50 @@ public class HttpConnector implements WsConnector {
|
||||
return okHttpRequestBuilder;
|
||||
}
|
||||
|
||||
private OkHttpResponse doCall(Request okRequest) {
|
||||
Call call = okHttpClient.newCall(okRequest);
|
||||
private static Response doCall(OkHttpClient client, Request okRequest) {
|
||||
Call call = client.newCall(okRequest);
|
||||
try {
|
||||
Response okResponse = call.execute();
|
||||
return new OkHttpResponse(okResponse);
|
||||
return call.execute();
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Fail to request " + okRequest.url(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private Response checkRedirect(Response response) {
|
||||
switch (response.code()) {
|
||||
case HTTP_MOVED_PERM:
|
||||
case HTTP_MOVED_TEMP:
|
||||
case HTTP_TEMP_REDIRECT:
|
||||
case HTTP_PERM_REDIRECT:
|
||||
// OkHttpClient does not follow the redirect with the same HTTP method. A POST is
|
||||
// redirected to a GET. Because of that the redirect must be manually implemented.
|
||||
// See:
|
||||
// https://github.com/square/okhttp/blob/07309c1c7d9e296014268ebd155ebf7ef8679f6c/okhttp/src/main/java/okhttp3/internal/http/RetryAndFollowUpInterceptor.java#L316
|
||||
// https://github.com/square/okhttp/issues/936#issuecomment-266430151
|
||||
return followPostRedirect(response);
|
||||
default:
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
private Response followPostRedirect(Response response) {
|
||||
String location = response.header("Location");
|
||||
if (location == null) {
|
||||
throw new IllegalStateException(format("Missing HTTP header 'Location' in redirect of %s", response.request().url()));
|
||||
}
|
||||
HttpUrl url = response.request().url().resolve(location);
|
||||
|
||||
// Don't follow redirects to unsupported protocols.
|
||||
if (url == null) {
|
||||
throw new IllegalStateException(format("Unsupported protocol in redirect of %s to %s", response.request().url(), location));
|
||||
}
|
||||
|
||||
Request.Builder redirectRequest = response.request().newBuilder();
|
||||
redirectRequest.post(response.request().body());
|
||||
response.body().close();
|
||||
return doCall(noRedirectOkHttpClient, redirectRequest.url(url).build());
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 5.5
|
||||
*/
|
||||
|
@ -20,6 +20,7 @@
|
||||
package org.sonarqube.ws.client;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.util.List;
|
||||
@ -62,6 +63,31 @@ public class HttpConnectorTest {
|
||||
serverUrl = server.url("").url().toString();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void follow_redirects_post() throws IOException, InterruptedException {
|
||||
MockWebServer server2 = new MockWebServer();
|
||||
server2.start();
|
||||
server2.url("").url().toString();
|
||||
|
||||
server.enqueue(new MockResponse()
|
||||
.setResponseCode(302)
|
||||
.setHeader("Location", server2.url("").url().toString()));
|
||||
|
||||
server2.enqueue(new MockResponse()
|
||||
.setResponseCode(200));
|
||||
|
||||
underTest = HttpConnector.newBuilder().url(serverUrl).build();
|
||||
PostRequest request = new PostRequest("api/ce/submit").setParam("projectKey", "project");
|
||||
WsResponse response = underTest.call(request);
|
||||
|
||||
RecordedRequest recordedRequest = server2.takeRequest();
|
||||
|
||||
assertThat(recordedRequest.getMethod()).isEqualTo("POST");
|
||||
assertThat(recordedRequest.getBody().readUtf8()).isEqualTo("projectKey=project");
|
||||
assertThat(response.requestUrl()).isEqualTo(server2.url("").url().toString());
|
||||
assertThat(response.code()).isEqualTo(200);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_default_settings() throws Exception {
|
||||
answerHelloWorld();
|
||||
|
Loading…
Reference in New Issue
Block a user