import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
+import java.util.Optional;
import org.sonar.api.server.ws.LocalConnector;
import org.sonar.api.server.ws.internal.ValidatingRequest;
public String getMediaType() {
return localRequest.getMediaType();
}
+
+ @Override
+ public Optional<String> header(String name) {
+ return localRequest.getHeader(name);
+ }
}
import java.io.InputStream;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import javax.annotation.CheckForNull;
import javax.servlet.http.HttpServletRequest;
import org.sonar.api.server.ws.internal.PartImpl;
return source.getRequestURI().replaceFirst(source.getContextPath(), "");
}
+ @Override
+ public Optional<String> header(String name) {
+ return Optional.ofNullable(source.getHeader(name));
+ }
}
@Rule
public ExpectedException expectedException = ExpectedException.none();
- HttpServletRequest source = mock(HttpServletRequest.class);
+ private HttpServletRequest source = mock(HttpServletRequest.class);
- ServletRequest underTest = new ServletRequest(source);
+ private ServletRequest underTest = new ServletRequest(source);
@Test
public void call_method() {
assertThat(underTest.toString()).isEqualTo("http:localhost:9000/api/issues?components=sonar");
}
+
+ @Test
+ public void header_returns_the_value_of_http_header() {
+ when(source.getHeader("Accept")).thenReturn("text/plain");
+ assertThat(underTest.header("Accept")).hasValue("text/plain");
+ }
+
+ @Test
+ public void header_is_empty_if_absent_from_request() {
+ when(source.getHeader("Accept")).thenReturn(null);
+ assertThat(underTest.header("Accept")).isEmpty();
+ }
+
+ @Test
+ public void header_has_empty_value_if_present_in_request_without_value() {
+ when(source.getHeader("Accept")).thenReturn("");
+ assertThat(underTest.header("Accept")).hasValue("");
+ }
+
}
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import org.apache.commons.io.IOUtils;
import org.sonar.api.server.ws.internal.PartImpl;
import org.sonar.api.server.ws.internal.ValidatingRequest;
private final ListMultimap<String, String> multiParams = ArrayListMultimap.create();
private final Map<String, String> params = new HashMap<>();
+ private final Map<String, String> headers = new HashMap<>();
private final Map<String, Part> parts = Maps.newHashMap();
private String method = "GET";
private String mimeType = "application/octet-stream";
return this;
}
+ @Override
+ public Optional<String> header(String name) {
+ return Optional.ofNullable(headers.get(name));
+ }
+
+ public TestRequest setHeader(String name, String value) {
+ this.headers.put(requireNonNull(name), requireNonNull(value));
+ return this;
+ }
+
public TestResponse execute() {
try {
DumbResponse response = new DumbResponse();
package org.sonar.server.ws;
import com.google.common.collect.Maps;
-import com.google.protobuf.GeneratedMessage;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
-import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.apache.commons.io.IOUtils;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
+import static java.util.Objects.requireNonNull;
import static org.assertj.core.api.Assertions.assertThat;
import static org.sonar.server.ws.RequestVerifier.verifyRequest;
private String mediaType = MediaTypes.JSON;
private Map<String, String> params = Maps.newHashMap();
+ private Map<String, String> headers = Maps.newHashMap();
private final Map<String, Part> parts = Maps.newHashMap();
private TestRequest(String method) {
return params.keySet().contains(key);
}
+ @Override
+ public Optional<String> header(String name) {
+ return Optional.ofNullable(headers.get(name));
+ }
+
+ public TestRequest setHeader(String name, String value) {
+ this.headers.put(requireNonNull(name), requireNonNull(value));
+ return this;
+ }
+
@Override
public String getPath() {
return path;
import java.util.Collection;
import java.util.List;
+import java.util.Optional;
import javax.annotation.CheckForNull;
/**
* @see Request#multiParam(String)
*/
List<String> getMultiParam(String key);
+
+ /**
+ * @see Request#header(String)
+ * @since 6.6
+ */
+ Optional<String> getHeader(String name);
}
interface LocalResponse {
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
+import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Supplier;
return AbsentStringParam.absent();
}
+ /**
+ * Optional value of the HTTP header with specified name.
+ * If present, the result can have an empty string value ({@code ""}).
+ *
+ * @since 6.6
+ */
+ public abstract Optional<String> header(String name);
+
/**
* Allows a web service to call another web service.
* @see LocalConnector
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.apache.commons.io.IOUtils;
private final Map<String, String> params = new HashMap<>();
private final Map<String, Part> parts = new HashMap<>();
+ private final Map<String, String> headers = new HashMap<>();
private String mediaType = "application/json";
private String path;
return this;
}
+ @Override
+ public Optional<String> header(String name) {
+ return Optional.ofNullable(headers.get(name));
+ }
+
+ public SimpleGetRequest setHeader(String name, String value) {
+ headers.put(name, value);
+ return this;
+ }
}
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Nullable;
import org.apache.commons.io.IOUtils;
@DataProvider
public static Object[][] date_times() {
- return new Object[][] {
+ return new Object[][]{
{"2014-05-27", parseDate("2014-05-27")},
{"2014-05-27T15:50:45+0100", parseDateTime("2014-05-27T15:50:45+0100")},
{null, null}
private final ListMultimap<String, String> multiParams = ArrayListMultimap.create();
private final Map<String, String> params = new HashMap<>();
private final Map<String, Part> parts = new HashMap<>();
+ private final Map<String, String> headers = new HashMap<>();
@Override
public String method() {
parts.put(key, new PartImpl(input, fileName));
return this;
}
+
+ @Override
+ public Optional<String> header(String name) {
+ return Optional.ofNullable(headers.get(name));
+ }
+
+ public FakeRequest setHeader(String name, String value) {
+ headers.put(name, value);
+ return this;
+ }
}
private static class FakeWs implements WebService {
assertThat(underTest.getParams()).containsOnly(entry("foo", "bar"), entry("fee", "beer"));
}
+
+ @Test
+ public void header_returns_empty_if_header_is_not_present() {
+ assertThat(underTest.header("foo")).isEmpty();
+ }
+
+ @Test
+ public void header_returns_value_of_header_if_present() {
+ underTest.setHeader("foo", "bar");
+ assertThat(underTest.header("foo")).hasValue("bar");
+ }
+
+ @Test
+ public void header_returns_empty_string_value_if_header_is_present_without_value() {
+ underTest.setHeader("foo", "");
+ assertThat(underTest.header("foo")).hasValue("");
+ }
}
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.ListMultimap;
import java.util.Collection;
+import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Strings.isNullOrEmpty;
import static java.util.Collections.singletonList;
+import static java.util.Collections.unmodifiableSet;
import static java.util.Objects.requireNonNull;
abstract class BaseRequest<SELF extends BaseRequest> implements WsRequest {
private String mediaType = MediaTypes.JSON;
private final DefaultParameters parameters = new DefaultParameters();
+ private final DefaultHeaders headers = new DefaultHeaders();
BaseRequest(String path) {
this.path = path;
return parameters;
}
+ @Override
+ public Headers getHeaders() {
+ return headers;
+ }
+
+ public SELF setHeader(String name, @Nullable String value) {
+ requireNonNull(name, "Header name can't be null");
+ headers.setValue(name, value);
+ return (SELF) this;
+ }
+
private static class DefaultParameters implements Parameters {
// preserve insertion order
private final ListMultimap<String, String> keyValues = LinkedListMultimap.create();
return this;
}
}
+
+ private static class DefaultHeaders implements Headers {
+ private final Map<String, String> keyValues = new HashMap<>();
+
+ @Override
+ public Optional<String> getValue(String name) {
+ return Optional.ofNullable(keyValues.get(name));
+ }
+
+ private DefaultHeaders setValue(String name, @Nullable String value) {
+ checkArgument(!isNullOrEmpty(name));
+
+ if (value == null) {
+ keyValues.remove(name);
+ } else {
+ keyValues.put(name, value);
+ }
+ return this;
+ }
+
+ @Override
+ public Set<String> getNames() {
+ return unmodifiableSet(keyValues.keySet());
+ }
+ }
}
--- /dev/null
+/*
+ * 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.util.Optional;
+import java.util.Set;
+
+/**
+ * HTTP headers
+ *
+ * @since 6.6
+ */
+public interface Headers {
+
+ Optional<String> getValue(String name);
+
+ Set<String> getNames();
+
+}
private Request.Builder prepareOkRequestBuilder(WsRequest getRequest, HttpUrl.Builder urlBuilder) {
Request.Builder okHttpRequestBuilder = new Request.Builder()
.url(urlBuilder.build())
- .addHeader("Accept", getRequest.getMediaType())
- .addHeader("Accept-Charset", "UTF-8");
+ .header("Accept", getRequest.getMediaType())
+ .header("Accept-Charset", "UTF-8");
if (credentials != null) {
okHttpRequestBuilder.header("Authorization", credentials);
}
+ getRequest.getHeaders().getNames().forEach(name ->
+ okHttpRequestBuilder.header(name, getRequest.getHeaders().getValue(name).get()));
return okHttpRequestBuilder;
}
/**
* Private since 5.5.
+ *
* @see HttpConnector#newBuilder()
*/
private Builder() {
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.List;
+import java.util.Optional;
import org.sonar.api.server.ws.LocalConnector;
import static java.nio.charset.StandardCharsets.UTF_8;
private static class DefaultLocalRequest implements LocalConnector.LocalRequest {
private final WsRequest wsRequest;
- private final Parameters parameters;
public DefaultLocalRequest(WsRequest wsRequest) {
this.wsRequest = wsRequest;
- this.parameters = wsRequest.getParameters();
}
@Override
@Override
public boolean hasParam(String key) {
- return !parameters.getValues(key).isEmpty();
+ return !wsRequest.getParameters().getValues(key).isEmpty();
}
@Override
public String getParam(String key) {
- return parameters.getValue(key);
+ return wsRequest.getParameters().getValue(key);
}
@Override
public List<String> getMultiParam(String key) {
- return parameters.getValues(key);
+ return wsRequest.getParameters().getValues(key);
+ }
+
+ @Override
+ public Optional<String> getHeader(String name) {
+ return wsRequest.getHeaders().getValue(name);
}
}
Parameters getParameters();
+ Headers getHeaders();
+
enum Method {
GET, POST
}
@Rule
public ExpectedException expectedException = ExpectedException.none();
- FakeRequest underTest = new FakeRequest("api/foo");
+ private FakeRequest underTest = new FakeRequest("api/foo");
@Test
public void test_defaults() {
underTest.setParam(null, "val");
}
+ @Test
+ public void headers_are_empty_by_default() {
+ assertThat(underTest.getHeaders().getNames()).isEmpty();
+ }
+
+ @Test
+ public void set_and_get_headers() {
+ underTest.setHeader("foo", "fooz");
+ underTest.setHeader("bar", "barz");
+
+ assertThat(underTest.getHeaders().getNames()).containsExactlyInAnyOrder("foo", "bar");
+ assertThat(underTest.getHeaders().getValue("foo")).hasValue("fooz");
+ assertThat(underTest.getHeaders().getValue("bar")).hasValue("barz");
+ assertThat(underTest.getHeaders().getValue("xxx")).isEmpty();
+ }
+
private void assertParameters(MapEntry<String, String>... values) {
Parameters parameters = underTest.getParameters();
assertThat(parameters.getKeys()).extracting(key -> MapEntry.entry(key, parameters.getValue(key))).containsExactly(values);
assertThat(recordedRequest.getHeader("Accept-Encoding")).isEqualTo("gzip");
}
+ @Test
+ public void add_headers_to_GET_request() throws Exception {
+ answerHelloWorld();
+ GetRequest request = new GetRequest("api/issues/search")
+ .setHeader("X-Foo", "fooz")
+ .setHeader("X-Bar", "barz");
+
+ underTest = HttpConnector.newBuilder().url(serverUrl).build();
+ underTest.call(request);
+
+ RecordedRequest recordedRequest = server.takeRequest();
+ assertThat(recordedRequest.getHeader("X-Foo")).isEqualTo("fooz");
+ assertThat(recordedRequest.getHeader("X-Bar")).isEqualTo("barz");
+ }
+
@Test
public void use_basic_authentication() throws Exception {
answerHelloWorld();
assertThat(recordedRequest.getMethod()).isEqualTo("POST");
assertThat(recordedRequest.getPath()).isEqualTo("/api/issues/search");
assertThat(recordedRequest.getBody().readUtf8()).isEqualTo("severity=MAJOR");
+ assertThat(recordedRequest.getHeader("Accept")).isEqualTo("application/x-protobuf");
+ }
+
+ @Test
+ public void add_header_to_POST_request() throws Exception {
+ answerHelloWorld();
+ PostRequest request = new PostRequest("api/issues/search")
+ .setHeader("X-Foo", "fooz")
+ .setHeader("X-Bar", "barz");
+
+ underTest = HttpConnector.newBuilder().url(serverUrl).build();
+ underTest.call(request);
+
+ RecordedRequest recordedRequest = server.takeRequest();
+ assertThat(recordedRequest.getHeader("X-Foo")).isEqualTo("fooz");
+ assertThat(recordedRequest.getHeader("X-Bar")).isEqualTo("barz");
}
@Test