package io.dropwizard.jetty;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Joiner;
import io.dropwizard.util.Duration;
import io.dropwizard.validation.MinDuration;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlets.PushCacheFilter;
import javax.annotation.Nullable;
import javax.servlet.DispatcherType;
import javax.validation.constraints.Min;
import java.util.EnumSet;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* A factory for building HTTP/2 {@link PushCacheFilter},
* <p/>
* <b>Configuration Parameters:</b>
* <table>
* <tr>
* <td>Name</td>
* <td>Default</td>
* <td>Description</td>
* </tr>
* <tr>
* <td>{@code enabled}</td>
* <td>false</td>
* <td>
* If true, the filter will organize resources as primary resources (those referenced by the
* <i>Referer</i> header) and secondary resources (those that have the <i>Referer</i> header).
* Secondary resources that have been requested within a time window from the request of the
* primary resource will be associated with the it. The next time a client will
* request the primary resource, the server will send to the client the secondary resources
* along with the primary in a single response.
* </td>
* </tr>
* <tr>
* <td>{@code associatePeriod}</td>
* <td>4 seconds</td>
* <td>
* The time window within which a request for a secondary resource will be associated to a
* primary resource.
* </td>
* </tr>
* <tr>
* <td>{@code maxAssociations}</td>
* <td>16</td>
* <td>
* The maximum number of secondary resources that may be associated to a primary resource.
* </td>
* </tr>
* <tr>
* <td>{@code refererHosts}</td>
* <td>All hosts</td>
* <td>
* The list of referrer hosts for which the server push technology is supported.
* </td>
* </tr>
* <tr>
* <td>{@code refererPorts}</td>
* <td>All ports</td>
* <td>
* The list of referrer ports for which the server push technology is supported.
* </td>
* </tr>
* </table>
*/
public class ServerPushFilterFactory {
private static final Joiner COMMA_JOINER = Joiner.on(",");
private boolean enabled = false;
@MinDuration(value = 1, unit = TimeUnit.MILLISECONDS)
private Duration associatePeriod = Duration.seconds(4);
@Min(1)
private int maxAssociations = 16;
@Nullable
private List<String> refererHosts;
@Nullable
private List<Integer> refererPorts;
@JsonProperty
public boolean isEnabled() {
return enabled;
}
@JsonProperty
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
@JsonProperty
public Duration getAssociatePeriod() {
return associatePeriod;
}
@JsonProperty
public void setAssociatePeriod(Duration associatePeriod) {
this.associatePeriod = associatePeriod;
}
@JsonProperty
public int getMaxAssociations() {
return maxAssociations;
}
@JsonProperty
public void setMaxAssociations(int maxAssociations) {
this.maxAssociations = maxAssociations;
}
@Nullable
@JsonProperty
public List<String> getRefererHosts() {
return refererHosts;
}
@JsonProperty
public void setRefererHosts(@Nullable List<String> refererHosts) {
this.refererHosts = refererHosts;
}
@Nullable
@JsonProperty
public List<Integer> getRefererPorts() {
return refererPorts;
}
@JsonProperty
public void setRefererPorts(@Nullable List<Integer> refererPorts) {
this.refererPorts = refererPorts;
}
public void addFilter(ServletContextHandler handler) {
if (!enabled) {
return;
}
handler.setInitParameter("associatePeriod", String.valueOf(associatePeriod.toMilliseconds()));
handler.setInitParameter("maxAssociations", String.valueOf(maxAssociations));
if (refererHosts != null) {
handler.setInitParameter("hosts", COMMA_JOINER.join(refererHosts));
}
if (refererPorts != null) {
handler.setInitParameter("ports", COMMA_JOINER.join(refererPorts));
}
handler.addFilter(PushCacheFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
}
}