Permalink
Please sign in to comment.
Browse files
Provide a pure JRE implementation of HttpClient
So we can avoid having to take a dependency on Apache HttpClient.
- Loading branch information...
Showing
with
219 additions
and 0 deletions.
| @@ -0,0 +1,114 @@ | ||
| // Licensed to the Software Freedom Conservancy (SFC) under one | ||
| // or more contributor license agreements. See the NOTICE file | ||
| // distributed with this work for additional information | ||
| // regarding copyright ownership. The SFC licenses this file | ||
| // to you under the Apache License, Version 2.0 (the | ||
| // "License"); you may not use this file except in compliance | ||
| // with the License. You may obtain a copy of the License at | ||
| // | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, | ||
| // software distributed under the License is distributed on an | ||
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
| // KIND, either express or implied. See the License for the | ||
| // specific language governing permissions and limitations | ||
| // under the License. | ||
| package org.openqa.selenium.remote.internal; | ||
| import org.openqa.selenium.remote.http.HttpClient; | ||
| import org.openqa.selenium.remote.http.HttpMethod; | ||
| import org.openqa.selenium.remote.http.HttpRequest; | ||
| import org.openqa.selenium.remote.http.HttpResponse; | ||
| import java.io.ByteArrayOutputStream; | ||
| import java.io.IOException; | ||
| import java.io.InputStream; | ||
| import java.io.OutputStream; | ||
| import java.net.HttpURLConnection; | ||
| import java.net.URL; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
| import java.util.Objects; | ||
| public class JreHttpClient implements HttpClient { | ||
| private final URL url; | ||
| private JreHttpClient(URL url) { | ||
| if (!url.getProtocol().toLowerCase().startsWith("http")) { | ||
| throw new IllegalArgumentException("Base URL must be an http URL: " + url); | ||
| } | ||
| this.url = url; | ||
| } | ||
| @Override | ||
| public HttpResponse execute(HttpRequest request, boolean followRedirects) throws IOException { | ||
| URL url = new URL(this.url.toString() + request.getUri()); | ||
| HttpURLConnection connection = (HttpURLConnection) url.openConnection(); | ||
| try { | ||
| connection.setInstanceFollowRedirects(followRedirects); | ||
| for (String name : request.getHeaderNames()) { | ||
| for (String value : request.getHeaders(name)) { | ||
| connection.addRequestProperty(name, value); | ||
| } | ||
| } | ||
| connection.setRequestProperty("Content-length", String.valueOf(request.getContent().length)); | ||
| connection.setRequestMethod(request.getMethod().toString()); | ||
| connection.setDoInput(true); | ||
| if (request.getMethod() == HttpMethod.POST) { | ||
| connection.setDoOutput(true); | ||
| try (OutputStream os = connection.getOutputStream()) { | ||
| os.write(request.getContent()); | ||
| } | ||
| } | ||
| HttpResponse response = new HttpResponse(); | ||
| response.setStatus(connection.getResponseCode()); | ||
| for (Map.Entry<String, List<String>> entry : connection.getHeaderFields().entrySet()) { | ||
| // HttpURLConnection stores headers in a HashMap. The order they come back is basically | ||
| // random. | ||
| for (String value : entry.getValue()) { | ||
| response.addHeader(entry.getKey(), value); | ||
| } | ||
| } | ||
| InputStream is = connection.getErrorStream(); | ||
| if (is == null) { | ||
| is = connection.getInputStream(); | ||
| } | ||
| try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) { | ||
| int count; | ||
| byte[] data = new byte[1024]; | ||
| while ((count = is.read(data, 0, data.length)) != -1) { | ||
| bos.write(data, 0, count); | ||
| } | ||
| bos.flush(); | ||
| response.setContent(bos.toByteArray()); | ||
| } finally { | ||
| is.close(); | ||
| } | ||
| return response; | ||
| } finally { | ||
| connection.disconnect(); | ||
| } | ||
| } | ||
| @Override | ||
| public void close() throws IOException { | ||
| } | ||
| public static class Factory implements HttpClient.Factory { | ||
| @Override | ||
| public HttpClient createClient(URL url) { | ||
| return new JreHttpClient(Objects.requireNonNull(url, "Base URL must be set")); | ||
| } | ||
| } | ||
| } |
| @@ -0,0 +1,105 @@ | ||
| // Licensed to the Software Freedom Conservancy (SFC) under one | ||
| // or more contributor license agreements. See the NOTICE file | ||
| // distributed with this work for additional information | ||
| // regarding copyright ownership. The SFC licenses this file | ||
| // to you under the Apache License, Version 2.0 (the | ||
| // "License"); you may not use this file except in compliance | ||
| // with the License. You may obtain a copy of the License at | ||
| // | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, | ||
| // software distributed under the License is distributed on an | ||
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
| // KIND, either express or implied. See the License for the | ||
| // specific language governing permissions and limitations | ||
| // under the License. | ||
| package org.openqa.selenium.remote.internal; | ||
| import static org.junit.Assert.assertEquals; | ||
| import static org.junit.Assert.assertTrue; | ||
| import com.google.common.collect.HashMultimap; | ||
| import com.google.common.collect.ImmutableList; | ||
| import com.google.common.collect.Multimap; | ||
| import org.junit.Test; | ||
| import org.openqa.selenium.net.PortProber; | ||
| import org.openqa.selenium.remote.http.HttpClient; | ||
| import org.openqa.selenium.remote.http.HttpMethod; | ||
| import org.openqa.selenium.remote.http.HttpRequest; | ||
| import org.openqa.selenium.remote.http.HttpResponse; | ||
| import org.seleniumhq.jetty9.server.Server; | ||
| import org.seleniumhq.jetty9.servlet.ServletContextHandler; | ||
| import org.seleniumhq.jetty9.servlet.ServletHolder; | ||
| import java.io.IOException; | ||
| import javax.servlet.ServletException; | ||
| import javax.servlet.http.HttpServlet; | ||
| import javax.servlet.http.HttpServletRequest; | ||
| import javax.servlet.http.HttpServletResponse; | ||
| public class JreHttpClientTest { | ||
| @Test | ||
| public void responseShouldCaptureASingleHeader() throws Exception { | ||
| HashMultimap<String, String> headers = HashMultimap.create(); | ||
| headers.put("Cake", "Delicious"); | ||
| HttpResponse response = getResponseWithHeaders(headers); | ||
| String value = response.getHeader("Cake"); | ||
| assertEquals("Delicious", value); | ||
| } | ||
| /** | ||
| * The HTTP Spec that it should be | ||
| * <a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2">safe to combine them | ||
| * </a>, but things like the <a href="https://www.ietf.org/rfc/rfc2109.txt">cookie spec</a> make | ||
| * this hard (notably when a legal value may contain a comma). | ||
| */ | ||
| @Test | ||
| public void responseShouldKeepMultipleHeadersSeparate() throws Exception { | ||
| HashMultimap<String, String> headers = HashMultimap.create(); | ||
| headers.put("Cheese", "Cheddar"); | ||
| headers.put("Cheese", "Brie, Gouda"); | ||
| HttpResponse response = getResponseWithHeaders(headers); | ||
| ImmutableList<String> values = ImmutableList.copyOf(response.getHeaders("Cheese")); | ||
| assertTrue(values.contains("Cheddar")); | ||
| assertTrue(values.contains("Brie, Gouda")); | ||
| } | ||
| private HttpResponse getResponseWithHeaders(final Multimap<String, String> headers) | ||
| throws Exception { | ||
| Server server = new Server(PortProber.findFreePort()); | ||
| ServletContextHandler handler = new ServletContextHandler(); | ||
| handler.setContextPath(""); | ||
| class Headers extends HttpServlet { | ||
| @Override | ||
| protected void doGet(HttpServletRequest req, HttpServletResponse resp) | ||
| throws ServletException, IOException { | ||
| headers.forEach(resp::addHeader); | ||
| resp.setContentLengthLong(0); | ||
| } | ||
| } | ||
| ServletHolder holder = new ServletHolder(new Headers()); | ||
| handler.addServlet(holder, "/*"); | ||
| server.setHandler(handler); | ||
| server.start(); | ||
| try { | ||
| HttpClient client = new JreHttpClient.Factory().createClient(server.getURI().toURL()); | ||
| HttpRequest request = new HttpRequest(HttpMethod.GET, "/foo"); | ||
| return client.execute(request, true); | ||
| } finally { | ||
| server.stop(); | ||
| } | ||
| } | ||
| } |
0 comments on commit
c74e42e