Browse Source

SONAR-9301 Support HTTP redirect on scanner side

pull/2093/head
Duarte Meneses 7 years ago
parent
commit
ae7c09e3fb

+ 95
- 0
it/it-tests/src/test/java/it/analysis/RedirectTest.java View File

/*
* 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);
}
}

+ 58
- 8
sonar-ws/src/main/java/org/sonarqube/ws/client/HttpConnector.java View File

import static com.google.common.base.Strings.nullToEmpty; import static com.google.common.base.Strings.nullToEmpty;
import static java.lang.String.format; 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. * 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> * <p>TLS 1.0, 1.1 and 1.2 are supported on both Java 7 and 8. SSLv3 is not supported.</p>
private final HttpUrl baseUrl; private final HttpUrl baseUrl;
private final String credentials; private final String credentials;
private final OkHttpClient okHttpClient; private final OkHttpClient okHttpClient;
private final OkHttpClient noRedirectOkHttpClient;


private HttpConnector(Builder builder) { private HttpConnector(Builder builder) {
this.baseUrl = HttpUrl.parse(builder.url.endsWith("/") ? builder.url : format("%s/", builder.url)); this.baseUrl = HttpUrl.parse(builder.url.endsWith("/") ? builder.url : format("%s/", builder.url));
okHttpClientBuilder.setSSLSocketFactory(builder.sslSocketFactory); okHttpClientBuilder.setSSLSocketFactory(builder.sslSocketFactory);
okHttpClientBuilder.setTrustManager(builder.sslTrustManager); okHttpClientBuilder.setTrustManager(builder.sslTrustManager);
this.okHttpClient = okHttpClientBuilder.build(); this.okHttpClient = okHttpClientBuilder.build();
this.noRedirectOkHttpClient = newClientWithoutRedirect(this.okHttpClient);
}

private static OkHttpClient newClientWithoutRedirect(OkHttpClient client) {
return client.newBuilder()
.followRedirects(false)
.followSslRedirects(false)
.build();
} }


@Override @Override
completeUrlQueryParameters(getRequest, urlBuilder); completeUrlQueryParameters(getRequest, urlBuilder);


Request.Builder okRequestBuilder = prepareOkRequestBuilder(getRequest, urlBuilder).get(); Request.Builder okRequestBuilder = prepareOkRequestBuilder(getRequest, urlBuilder).get();
return doCall(okRequestBuilder.build());
return new OkHttpResponse(doCall(okHttpClient, okRequestBuilder.build()));
} }


private WsResponse post(PostRequest postRequest) { private WsResponse post(PostRequest postRequest) {
}); });
body = bodyBuilder.build(); 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) { private HttpUrl.Builder prepareUrlBuilder(WsRequest wsRequest) {
.newBuilder(); .newBuilder();
} }


private static void completeUrlQueryParameters(BaseRequest request, HttpUrl.Builder urlBuilder) {
private static void completeUrlQueryParameters(BaseRequest<?> request, HttpUrl.Builder urlBuilder) {
request.getParameters().getKeys() request.getParameters().getKeys()
.forEach(key -> request.getParameters().getValues(key) .forEach(key -> request.getParameters().getValues(key)
.forEach(value -> urlBuilder.addQueryParameter(key, value))); .forEach(value -> urlBuilder.addQueryParameter(key, value)));
return okHttpRequestBuilder; 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 { try {
Response okResponse = call.execute();
return new OkHttpResponse(okResponse);
return call.execute();
} catch (IOException e) { } catch (IOException e) {
throw new IllegalStateException("Fail to request " + okRequest.url(), 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 * @since 5.5
*/ */

+ 26
- 0
sonar-ws/src/test/java/org/sonarqube/ws/client/HttpConnectorTest.java View File

package org.sonarqube.ws.client; package org.sonarqube.ws.client;


import java.io.File; import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.Proxy; import java.net.Proxy;
import java.util.List; import java.util.List;
serverUrl = server.url("").url().toString(); 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 @Test
public void test_default_settings() throws Exception { public void test_default_settings() throws Exception {
answerHelloWorld(); answerHelloWorld();

Loading…
Cancel
Save