/** * @param headerName case-insensitive header name * @return the first value of the header, if any. */ public Optional<String> getFirst(String headerName) { List<String> values = get(headerName); if (values.isEmpty()) { return Optional.empty(); } else { return Optional.of(values.get(0)); } }
private static Charset extractCharset(RawHttpHeaders extensions) { String charset = extensions.getFirst("Charset").orElse("UTF-8"); try { return Charset.forName(charset); } catch (Exception e) { return StandardCharsets.UTF_8; } }
private static Builder newBuilder(RawHttpHeaders headers, boolean validateHeaders) { Builder builder = new Builder(validateHeaders); for (Map.Entry<String, Header> entry : headers.headersByCapitalizedName.entrySet()) { builder.headersByCapitalizedName.put(entry.getKey(), entry.getValue().unfreeze()); } builder.headerNames.addAll(headers.getHeaderNames()); return builder; }
private static RawHttpHeaders withPlainTextExtension(RawHttpHeaders extensions) { RawHttpHeaders result = extensions.and(PLAIN_TEXT_HEADER); Optional<String> charset = result.getFirst("Charset"); if (!charset.isPresent() || !Charset.isSupported(charset.get())) { result = result.and(UTF8_HEADER); } return result; }
request.getHeaders().getFirst("Accept").orElse("")); response.getHeaders().getFirst("Content-Type").orElse("")); response.getHeaders().get("Content-Type")); request.getHeaders().getHeaderNames());
private RawHttpHeaders readHeaders(CloseableHttpResponse response) { Header[] allHeaders = response.getAllHeaders(); RawHttpHeaders.Builder headers = RawHttpHeaders.newBuilder(); for (Header header : allHeaders) { String meta = header.getElements().length > 0 ? ";" + Arrays.stream(header.getElements()) .flatMap(it -> Arrays.stream(it.getParameters()).map(v -> v.getName() + "=" + v.getValue())) .collect(joining(";")) : ""; headers.with(header.getName(), header.getValue() + meta); } return headers.build(); }
public RawHttpResponse<CloseableHttpResponse> send(RawHttpRequest request) throws IOException { RequestBuilder builder = RequestBuilder.create(request.getMethod()); builder.setUri(request.getUri()); builder.setVersion(toProtocolVersion(request.getStartLine().getHttpVersion())); request.getHeaders().getHeaderNames().forEach((name) -> request.getHeaders().get(name).forEach(value -> builder.addHeader(new BasicHeader(name, value)))); request.getBody().ifPresent(b -> builder.setEntity(new InputStreamEntity(b.asRawStream()))); CloseableHttpResponse response = httpClient.execute(builder.build()); RawHttpHeaders headers = readHeaders(response); StatusLine statusLine = adaptStatus(response.getStatusLine()); @Nullable LazyBodyReader body; if (response.getEntity() != null) { FramedBody framedBody = http.getFramedBody(statusLine, headers); body = new LazyBodyReader(framedBody, response.getEntity().getContent()); } else { body = null; } return new RawHttpResponse<>(response, request, statusLine, headers, body); }
/** * Create a new set of headers, adding/replacing the provided headers into this instance. * <p> * Multi-valued headers present in both this and the provided headers are not merged. The provided headers * are guaranteed to be present and have the same values in the returned instance. * * @param headers to add or replace on this. * @return new set of headers containing both this instance's values as well as the provided values */ public RawHttpHeaders and(RawHttpHeaders headers) { Builder builder = RawHttpHeaders.newBuilderSkippingValidation(this); Set<String> visitedNames = new HashSet<>(headers.headerNames.size()); headers.forEach((name, value) -> { String key = toUppercaseAscii(name); boolean isNewKey = visitedNames.add(key); if (isNewKey) { builder.overwrite(name, value); } else { builder.with(name, value); } }); return builder.build(); }
.map(ChunkedBodyContents::getTrailerHeaders) .orElse(emptyRawHttpHeaders()); if (trailingHeaders.isEmpty()) { headers = request.getHeaders(); } else { headers = RawHttpHeaders.newBuilder(request.getHeaders()) .merge(trailingHeaders) .build();
/** * @return the trailer-part of the chunked body. * This method should only be called after the full body has been read. Before that, it will * just return an empty set of headers. */ public RawHttpHeaders getTrailer() { return trailer == null ? RawHttpHeaders.empty() : trailer; } }
/** * Merge this builder's headers with the ones provided. * * @param headers to merge with this builder * @return this */ public Builder merge(RawHttpHeaders headers) { headers.forEach(this::with); return this; }
/** * @return new instance of {@link RawHttpHeaders} with all headers added to this builder. */ public RawHttpHeaders build() { return new RawHttpHeaders(headersByCapitalizedName, headerNames, true); }
/** * Determines whether a request with the given headers should have a body. * * @param headers HTTP request's headers * @return true if the headers indicate the request should have a body, false otherwise */ public static boolean requestHasBody(RawHttpHeaders headers) { // The presence of a message body in a request is signaled by a // Content-Length or Transfer-Encoding header field. Request message // framing is independent of method semantics, even if the method does // not define any use for a message body. return headers.contains("Content-Length") || headers.contains("Transfer-Encoding"); }
private static RawHttpHeaders contentTypeHeaderWithValue(String value) { return RawHttpHeaders.newBuilder() .with("Content-Type", value) .build(); }
/** * Create a new set of headers, adding/replacing the provided headers into this instance. * <p> * Multi-valued headers present in both this and the provided headers are not merged. The provided headers * are guaranteed to be present and have the same values in the returned instance. * * @param headers to add or replace on this. * @return new set of headers containing both this instance's values as well as the provided values */ public RawHttpHeaders and(RawHttpHeaders headers) { Builder builder = RawHttpHeaders.newBuilderSkippingValidation(this); Set<String> visitedNames = new HashSet<>(headers.headerNames.size()); headers.forEach((name, value) -> { String key = toUppercaseAscii(name); boolean isNewKey = visitedNames.add(key); if (isNewKey) { builder.overwrite(name, value); } else { builder.with(name, value); } }); return builder.build(); }
.map(ChunkedBodyContents::getTrailerHeaders) .orElse(emptyRawHttpHeaders()); if (trailingHeaders.isEmpty()) { headers = request.getHeaders(); } else { headers = RawHttpHeaders.newBuilder(request.getHeaders()) .merge(trailingHeaders) .build();
/** * @return the trailer-part of the chunked body. * This method should only be called after the full body has been read. Before that, it will * just return an empty set of headers. */ public RawHttpHeaders getTrailer() { return trailer == null ? RawHttpHeaders.empty() : trailer; } }
/** * Merge this builder's headers with the ones provided. * * @param headers to merge with this builder * @return this */ public Builder merge(RawHttpHeaders headers) { headers.forEach(this::with); return this; }