/*
* 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.sonar.scanner.bootstrap;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.util.ArrayList;
import java.util.List;
import org.sonar.api.CoreProperties;
import org.sonar.api.utils.MessageException;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.api.utils.log.Profiler;
import org.sonarqube.ws.client.HttpException;
import org.sonarqube.ws.client.WsClient;
import org.sonarqube.ws.client.WsConnector;
import org.sonarqube.ws.client.WsRequest;
import org.sonarqube.ws.client.WsResponse;
import static java.lang.String.format;
import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
import static java.net.HttpURLConnection.HTTP_FORBIDDEN;
import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
public class ScannerWsClient {
private static final Logger LOG = Loggers.get(ScannerWsClient.class);
private final WsClient target;
private final boolean hasCredentials;
private final GlobalMode globalMode;
public ScannerWsClient(WsClient target, boolean hasCredentials, GlobalMode globalMode) {
this.target = target;
this.hasCredentials = hasCredentials;
this.globalMode = globalMode;
}
/**
* If an exception is not thrown, the response needs to be closed by either calling close() directly, or closing the
* body content's stream/reader.
* @throws IllegalStateException if the request could not be executed due to
* a connectivity problem or timeout. Because networks can
* fail during an exchange, it is possible that the remote server
* accepted the request before the failure
* @throws HttpException if the response code is not in range [200..300)
*/
public WsResponse call(WsRequest request) {
Preconditions.checkState(!globalMode.isMediumTest(), "No WS call should be made in medium test mode");
Profiler profiler = Profiler.createIfDebug(LOG).start();
WsResponse response = target.wsConnector().call(request);
profiler.stopDebug(format("%s %d %s", request.getMethod(), response.code(), response.requestUrl()));
failIfUnauthorized(response);
return response;
}
public String baseUrl() {
return target.wsConnector().baseUrl();
}
@VisibleForTesting
WsConnector wsConnector() {
return target.wsConnector();
}
private void failIfUnauthorized(WsResponse response) {
int code = response.code();
if (code == HTTP_UNAUTHORIZED) {
response.close();
if (hasCredentials) {
// credentials are not valid
throw MessageException.of(format("Not authorized. Please check the properties %s and %s.",
CoreProperties.LOGIN, CoreProperties.PASSWORD));
}
// not authenticated - see https://jira.sonarsource.com/browse/SONAR-4048
throw MessageException.of(format("Not authorized. Analyzing this project requires to be authenticated. " +
"Please provide the values of the properties %s and %s.", CoreProperties.LOGIN, CoreProperties.PASSWORD));
}
if (code == HTTP_FORBIDDEN || code == HTTP_BAD_REQUEST) {
// SONAR-4397 Details are in response content
throw MessageException.of(tryParseAsJsonError(response.content()));
}
response.failIfNotSuccessful();
}
public static String tryParseAsJsonError(String responseContent) {
try {
JsonParser parser = new JsonParser();
JsonObject obj = parser.parse(responseContent).getAsJsonObject();
JsonArray errors = obj.getAsJsonArray("errors");
List<String> errorMessages = new ArrayList<>();
for (JsonElement e : errors) {
errorMessages.add(e.getAsJsonObject().get("msg").getAsString());
}
return Joiner.on(", ").join(errorMessages);
} catch (Exception e) {
return responseContent;
}
}
}