Package org.xlightweb.client

Source Code of org.xlightweb.client.RedirectHandler$BodylessRedirectResponseHandler

/*
*  Copyright (c) xlightweb.org, 2008 - 2010. All rights reserved.
*
*  This library is free software; you can redistribute it and/or
*  modify it under the terms of the GNU Lesser General Public
*  License as published by the Free Software Foundation; either
*  version 2.1 of the License, or (at your option) any later version.
*
*  This library is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
*  Lesser General Public License for more details.
*
*  You should have received a copy of the GNU Lesser General Public
*  License along with this library; if not, write to the Free Software
*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
* Please refer to the LGPL license at: http://www.gnu.org/copyleft/lesser.txt
* The latest copy of this software may be found on http://www.xlightweb.org/
*/
package org.xlightweb.client;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.xlightweb.BodyDataSink;
import org.xlightweb.HttpRequest;
import org.xlightweb.IBodyDestroyListener;
import org.xlightweb.IHttpExchange;
import org.xlightweb.IHttpRequest;
import org.xlightweb.IHttpRequestHandler;
import org.xlightweb.IHttpRequestHeader;
import org.xlightweb.IHttpResponse;
import org.xlightweb.IHttpResponseHandler;
import org.xlightweb.IHttpResponseHeader;
import org.xlightweb.ProtocolException;
import org.xlightweb.Supports100Continue;
import org.xlightweb.client.DuplicatingBodyForwarder.BodyDataSinkAdapter;
import org.xlightweb.client.DuplicatingBodyForwarder.ISink;
import org.xlightweb.client.DuplicatingBodyForwarder.InMemorySink;
import org.xsocket.Execution;
import org.xsocket.connection.IConnection.FlushMode;




/**
* Auto redirect handler
* @author grro@xlightweb.org
*/
@Supports100Continue
final class RedirectHandler implements IHttpRequestHandler {
   
   
    /**
     * RedirectHandler is unsynchronized by config. See HttpUtils$RequestHandlerInfo
     */
 
  private static final Logger LOG = Logger.getLogger(RedirectHandler.class.getName());

  private static final String COUNT_REDIRECTS_KEY = "org.xlightweb.client.RedirectHandler.countRedirects";
 
  private final HttpClient httpClient;
 
 
  /**
   * constructor
   *
   * @param httpClient  the http client to perform the redirects
   */
  public RedirectHandler(HttpClient httpClient) {
    this.httpClient = httpClient;
  }
 
 
  /**
   * {@inheritDoc}
   */
  public void onRequest(IHttpExchange exchange) throws IOException {
 
    IHttpRequest request = exchange.getRequest();
   
        // has body (e.g. PUT, POST)
        if (request.hasBody()) {
            BodyRedirectResponseHandler redirectHandler = new BodyRedirectResponseHandler(request.getRequestHeader().copy(), exchange);
            final BodyDataSink dataSink = exchange.forward(request.getRequestHeader(), redirectHandler);
            dataSink.setFlushmode(FlushMode.ASYNC);
           
            // BodyDataSink
            DuplicatingBodyForwarder forwarder = new DuplicatingBodyForwarder(request.getNonBlockingBody(), new BodyDataSinkAdapter(dataSink), redirectHandler);
            HttpClientConnection.setDataHandlerSilence(request.getNonBlockingBody(), forwarder);
           
        // ... no (e.g. GET, DELETE)
        } else {
            exchange.forward(request, new BodylessRedirectResponseHandler(request.getRequestHeader().copy(), exchange));
        }
  }

 
   
    abstract class AbstractRedirectResponseHandler implements IHttpResponseHandler {

        private IHttpRequestHeader requestHeader = null;
        private IHttpExchange exchange = null;
       
        private Integer countRedirects = 0;

       
        AbstractRedirectResponseHandler(IHttpRequestHeader requestHeader, IHttpExchange exchange) throws IOException {
            this.requestHeader = requestHeader;
            this.exchange = exchange;
           
            countRedirects = (Integer) requestHeader.getAttribute(COUNT_REDIRECTS_KEY);
            if (countRedirects == null) {
                countRedirects = 0;
            }
        }
       
        protected final int incCountRedirect() {
            return ++countRedirects;
        }
       
        protected final IHttpRequestHeader getRequestHeader() {
            return requestHeader;
        }
       
        protected final IHttpExchange getExchange() {
            return exchange;
        }
       

