mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Proper way to cancel test requests in repositories based on httpclient-4.x
This commit is contained in:
@@ -4,7 +4,6 @@ import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.tasks.Task;
|
||||
import com.intellij.tasks.TaskBundle;
|
||||
import com.intellij.tasks.TaskRepositoryType;
|
||||
import com.intellij.tasks.gitlab.model.GitlabIssue;
|
||||
import com.intellij.tasks.gitlab.model.GitlabProject;
|
||||
@@ -14,7 +13,9 @@ import com.intellij.util.Function;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.xmlb.annotations.Tag;
|
||||
import com.intellij.util.xmlb.annotations.Transient;
|
||||
import org.apache.http.*;
|
||||
import org.apache.http.HttpException;
|
||||
import org.apache.http.HttpRequest;
|
||||
import org.apache.http.HttpRequestInterceptor;
|
||||
import org.apache.http.client.ResponseHandler;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.protocol.HttpContext;
|
||||
@@ -118,24 +119,7 @@ public class GitlabRepository extends NewBaseRepositoryImpl {
|
||||
@Nullable
|
||||
@Override
|
||||
public CancellableConnection createCancellableConnection() {
|
||||
return new CancellableConnection() {
|
||||
private HttpGet myRequest = new HttpGet(getIssuesUrl());
|
||||
|
||||
@Override
|
||||
protected void doTest() throws Exception {
|
||||
HttpResponse response = getHttpClient().execute(myRequest);
|
||||
StatusLine statusLine = response.getStatusLine();
|
||||
if (statusLine != null && statusLine.getStatusCode() != HttpStatus.SC_OK) {
|
||||
throw new Exception(TaskBundle.message("failure.http.error", statusLine.getStatusCode(), statusLine.getReasonPhrase()));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: find more about proper request aborting in HttpClient4.x
|
||||
@Override
|
||||
public void cancel() {
|
||||
myRequest.abort();
|
||||
}
|
||||
};
|
||||
return new HttpTestConnection(new HttpGet(getIssuesUrl()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+42
@@ -3,6 +3,7 @@ package com.intellij.tasks.impl.httpclient;
|
||||
import com.intellij.tasks.TaskRepositoryType;
|
||||
import com.intellij.tasks.config.TaskSettings;
|
||||
import com.intellij.tasks.impl.BaseRepository;
|
||||
import com.intellij.tasks.impl.RequestFailedException;
|
||||
import com.intellij.tasks.impl.TaskUtil;
|
||||
import com.intellij.util.net.HttpConfigurable;
|
||||
import com.intellij.util.net.ssl.CertificateManager;
|
||||
@@ -14,6 +15,7 @@ import org.apache.http.client.CredentialsProvider;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.config.AuthSchemes;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.HttpRequestBase;
|
||||
import org.apache.http.client.protocol.HttpClientContext;
|
||||
import org.apache.http.conn.ssl.X509HostnameVerifier;
|
||||
import org.apache.http.impl.auth.BasicScheme;
|
||||
@@ -154,4 +156,44 @@ public abstract class NewBaseRepositoryImpl extends BaseRepository {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class HttpTestConnection extends CancellableConnection {
|
||||
|
||||
// Request can be changed during test
|
||||
protected volatile HttpRequestBase myCurrentRequest;
|
||||
|
||||
public HttpTestConnection(@NotNull HttpRequestBase request) {
|
||||
myCurrentRequest = request;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doTest() throws Exception {
|
||||
try {
|
||||
test();
|
||||
}
|
||||
catch (IOException e) {
|
||||
// Depending on request state AbstractExecutionAwareRequest.abort() can cause either
|
||||
// * RequestAbortedException if connection was not yet leased
|
||||
// * InterruptedIOException before reading response
|
||||
// * SocketException("Socket closed") during reading response
|
||||
// However in all cases 'aborted' flag should be properly set
|
||||
if (!myCurrentRequest.isAborted()) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void test() throws Exception {
|
||||
HttpResponse response = getHttpClient().execute(myCurrentRequest);
|
||||
StatusLine statusLine = response.getStatusLine();
|
||||
if (statusLine != null && statusLine.getStatusCode() != HttpStatus.SC_OK) {
|
||||
throw RequestFailedException.forStatusCode(statusLine.getStatusCode(), statusLine.getReasonPhrase());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
myCurrentRequest.abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import com.intellij.util.xmlb.annotations.Tag;
|
||||
import com.intellij.util.xmlb.annotations.Transient;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.StatusLine;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
@@ -23,6 +24,8 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -107,28 +110,36 @@ public class RedmineRepository extends NewBaseRepositoryImpl {
|
||||
return new RedmineRepository(this);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public void testConnection() throws Exception {
|
||||
// Strangely, Redmine doesn't return 401 or 403 error codes, if client sent wrong credentials, and instead
|
||||
// merely returns empty array of issues with status code of 200. This means that we should attempt to fetch
|
||||
// something more specific than issues to test proper configuration, e.g. current user information at
|
||||
// /users/current.json. Unfortunately this endpoint may be unavailable on some old servers (see IDEA-122845)
|
||||
// and in this case we have to come back to requesting issues in this case to test anything at all.
|
||||
HttpClient client = getHttpClient();
|
||||
URIBuilder uriBuilder = new URIBuilder(getRestApiUrl("users", "current.json"));
|
||||
if (isUseApiKeyAuthentication()) {
|
||||
uriBuilder.addParameter("key", getAPIKey());
|
||||
}
|
||||
HttpResponse response = client.execute(new HttpGet(uriBuilder.build()));
|
||||
//TaskUtil.prettyFormatResponseToLog(LOG, response);
|
||||
int code = response.getStatusLine().getStatusCode();
|
||||
if (code == HttpStatus.SC_NOT_FOUND) {
|
||||
getIssues("", 0, 1, true);
|
||||
return;
|
||||
}
|
||||
if (code != HttpStatus.SC_OK) {
|
||||
throw RequestFailedException.forStatusCode(code);
|
||||
}
|
||||
public CancellableConnection createCancellableConnection() {
|
||||
return new NewBaseRepositoryImpl.HttpTestConnection(new HttpGet()) {
|
||||
@Override
|
||||
protected void test() throws Exception {
|
||||
// Strangely, Redmine doesn't return 401 or 403 error codes, if client sent wrong credentials, and instead
|
||||
// merely returns empty array of issues with status code of 200. This means that we should attempt to fetch
|
||||
// something more specific than issues to test proper configuration, e.g. current user information at
|
||||
// /users/current.json. Unfortunately this endpoint may be unavailable on some old servers (see IDEA-122845)
|
||||
// and in this case we have to come back to requesting issues in this case to test anything at all.
|
||||
|
||||
URIBuilder uriBuilder = new URIBuilder(getRestApiUrl("users", "current.json"));
|
||||
if (isUseApiKeyAuthentication()) {
|
||||
uriBuilder.addParameter("key", getAPIKey());
|
||||
}
|
||||
myCurrentRequest.setURI(uriBuilder.build());
|
||||
HttpClient client = getHttpClient();
|
||||
|
||||
HttpResponse httpResponse = client.execute(myCurrentRequest);
|
||||
StatusLine statusLine = httpResponse.getStatusLine();
|
||||
if (statusLine != null && statusLine.getStatusCode() == HttpStatus.SC_NOT_FOUND) {
|
||||
myCurrentRequest = new HttpGet(getIssuesUrl(0, 1, true));
|
||||
statusLine = client.execute(myCurrentRequest).getStatusLine();
|
||||
}
|
||||
if (statusLine != null && statusLine.getStatusCode() != HttpStatus.SC_OK) {
|
||||
throw RequestFailedException.forStatusCode(statusLine.getStatusCode(), statusLine.getReasonPhrase());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -144,12 +155,6 @@ public class RedmineRepository extends NewBaseRepositoryImpl {
|
||||
|
||||
public List<RedmineIssue> fetchIssues(String query, int offset, int limit, boolean withClosed) throws Exception {
|
||||
ensureProjectsDiscovered();
|
||||
URIBuilder builder = new URIBuilder(getRestApiUrl("issues.json"))
|
||||
.addParameter("offset", String.valueOf(offset))
|
||||
.addParameter("limit", String.valueOf(limit))
|
||||
.addParameter("status_id", withClosed ? "*" : "open")
|
||||
.addParameter("assigned_to_id", "me");
|
||||
|
||||
// Legacy API, can't find proper documentation
|
||||
//if (StringUtil.isNotEmpty(query)) {
|
||||
// builder.addParameter("fields[]", "subject").addParameter("operators[subject]", "~").addParameter("values[subject][]", query);
|
||||
@@ -158,13 +163,22 @@ public class RedmineRepository extends NewBaseRepositoryImpl {
|
||||
//if (myCurrentProject != null && myCurrentProject != UNSPECIFIED_PROJECT) {
|
||||
// builder.addParameter("project_id", String.valueOf(myCurrentProject.getId()));
|
||||
//}
|
||||
HttpClient client = getHttpClient();
|
||||
HttpGet method = new HttpGet(getIssuesUrl(offset, limit, withClosed));
|
||||
IssuesWrapper wrapper = client.execute(method, new GsonSingleObjectDeserializer<IssuesWrapper>(GSON, IssuesWrapper.class));
|
||||
return wrapper == null ? Collections.<RedmineIssue>emptyList() : wrapper.getIssues();
|
||||
}
|
||||
|
||||
private URI getIssuesUrl(int offset, int limit, boolean withClosed) throws URISyntaxException {
|
||||
URIBuilder builder = new URIBuilder(getRestApiUrl("issues.json"))
|
||||
.addParameter("offset", String.valueOf(offset))
|
||||
.addParameter("limit", String.valueOf(limit))
|
||||
.addParameter("status_id", withClosed ? "*" : "open")
|
||||
.addParameter("assigned_to_id", "me");
|
||||
if (isUseApiKeyAuthentication()) {
|
||||
builder.addParameter("key", myAPIKey);
|
||||
}
|
||||
HttpClient client = getHttpClient();
|
||||
HttpGet method = new HttpGet(builder.toString());
|
||||
IssuesWrapper wrapper = client.execute(method, new GsonSingleObjectDeserializer<IssuesWrapper>(GSON, IssuesWrapper.class));
|
||||
return wrapper == null ? Collections.<RedmineIssue>emptyList() : wrapper.getIssues();
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public List<RedmineProject> fetchProjects() throws Exception {
|
||||
|
||||
+3
-2
@@ -64,8 +64,9 @@ public class RedmineIntegrationTest extends TaskManagerTestCase {
|
||||
public void testCredentialsCheck() throws Exception {
|
||||
myRepository.setPassword("wrong-password");
|
||||
try {
|
||||
myRepository.testConnection();
|
||||
fail("testConnection() should fails, when wrong credentials specified");
|
||||
//noinspection ConstantConditions
|
||||
final Exception exception = myRepository.createCancellableConnection().call();
|
||||
assertNotNull("Test connection must fail when wrong credentials specified", exception);
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertEquals(TaskBundle.message("failure.login"), e.getMessage());
|
||||
|
||||
Reference in New Issue
Block a user