/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://oss.oracle.com/licenses/CDDL+GPL-1.1
 * or LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package javax.servlet.http;

import java.util.Set;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/** 
 * Build a request to be pushed.
 * 
 * According section 8.2 of RFC 7540, a promised request must be cacheable and
 * safe without a request body.
 *
 * <p>A PushBuilder is obtained by calling {@link
 * HttpServletRequest#newPushBuilder()}.  Each call to this method will
 * a new instance of a PushBuilder based off the current {@code
 * HttpServletRequest}, or null.  Any mutations to the returned PushBuilder are
 * not reflected on future returns.</p>
 *
 * <p>The instance is initialized as follows:</p>
 *
 * <ul>
 *
 * <li>The method is initialized to "GET"</li>
 *
 * <li>The existing request headers of the current {@link HttpServletRequest}
 * are added to the builder, except for:
 *
 * <ul>
 *   <li>Conditional headers (defined in RFC 7232)
 *   <li>Range headers
 *   <li>Expect headers
 *   <li>Authorization headers
 *   <li>Referrer headers
 * </ul>
 *
 * </li>
 *
 * <li>If the request was authenticated, an Authorization header will 
 * be set with a container generated token that will result in equivalent
 * Authorization for the pushed request.</li>
 *
 * <li>The session ID will be the value returned from {@link
 * HttpServletRequest#getRequestedSessionId()}, unless {@link
 * HttpServletRequest#getSession(boolean)} has previously been called to
 * create a new {@link HttpSession} prior to the call to create the
 * {@code PushBuilder}, in which case the new session ID will be used as
 * the PushBuilder's requested session ID.  Note that the session ID
 * returned from the request can effectively come from one of two
 * "sources": a cookie or the URL (as specified in {@link
 * HttpServletRequest#isRequestedSessionIdFromCookie} and {@link
 * HttpServletRequest#isRequestedSessionIdFromURL}, respectively).  The
 * session ID for the {@code PushBuilder} will also come from the same
 * source as the request.</li>
 *
 * <li>The Referer(sic) header will be set to {@link
 * HttpServletRequest#getRequestURL()} plus any {@link
 * HttpServletRequest#getQueryString()} </li>
 *
 * <li>If {@link HttpServletResponse#addCookie(Cookie)} has been called
 * on the associated response, then a corresponding Cookie header will be added
 * to the PushBuilder, unless the {@link Cookie#getMaxAge()} is &lt;=0, in which
 * case the Cookie will be removed from the builder.</li>
 *
 * </ul> 
 *
 * <p>The {@link #path} method must be called on the {@code PushBuilder}
 * instance before the call to {@link #push}.  Failure to do so must
 * cause an exception to be thrown from {@link
 * #push}, as specified in that method.</p>
 * 
 * <p>A PushBuilder can be customized by chained calls to mutator
 * methods before the {@link #push()} method is called to initiate an
 * asynchronous push request with the current state of the builder.
 * After the call to {@link #push()}, the builder may be reused for
 * another push, however the implementation must make it so the {@link
 * #path(String)} and conditional headers (defined in RFC 7232) 
 * values are cleared before returning from {@link #push}.
 * All other values are retained over calls to {@link #push()}.
 *
 * @since Servlet 4.0
 */
public interface PushBuilder {
    /** 
     * <p>Set the method to be used for the push.</p>
     * 
     * @throws NullPointerException if the argument is {@code null}
     *
     * @throws IllegalArgumentException if the argument is the empty String,
     *         or any non-cacheable or unsafe methods defined in RFC 7231,
     *         which are POST, PUT, DELETE, CONNECT, OPTIONS and TRACE.
     *
     * @param method the method to be used for the push.  
     * @return this builder.
     */
    public PushBuilder method(String method);
    
