| /* |
| * 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); |
| } |
| } |