        @Execution(Execution.NONTHREADED)
        public void onResponse(IHttpResponse response) throws IOException {
           
            if (isRedirectResponse(requestHeader, response.getResponseHeader())) {

                if (LOG.isLoggable(Level.FINE)) {
                    LOG.fine("got redirect response: " + response.getResponseHeader());
                }
               
                // get the new location
                URL newLocation = getRedirectURI(response, requestHeader.isSecure(), requestHeader.getServerName(), requestHeader.getServerPort());
               
                // .. and update the stored request
                requestHeader.setRequestUrl(newLocation);

                if (countRedirects < httpClient.getMaxRedirects()) {

                    // if is secured perform redirect request in threaded context -> implicit startSSL() has to be performed within threaded context  
                    if (requestHeader.isSecure()) {
                        Runnable task = new Runnable() {
                            public void run() {
                                sendRedirectedRequest();
                            }
                        };
                        httpClient.getWorkerpool().execute(task);
                       
                    } else {
                        sendRedirectedRequest();
                    }
                   
                   
                } else {
                    exchange.sendError(new IOException("max redirects " + httpClient.getMaxRedirects() + " reached. request will not be executed: " + requestHeader));
                }
               
            } else {
                exchange.send(response);
            }
        }

        abstract void sendRedirectedRequest();
       
       
        @Execution(Execution.NONTHREADED)
        public final void onException(final IOException ioe) {
            exchange.sendError(ioe);
        }
       

        final boolean isRedirectResponse(IHttpRequestHeader requestHeader, IHttpResponseHeader responseHeader) {
           
            switch (responseHeader.getStatus()) {
           
            // 300 Multiple choices
            case 300:
                return false;
               
            // 301 Moved permanently
            case 301:
                if (requestHeader.getMethod().equalsIgnoreCase("GET") || requestHeader.getMethod().equalsIgnoreCase("HEAD")) {
                    return true;
                } else {
                    return false;
                }

               
            // 302 found
            case 302:
                if (httpClient.isTreat302RedirectAs303()) {
                    return true;
                   
                } else if (requestHeader.getMethod().equalsIgnoreCase("GET") || requestHeader.getMethod().equalsIgnoreCase("HEAD")) {
                    return true;
                   
                } else {
                    return false;
                }
           
            // 303 See other
            case 303:
                return true;
           
               
            // 304 Not modified
            case 304:
                return false;

            // 305 Use proxy
            case 305:
                return false;

            // 306 (unused)
            case 306:
                return false;

            // 307 temporary redirect
            case 307:
                return true;

            default:
                return false;
            }
        }
       
        private URL getRedirectURI(IHttpResponse response, boolean isSSL, String originalHost, int originalPort) {
           
            String location = response.getHeader("Location");
           
            if (location != null) {
               
                try {
                   
                    if (isRelativeUrl(location)) {
                        if (isSSL) {
                            if (originalPort == -1) {
                                return new URL("https://" + originalHost + location);
                            } else {
                                return new URL("https://" + originalHost + ":" + originalPort + location);
                            }
                        } else {
                            if (originalPort == -1) {
                                return new URL("http://" + originalHost + location);
                            } else {
                                return new URL("http://" + originalHost + ":" + originalPort + location);
                            }
                        }
                       
                    } else {
                        return new URL(location);
                    }
                   
                } catch (MalformedURLException e) {
                    if (LOG.isLoggable(Level.FINE)) {
                        LOG.fine("could not create relocation url . reason " + e.toString());
                    }
                }
                   
            }
       
            return null;
        }
       
       
        private boolean isRelativeUrl(String url) {
            url = url.toUpperCase();
            return (!url.startsWith("HTTP") && !url.startsWith("HTTPS"));
        }
    }  
 
 
    
    /**
     * ResponseHandler is unsynchronized by config. See HttpUtils$ResponseHandlerInfo
     */
    @Supports100Continue
    private final class BodyRedirectResponseHandler extends AbstractRedirectResponseHandler implements ISink {

        private final InMemorySink inMemorySink = new InMemorySink();
        private boolean is100ContinueReceived = false;
        private boolean isBufferIsActivated = true;
       
        BodyRedirectResponseHandler(IHttpRequestHeader requestHeader, IHttpExchange exchange) throws IOException {
            super(requestHeader, exchange);
        }
       