    /**
     * Set the query string to be used for the push.  
     *
     * The query string will be appended to any query String included in a call
     * to {@link #path(String)}.  Any duplicate parameters must be preserved.
     * This method should be used instead of a query in {@link #path(String)}
     * when multiple {@link #push()} calls are to be made with the same
     * query string.
     * @param  queryString the query string to be used for the push. 
     * @return this builder.
     */
    public PushBuilder queryString(String queryString);
    
    /**
     * Set the SessionID to be used for the push.
     * The session ID will be set in the same way it was on the associated request (ie
     * as a cookie if the associated request used a cookie, or as a url parameter if
     * the associated request used a url parameter).
     * Defaults to the requested session ID or any newly assigned session id from
     * a newly created session.
     * @param sessionId the SessionID to be used for the push.
     * @return this builder.
     */
    public PushBuilder sessionId(String sessionId);
    
    /** 
     * <p>Set a request header to be used for the push.  If the builder has an
     * existing header with the same name, its value is overwritten.</p>
     *
     * @param name The header name to set
     * @param value The header value to set
     * @return this builder.
     */
    public PushBuilder setHeader(String name, String value);
    
    /** 
     * <p>Add a request header to be used for the push.</p>
     * @param name The header name to add
     * @param value The header value to add
     * @return this builder.
     */
    public PushBuilder addHeader(String name, String value);

    /** 
     * <p>Remove the named request header.  If the header does not exist, take
     * no action.</p>
     *
     * @param name The name of the header to remove
     * @return this builder.
     */
    public PushBuilder removeHeader(String name);

    /** 
     * Set the URI path to be used for the push.  The path may start
     * with "/" in which case it is treated as an absolute path,
     * otherwise it is relative to the context path of the associated
     * request.  There is no path default and {@link #path(String)} must
     * be called before every call to {@link #push()}.  If a query
     * string is present in the argument {@code path}, its contents must
     * be merged with the contents previously passed to {@link
     * #queryString}, preserving duplicates.
     *
     * @param path the URI path to be used for the push, which may include a
     * query string.
     * @return this builder.
     */
    public PushBuilder path(String path);
    
    /**
     * Push a resource given the current state of the builder,
     * the method must be non-blocking.
     *
     * <p>Push a resource based on the current state of the PushBuilder.
     * Calling this method does not guarantee the resource will actually
     * be pushed, since it is possible the client can decline acceptance
     * of the pushed resource using the underlying HTTP/2 protocol.</p>
     *
     * <p>If the builder has a session ID, then the pushed request will
     * include the session ID either as a Cookie or as a URI parameter
     * as appropriate. The builders query string is merged with any
     * passed query string.</p>
     *
     * <p>Before returning from this method, the builder has its path,
     * conditional headers (defined in RFC 7232) nulled. All other fields
     * are left as is for possible reuse in another push.</p>
     *
     * @throws IllegalStateException if there was no call to {@link
     * #path} on this instance either between its instantiation or the
     * last call to {@code push()} that did not throw an
     * IllegalStateException.
     */
    public void push();
    
    /**
     * Return the method to be used for the push.
     *
     * @return the method to be used for the push.
     */
    public String getMethod();

    /**
     * Return the query string to be used for the push.
     *
     * @return the query string to be used for the push.
     */
    public String getQueryString();

    /**
     * Return the SessionID to be used for the push.
     * 
     * @return the SessionID to be used for the push.
     */
    public String getSessionId();

    /**
     * Return the set of header to be used for the push.
     *
     * <p>The returned set is not backed by the {@code PushBuilder} object,
     * so changes in the returned set are not reflected in the
     * {@code PushBuilder} object, and vice-versa.</p>
     *
     * @return the set of header to be used for the push.
     */
    public Set<String> getHeaderNames();

    /**
     * Return the header of the given name to be used for the push.
     * @param name the name of the header
     *
     * @return the header of the given name to be used for the push.
     */
    public String getHeader(String name);

    /**
     * Return the URI path to be used for the push.
     *
     * @return the URI path to be used for the push.
     */
    public String getPath();
}
