Skip to content

Commit

Permalink
Start providing abstractions for consistent handling of HttpRequests
Browse files Browse the repository at this point in the history
The new grid server makes an effort not to differentiate between
local and remote execution. This is a nice model, and we should
extend it to other places in the code base.

This also provides an abstraction to start solving the problem of
wanting to wrap an HttpClient with methods that modify requests
or responses (eg. to add authentication headers).

Finally, we provide enough infrastructure to make providing
routes to various handlers easy to implement, allowing complicated
routing mechanisms to be built up.
  • Loading branch information
shs96c committed May 22, 2019
1 parent 2de17d6 commit abf2219
Show file tree
Hide file tree
Showing 17 changed files with 636 additions and 51 deletions.
4 changes: 2 additions & 2 deletions java/client/src/org/openqa/selenium/remote/http/Contents.java
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ public static String string(Supplier<InputStream> supplier, Charset charset) {
return new String(bytes(supplier), charset);
}

public static String string(HttpMessage message) {
public static String string(HttpMessage<?> message) {
return string(message.getContent(), message.getContentEncoding());
}

Expand All @@ -102,7 +102,7 @@ public static Reader reader(Supplier<InputStream> supplier, Charset charset) {
return new InputStreamReader(supplier.get(), charset);
}

public static Reader reader(HttpMessage message) {
public static Reader reader(HttpMessage<?> message) {
return reader(message.getContent(), message.getContentEncoding());
}

Expand Down
51 changes: 51 additions & 0 deletions java/client/src/org/openqa/selenium/remote/http/Filter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// 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.http;

import java.util.function.Function;

/**
* Can be wrapped around an {@link HttpHandler} in order to either modify incoming
* {@link HttpRequest}s or outgoing {@link HttpResponse}s using the well-known "Filter" pattern.
* This is very similar to the Servlet spec's {@link javax.servlet.Filter}, but takes advantage of
* lambdas:
* <pre>{@code
* Filter filter = next -> {
* return req -> {
* req.addHeader("cheese", "brie");
* HttpResponse res = next.apply(req);
* res.addHeader("vegetable", "peas");
* return res;
* };
* }
* }</pre>
*
*<p>Because each filter returns an {@link HttpHandler}, it's easy to do processing before, or after
* each request, as well as short-circuit things if necessary.
*/
@FunctionalInterface
public interface Filter extends Function<HttpHandler, HttpHandler> {

default Filter andThen(Filter next) {
return req -> apply(next.apply(req));
}

default HttpHandler andFinally(HttpHandler end) {
return request -> Filter.this.apply(end).apply(request);
}
}
28 changes: 28 additions & 0 deletions java/client/src/org/openqa/selenium/remote/http/HttpHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// 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.http;

import java.util.function.Function;

@FunctionalInterface
public interface HttpHandler extends Function<HttpRequest, HttpResponse> {

default HttpHandler with(Filter filter) {
return filter.andFinally(this);
}
}
33 changes: 24 additions & 9 deletions java/client/src/org/openqa/selenium/remote/http/HttpMessage.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import static org.openqa.selenium.remote.http.Contents.string;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import com.google.common.net.MediaType;

Expand All @@ -37,7 +38,7 @@
import java.util.function.Supplier;
import java.util.stream.Collectors;

class HttpMessage {
class HttpMessage<M extends HttpMessage<M>> {

private final Multimap<String, String> headers = ArrayListMultimap.create();
private final Map<String, Object> attributes = new HashMap<>();
Expand All @@ -54,12 +55,18 @@ public Object getAttribute(String key) {
return attributes.get(key);
}

public void setAttribute(String key, Object value) {
public M setAttribute(String key, Object value) {
attributes.put(key, value);
return self();
}

public void removeAttribute(String key) {
public M removeAttribute(String key) {
attributes.remove(key);
return self();
}

public Iterable<String> getAttributeNames() {
return ImmutableSet.copyOf(attributes.keySet());
}

public Iterable<String> getHeaderNames() {
Expand Down Expand Up @@ -87,17 +94,18 @@ public String getHeader(String name) {
return null;
}

public void setHeader(String name, String value) {
removeHeader(name);
addHeader(name, value);
public M setHeader(String name, String value) {
return removeHeader(name).addHeader(name, value);
}

public void addHeader(String name, String value) {
public M addHeader(String name, String value) {
headers.put(name, value);
return self();
}

public void removeHeader(String name) {
public M removeHeader(String name) {
headers.removeAll(name);
return self();
}

public Charset getContentEncoding() {
Expand Down Expand Up @@ -130,8 +138,9 @@ public void setContent(InputStream toStreamFrom) {
setContent(() -> toStreamFrom);
}

public void setContent(Supplier<InputStream> supplier) {
public M setContent(Supplier<InputStream> supplier) {
this.content = Objects.requireNonNull(supplier, "Supplier must be set.");
return self();
}

public Supplier<InputStream> getContent() {
Expand Down Expand Up @@ -167,8 +176,14 @@ public InputStream getContentStream() {
* again.
* @deprecated No direct replacement. Use {@link #getContent()} and call {@link Supplier#get()}.
*/
@Deprecated
public InputStream consumeContentStream() {
return getContent().get();
}

@SuppressWarnings("unchecked")
private M self() {
return (M) this;
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
import java.util.Iterator;
import java.util.Objects;

public class HttpRequest extends HttpMessage {
public class HttpRequest extends HttpMessage<HttpRequest> {

private final HttpMethod method;
private final String uri;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

import static java.net.HttpURLConnection.HTTP_OK;

public class HttpResponse extends HttpMessage {
public class HttpResponse extends HttpMessage<HttpResponse> {

public static final String HTTP_TARGET_HOST = "http.target.host";

Expand All @@ -29,17 +29,19 @@ public int getStatus() {
return status;
}

public void setStatus(int status) {
public HttpResponse setStatus(int status) {
this.status = status;
return this;
}

/**
* Sets the host this response was received from.
*
* @param host originating host
*/
public void setTargetHost(String host) {
public HttpResponse setTargetHost(String host) {
setAttribute(HTTP_TARGET_HOST, host);
return this;
}

/**
Expand Down
Loading

0 comments on commit abf2219

Please sign in to comment.