/* * Copyright 2016 Netflix, Inc. * * 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 io.reactivex.netty.protocol.http.client.internal; import io.netty.handler.codec.http.DefaultHttpRequest; import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpUtil; import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.LastHttpContent; import io.netty.handler.codec.http.cookie.ClientCookieEncoder; import io.netty.handler.codec.http.cookie.Cookie; import io.reactivex.netty.channel.Connection; import io.reactivex.netty.channel.FlushSelectorOperator; import rx.Observable; import rx.functions.Func1; import java.util.Date; import java.util.Map; import java.util.Map.Entry; public final class RawRequest<I, O> { private final Redirector<I, O> redirector; private final HttpRequest headers; @SuppressWarnings("rawtypes") private final Observable content; private final Func1<?, Boolean> flushSelector; private final boolean hasTrailers; @SuppressWarnings("rawtypes") private RawRequest(HttpRequest headers, Observable content, Func1<?, Boolean> flushSelector, boolean hasTrailers, Redirector<I, O> redirector) { this.headers = headers; this.content = content; this.flushSelector = flushSelector; this.hasTrailers = hasTrailers; this.redirector = redirector; } public RawRequest<I, O> addHeader(CharSequence name, Object value) { HttpRequest headersCopy = _copyHeaders(); headersCopy.headers().add(name, value); return new RawRequest<>(headersCopy, content, flushSelector, hasTrailers, redirector); } public RawRequest<I, O> addHeaders(Map<? extends CharSequence, ? extends Iterable<Object>> headers) { HttpRequest headersCopy = _copyHeaders(); for (Entry<? extends CharSequence, ? extends Iterable<Object>> header : headers.entrySet()) { headersCopy.headers().add(header.getKey(), header.getValue()); } return new RawRequest<>(headersCopy, content, flushSelector, hasTrailers, redirector); } public RawRequest<I, O> addHeaderValues(CharSequence name, Iterable<Object> values) { HttpRequest headersCopy = _copyHeaders(); headersCopy.headers().add(name, values); return new RawRequest<>(headersCopy, content, flushSelector, hasTrailers, redirector); } public RawRequest<I, O> addCookie(Cookie cookie) { String cookieHeader = ClientCookieEncoder.STRICT.encode(cookie); return addHeader(HttpHeaderNames.COOKIE, cookieHeader); } public RawRequest<I, O> addDateHeader(CharSequence name, Date value) { HttpRequest headersCopy = _copyHeaders(); headersCopy.headers().add(name, value); return new RawRequest<>(headersCopy, content, flushSelector, hasTrailers, redirector); } public RawRequest<I, O> addDateHeader(CharSequence name, Iterable<Date> values) { HttpRequest headersCopy = _copyHeaders(); for (Date value : values) { headersCopy.headers().add(name, value); } return new RawRequest<>(headersCopy, content, flushSelector, hasTrailers, redirector); } public RawRequest<I, O> setDateHeader(CharSequence name, Date value) { HttpRequest headersCopy = _copyHeaders(); headersCopy.headers().set(name, value); return new RawRequest<>(headersCopy, content, flushSelector, hasTrailers, redirector); } public RawRequest<I, O> setHeader(CharSequence name, Object value) { HttpRequest headersCopy = _copyHeaders(); headersCopy.headers().set(name, value); return new RawRequest<>(headersCopy, content, flushSelector, hasTrailers, redirector); } public RawRequest<I, O> setHeaders(Map<? extends CharSequence, ? extends Iterable<Object>> headers) { HttpRequest headersCopy = _copyHeaders(); for (Entry<? extends CharSequence, ? extends Iterable<Object>> header : headers.entrySet()) { headersCopy.headers().set(header.getKey(), header.getValue()); } return new RawRequest<>(headersCopy, content, flushSelector, hasTrailers, redirector); } public RawRequest<I, O> setHeaderValues(CharSequence name, Iterable<Object> values) { HttpRequest headersCopy = _copyHeaders(); headersCopy.headers().set(name, values); return new RawRequest<>(headersCopy, content, flushSelector, hasTrailers, redirector); } public RawRequest<I, O> setDateHeader(CharSequence name, Iterable<Date> values) { HttpRequest headersCopy = _copyHeaders(); boolean addNow = false; for (Date value : values) { if (addNow) { headersCopy.headers().add(name, value); } else { headersCopy.headers().set(name, value); addNow = true; } } return new RawRequest<>(headersCopy, content, flushSelector, hasTrailers, redirector); } public RawRequest<I, O> setKeepAlive(boolean keepAlive) { HttpRequest headersCopy = _copyHeaders(); HttpUtil.setKeepAlive(headersCopy, keepAlive); return new RawRequest<>(headersCopy, content, flushSelector, hasTrailers, redirector); } public RawRequest<I, O> setTransferEncodingChunked() { HttpRequest headersCopy = _copyHeaders(); HttpUtil.setTransferEncodingChunked(headersCopy, true); return new RawRequest<>(headersCopy, content, flushSelector, hasTrailers, redirector); } public RawRequest<I, O> removeHeader(CharSequence name) { HttpRequest headersCopy = _copyHeaders(); headersCopy.headers().remove(name); return new RawRequest<>(headersCopy, content, flushSelector, hasTrailers, redirector); } public RawRequest<I, O> setMethod(HttpMethod method) { HttpRequest headersCopy = _copyHeaders(headers.uri(), method); return new RawRequest<>(headersCopy, content, flushSelector, hasTrailers, redirector); } public RawRequest<I, O> setUri(String uri) { HttpRequest headersCopy = _copyHeaders(uri, headers.method()); return new RawRequest<>(headersCopy, content, flushSelector, hasTrailers, redirector); } public RawRequest<I, O> followRedirect(Redirector<I, O> redirectHandler) { return new RawRequest<>(headers, content, flushSelector, hasTrailers, redirectHandler); } @SuppressWarnings({"rawtypes", "unchecked"}) public Observable asObservable(Connection<?, ?> connection) { HttpRequest headers = this.headers; if (null == content) { headers = _copyHeaders(); headers.headers().set(HttpHeaderNames.CONTENT_LENGTH, 0); } Observable toReturn = Observable.just(headers); if (null != content) { if (null == flushSelector) { toReturn = toReturn.concatWith(content); } else { toReturn = toReturn.concatWith(content.lift(new FlushSelectorOperator(flushSelector, connection))); } } if (!hasTrailers) { toReturn = toReturn.concatWith(Observable.just(LastHttpContent.EMPTY_LAST_CONTENT)); } return toReturn; } private HttpRequest _copyHeaders() { return _copyHeaders(headers.uri(), headers.method()); } private HttpRequest _copyHeaders(String uri, HttpMethod method) { final HttpRequest newHeaders = new DefaultHttpRequest(headers.protocolVersion(), method, uri); // TODO: May be we can optimize this by not copying for (Entry<String, String> header : headers.headers()) { newHeaders.headers().set(header.getKey(), header.getValue()); } return newHeaders; } public static <I, O> RawRequest<I, O> create(HttpVersion version, HttpMethod httpMethod, String uri, Redirector<I, O> redirectHandler) { final HttpRequest headers = new DefaultHttpRequest(version, httpMethod, uri); return create(headers, null, null, false, redirectHandler); } @SuppressWarnings("rawtypes") public static <I, O> RawRequest<I, O> create(HttpRequest headers, Observable content, boolean hasTrailers, Redirector<I, O> redirectHandler) { return create(headers, content, null, hasTrailers, redirectHandler); } @SuppressWarnings("rawtypes") public static <I, O> RawRequest<I, O> create(HttpRequest headers, Observable content, Func1<?, Boolean> flushSelector, boolean hasTrailers, Redirector<I, O> redirectHandler) { return new RawRequest<>(headers, content, flushSelector, hasTrailers, redirectHandler); } public HttpRequest getHeaders() { return headers; } @SuppressWarnings("rawtypes") public Observable getContent() { return content; } public Func1<?, Boolean> getFlushSelector() { return flushSelector; } public boolean hasTrailers() { return hasTrailers; } public Redirector<I, O> getRedirector() { return redirector; } }