blob: de07a0504f5ffd159a03b5dd06a23dacbbadb965 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.jclouds.http;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.jclouds.http.Uris.uriBuilder;
import static org.jclouds.http.utils.Queries.queryParser;
import static org.jclouds.io.Payloads.newUrlEncodedFormPayload;
import java.net.URI;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jclouds.io.Payload;
import org.jclouds.javax.annotation.Nullable;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.base.MoreObjects.ToStringHelper;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
/**
* Represents a request that can be executed within {@link HttpCommandExecutorService}
*/
public class HttpRequest extends HttpMessage {
public static final Set<String> NON_PAYLOAD_METHODS = ImmutableSet
.of("OPTIONS", "GET", "HEAD", "DELETE", "TRACE", "CONNECT");
public static Builder<?> builder() {
return new ConcreteBuilder();
}
public Builder<?> toBuilder() {
return new ConcreteBuilder().fromHttpRequest(this);
}
public abstract static class Builder<T extends Builder<T>> extends HttpMessage.Builder<T> {
protected String method;
protected URI endpoint;
protected ImmutableList.Builder<HttpRequestFilter> filters = ImmutableList.<HttpRequestFilter>builder();
/**
* @see HttpRequest#getMethod()
*/
public T method(String method) {
this.method = checkNotNull(method, "method");
return self();
}
/**
* @see HttpRequest#getEndpoint()
*/
public T endpoint(URI endpoint) {
this.endpoint = checkNotNull(endpoint, "endpoint");
return self();
}
/**
* @see HttpRequest#getEndpoint()
*/
public T endpoint(String endpoint) {
return endpoint(URI.create(checkNotNull(endpoint, "endpoint")));
}
/**
* @see HttpRequest#getEndpoint()
*/
public T addQueryParam(String name, Iterable<String> values) {
endpoint = uriBuilder(endpoint).addQuery(name, values).build();
return self();
}
/**
* @see HttpRequest#getEndpoint()
*/
public T addQueryParam(String name, String... values) {
endpoint = uriBuilder(endpoint).addQuery(name, values).build();
return self();
}
/**
* @see HttpRequest#getEndpoint()
*/
public T addQueryParams(Multimap<String, String> parameters) {
endpoint = uriBuilder(endpoint).addQuery(parameters).build();
return self();
}
/**
* @see HttpRequest#getEndpoint()
*/
public T replaceQueryParam(String name, Iterable<String> values) {
endpoint = uriBuilder(endpoint).replaceQuery(name, values).build();
return self();
}
/**
* @see HttpRequest#getEndpoint()
*/
public T replaceQueryParam(String name, String... values) {
endpoint = uriBuilder(endpoint).replaceQuery(name, values).build();
return self();
}
/**
* @see HttpRequest#getEndpoint()
*/
public T replaceQueryParams(Map<String, String> parameters) {
return replaceQueryParams(Multimaps.forMap(parameters));
}
/**
* @see HttpRequest#getEndpoint()
*/
public T replaceQueryParams(Multimap<String, String> parameters) {
endpoint = uriBuilder(endpoint).replaceQuery(parameters).build();
return self();
}
/**
* @see HttpRequest#getEndpoint()
*/
public T replacePath(String path) {
checkNotNull(endpoint, "endpoint");
checkNotNull(path, "path");
endpoint = uriBuilder(endpoint).path(path).build();
return self();
}
/**
* @see #addFormParams
*/
public T addFormParam(String name, String... values) {
return addFormParams(ImmutableMultimap.<String, String> builder()
.putAll(checkNotNull(name, "name"), checkNotNull(values, "values of %s", name)).build());
}
/**
* Replaces the current payload with one that is a urlencoded payload including the following
* parameters and any formerly set.
*
* @see HttpRequest#getPayload()
*/
public T addFormParams(Multimap<String, String> parameters) {
checkNotNull(endpoint, "endpoint");
Multimap<String, String> map = payload != null ? queryParser().apply(payload.getRawContent().toString())
: LinkedHashMultimap.<String, String> create();
map.putAll(parameters);
payload = newUrlEncodedFormPayload(map);
return self();
}
/**
* @see #replaceFormParams
*/
public T replaceFormParam(String name, String... values) {
return replaceFormParams(ImmutableMultimap.<String, String> builder()
.putAll(checkNotNull(name, "name"), checkNotNull(values, "values of %s", name)).build());
}
/**
* Replaces the current payload with one that is a urlencoded payload including the following
* parameters and any formerly set.
*
* @see HttpRequest#getPayload()
*/
public T replaceFormParams(Multimap<String, String> parameters) {
checkNotNull(endpoint, "endpoint");
Multimap<String, String> map = payload != null ? queryParser().apply(payload.getRawContent().toString())
: LinkedHashMultimap.<String, String> create();
for (Map.Entry<String, Collection<String>> entry : parameters.asMap().entrySet()) {
map.replaceValues(entry.getKey(), entry.getValue());
}
payload = newUrlEncodedFormPayload(map);
return self();
}
/**
* @see HttpRequest#getFilters()
*/
public T filters(Iterable<HttpRequestFilter> filters) {
this.filters = ImmutableList.<HttpRequestFilter>builder();
this.filters.addAll(checkNotNull(filters, "filters"));
return self();
}
/**
* @see HttpRequest#getFilters()
*/
public T filter(HttpRequestFilter filter) {
this.filters.add(checkNotNull(filter, "filter"));
return self();
}
public HttpRequest build() {
return new HttpRequest(method, endpoint, headers.build(), payload, filters.build());
}
public T fromHttpRequest(HttpRequest in) {
return super.fromHttpMessage(in)
.method(in.getMethod())
.endpoint(in.getEndpoint())
.filters(in.getFilters());
}
}
private static class ConcreteBuilder extends Builder<ConcreteBuilder> {
@Override
protected ConcreteBuilder self() {
return this;
}
}
private final String method;
private final URI endpoint;
private final List<HttpRequestFilter> filters;
protected HttpRequest(String method, URI endpoint, Multimap<String, String> headers, @Nullable Payload payload,
Iterable<HttpRequestFilter> filters) {
super(headers, payload);
this.method = checkNotNull(method, "method");
this.endpoint = checkNotNull(endpoint, "endpoint");
checkArgument(endpoint.getHost() != null, "endpoint.getHost() is null for %s", endpoint);
this.filters = ImmutableList.<HttpRequestFilter> copyOf(checkNotNull(filters, "filters"));
}
public String getRequestLine() {
return String.format("%s %s HTTP/1.1", getMethod(), getEndpoint().toASCIIString());
}
/**
* We cannot return an enum, as per specification custom methods are allowed. Enums are not
* extensible.
*
* @see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.1" >rfc2616</a>
*/
public String getMethod() {
return method;
}
public URI getEndpoint() {
return endpoint;
}
public List<HttpRequestFilter> getFilters() {
return filters;
}
@Override
public int hashCode() {
return Objects.hashCode(method, endpoint, super.hashCode());
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
// testing equals by value, not by type
if (!(obj instanceof HttpRequest)) return false;
HttpRequest that = HttpRequest.class.cast(obj);
return super.equals(that) && Objects.equal(this.method, that.method)
&& Objects.equal(this.endpoint, that.endpoint);
}
@Override
protected ToStringHelper string() {
return MoreObjects.toStringHelper("").omitNullValues()
.add("method", method)
.add("endpoint", endpoint)
.add("headers", headers)
.add("payload", payload);
}
}