        @Override
        public void onResponse(IHttpResponse response) throws IOException {

            if (response.getStatus() == 100) {
                is100ContinueReceived = true;
               
                if ((response.getAttribute(HttpClientConnection.TIMEOUT_100_CONTINUE_RESPONSE) != null) && ((Boolean) response.getAttribute(HttpClientConnection.TIMEOUT_100_CONTINUE_RESPONSE) == true)) {
                    if (LOG.isLoggable(Level.FINE)) {
                        LOG.fine("Got auto-generated 100-continue response " + response.getReason());
                    }
                   
                } else {
                    if (LOG.isLoggable(Level.FINE)) {
                        LOG.fine("Got 100-continue response. destroying buffer body data sink");
                    }
                    isBufferIsActivated = false;
                    inMemorySink.destroy();
                }
               
            } else if (is100ContinueReceived && isRedirectResponse(getRequestHeader(), response.getResponseHeader())) {
                onException(new ProtocolException("Response order error. Got a redirect response after a 100-continue response", response.getResponseHeader()));
            }

            super.onResponse(response);
        }
       
        public void onData(ByteBuffer data) throws IOException {
            if (isBufferIsActivated) {
                inMemorySink.onData(data);
            }
        }
       
       
        public void close() throws IOException {
            inMemorySink.close();
        }
       
        public void destroy() {
            inMemorySink.destroy();
        }
       
        public void setDestroyListener(IBodyDestroyListener destroyListener) {
            inMemorySink.setDestroyListener(destroyListener);
        }

        public String getId() {
            return inMemorySink.getId();
        }
                       
       
        protected void sendRedirectedRequest() {
           
            Runnable task = new Runnable() {
                
                 public void run() {
                     if (LOG.isLoggable(Level.FINE)) {
                         LOG.fine("send redirected request (body size " + inMemorySink.getSize() + "): " + getRequestHeader());
                     }
                    
                     try {
                         getRequestHeader().setAttribute(COUNT_REDIRECTS_KEY, incCountRedirect());
                         getRequestHeader().setAttribute(CookieHandler.COOKIE_WARNING_KEY, false);
                        
                         IHttpResponseHandler respHdl = new IHttpResponseHandler() {
                            
                             @Execution(Execution.NONTHREADED)
                             public void onResponse(IHttpResponse response) throws IOException {
                                 getExchange().send(response);
                             }
                            
                             @Execution(Execution.NONTHREADED)
                             public void onException(IOException ioe) throws IOException {
                                 getExchange().sendError(ioe);
                             }
                         };
                        
                         BodyDataSink ds = httpClient.send(getRequestHeader(), respHdl);
                         ds.setFlushmode(FlushMode.ASYNC);
                        
                         inMemorySink.forwardTo(new BodyDataSinkAdapter(ds));
                        
                     } catch (IOException ioe) {
                         getExchange().sendError(new IOException("can execute redirect request " + getRequestHeader() + " reason: " + ioe.toString()));
                     }
                 }
             };
            
             // run in own thread to prevent dead locks
             httpClient.getWorkerpool().execute(task);
         }
    }
   
   
 
    /**
     * ResponseHandler is unsynchronized by config. See HttpUtils$ResponseHandlerInfo
     */
    @Supports100Continue
  private final class BodylessRedirectResponseHandler extends AbstractRedirectResponseHandler {

    BodylessRedirectResponseHandler(IHttpRequestHeader requestHeader, IHttpExchange exchange) throws IOException {
        super(requestHeader, exchange);
    }
   
   
    protected void sendRedirectedRequest() {
       
       Runnable task = new Runnable() {
           
            public void run() {
                if (LOG.isLoggable(Level.FINE)) {
                    LOG.fine("send redirected request: " + getRequestHeader());
                }
                try {
                    getRequestHeader().setAttribute(COUNT_REDIRECTS_KEY, incCountRedirect());
                    getRequestHeader().setAttribute(CookieHandler.COOKIE_WARNING_KEY, false);
                   
                    IHttpResponseHandler respHdl = new IHttpResponseHandler() {
                       
                        @Execution(Execution.NONTHREADED)
                        public void onResponse(IHttpResponse response) throws IOException {
                            getExchange().send(response);
                        }
                       
                        @Execution(Execution.NONTHREADED)
                        public void onException(IOException ioe) throws IOException {
                            getExchange().sendError(ioe);
                        }
                    };
                   
                    httpClient.send(new HttpRequest(getRequestHeader()), respHdl);
                } catch (IOException ioe) {
                    getExchange().sendError(new IOException("can execute redirect request " + getRequestHeader() + " reason: " + ioe.toString()));
                }
            }
        };
       
        // run in own thread to prevent dead locks
        httpClient.getWorkerpool().execute(task);
    }
  } 
}
TOP

Related Classes of org.xlightweb.client.RedirectHandler$BodylessRedirectResponseHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.