groovyx.net.http.FromServer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of http-builder-ng-core Show documentation
Show all versions of http-builder-ng-core Show documentation
Groovy client for making http requests.
The newest version!
/**
* Copyright (C) 2017 HttpBuilder-NG Project
*
* Licensed 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 groovyx.net.http;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.HttpCookie;
import java.net.URI;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.BiFunction;
import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME;
import static java.util.Arrays.stream;
import static java.util.Collections.unmodifiableList;
import static java.util.Collections.unmodifiableMap;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
/**
* Adapter interface used to provide a bridge for response data between the {@link HttpBuilder} API and the underlying client implementation.
*/
public interface FromServer {
public static final String DEFAULT_CONTENT_TYPE = "application/octet-stream";
/**
* Defines the interface to the HTTP headers contained in the response. (see also
* https://en.wikipedia.org/wiki/List_of_HTTP_header_fields[List of HTTP Header Fields])
*/
public static abstract class Header implements Map.Entry {
final String key;
final String value;
private T parsed;
protected static String key(final String raw) {
return raw.substring(0, raw.indexOf(':')).trim();
}
protected static String cleanQuotes(final String str) {
return str.startsWith("\"") ? str.substring(1, str.length() - 1) : str;
}
protected static String value(final String raw) {
return cleanQuotes(raw.substring(raw.indexOf(':') + 1).trim());
}
protected Header(final String key, final String value) {
this.key = key;
this.value = value;
}
/**
* Retrieves the header `key`.
*
* @return the header key
*/
public String getKey() {
return key;
}
/**
* Retrieves the header `value`.
*
* @return the header value
*/
public String getValue() {
return value;
}
/**
* Unsupported, headers are read-only.
*
* @throws UnsupportedOperationException always
*/
public String setValue(final String val) {
throw new UnsupportedOperationException();
}
@Override
public boolean equals(final Object o) {
if (!(o instanceof Header)) {
return false;
}
Header other = (Header) o;
return (Objects.equals(getKey(), other.getKey()) &&
Objects.equals(getValue(), other.getValue()));
}
@Override
public int hashCode() {
return Objects.hash(getKey(), getValue());
}
@Override
public String toString() {
return key + ": " + value;
}
/**
* Retrieves the parsed representation of the 'value`. The type of
* the returned `Object` depends on the header and will be given
* by the `getParsedType()` property.
*
* @return the parsed header value
*/
public T getParsed() {
if (parsed == null) {
this.parsed = parse();
}
return parsed;
}
/**
* Retrieves the type of the parsed representation of the 'value`.
*
* @return the parsed header value type
*/
public abstract Class> getParsedType();
/**
* Performs the parse of the `value`
*
* @return the parsed header value
*/
protected abstract T parse();
/**
* Creates a `Header` from a full header string. The string format is colon-delimited as `KEY:VALUE`.
*
* [source,groovy]
* ----
* Header header = Header.full('Content-Type:text/plain')
* assert header.key == 'Content-Type'
* assert header.value == 'text/plain'
* ----
*
* @param raw the full header string
* @return the `Header` representing the given header string
*/
public static Header> full(final String raw) {
return keyValue(key(raw), value(raw));
}
/**
* Creates a `Header` from a given `key` and `value`.
*
* @param key the header key
* @param value the header value
* @return the populated `Header`
*/
public static Header> keyValue(String key, String value) {
final BiFunction func = constructors.get(key);
return func == null ? new ValueOnly(key, value) : func.apply(key, value);
}
/**
* Used to find a specific `Header` by key from a {@link Collection} of `Header`s.
*
* @param headers the {@link Collection} of `Header`s to be searched
* @param key the key of the desired `Header`
* @return the `Header` with the matching key (or `null`)
*/
public static Header> find(final Collection> headers, final String key) {
return headers.stream().filter((h) -> h.getKey().equalsIgnoreCase(key)).findFirst().orElse(null);
}
/**
* Type representing headers that are simple key/values, with no parseable structure in the value. For example: `Accept-Ranges: bytes`.
*/
public static class ValueOnly extends Header {
public ValueOnly(final String key, final String value) {
super(key, value);
}
public String parse() {
return getValue();
}
/**
* Always returns {@link String}
*
* @return the parsed header type
*/
public Class> getParsedType() {
return String.class;
}
}
/**
* Type representing headers that have values which are parseable as key/value pairs,
* provided the header hey is included in the key/value map.
* For example: `Content-Type: text/html; charset=utf-8`
*/
public static class CombinedMap extends Header