Package rabbit.proxy

Source Code of rabbit.proxy.Connection

package rabbit.proxy;

import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URL;
import java.net.MalformedURLException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Date;
import java.util.List;
import rabbit.cache.Cache;
import rabbit.cache.CacheEntry;
import rabbit.filter.HttpFilter;
import rabbit.handler.BaseHandler;
import rabbit.handler.Handler;
import rabbit.handler.HandlerFactory;
import rabbit.handler.MultiPartHandler;
import rabbit.handler.ResourceSource;
import rabbit.http.HttpDateParser;
import rabbit.http.HttpHeader;
import rabbit.io.WebConnection;
import rabbit.util.Counter;
import rabbit.util.Logger;
import rabbit.util.TrafficLogger;

/** The base connection class for rabbit.
*
*  This is the class that handle the http protocoll for proxies.
*
* @author <a href="mailto:robo@khelekore.org">Robert Olofsson</a>
*/
public class Connection {
    /** The id of this connection. */
    private ConnectionId id;
   
    /** The client channel */
    private SocketChannel channel;
   
    /** The current request */
    private HttpHeader request;

    /** The current byte buffer */
    private ByteBuffer requestBuffer;
   
    /** The selector to use */
    private Selector selector;
   
    /** The proxy we are serving */
    private HttpProxy proxy;

    /** The current status of this connection. */
    private String status;
   
    /** The time this connection was started. */
    private long started;

    private boolean  keepalive      = true;
    private boolean  meta           = false;
    private boolean  chunk          = true;
    private boolean  mayUseCache    = true;
    private boolean  mayCache       = true;
    private boolean  mayFilter      = true;
    private boolean  mustRevalidate = false;
    private boolean  addedINM       = false;
    private boolean  addedIMS       = false;
   
    /** If the user has authenticated himself */
    private String userName = null;
    private String password = null;

    /* Current status information */
    private String requestVersion = null;
    private String requestLine   = null;
    private String statusCode    = null;
    private String extraInfo     = null;
    private String contentLength = null;

    private ClientResourceHandler clientResourceHandler;

    private StandardResponseHeaders responseHandler =
    new StandardResponseHeaders (this);

    private TrafficLoggerHandler tlh = new TrafficLoggerHandler ();

    public Connection (ConnectionId id,  SocketChannel channel,
           Selector selector, HttpProxy proxy) {
  this.id = id;
  this.channel = channel;
  this.selector = selector;
  this.proxy = proxy;
  proxy.addCurrentConnection (this);
    }

    public ConnectionId getId () {
  return id;
    }

    /** Read a request.
     */
    public void readRequest () {
  clearStatuses ();
  try {
      channel.socket ().setTcpNoDelay (true);
      HttpHeaderListener clientListener = new RequestListener ();
      HttpHeaderReader requestReader =
    new HttpHeaderReader (channel, requestBuffer, selector,
              getLogger (), tlh.getClient (), true,
              proxy.getStrictHttp (), clientListener);
  } catch (Throwable ex) {
      handleFailedRequestRead (ex);
  }
    }
   
    private void handleFailedRequestRead (Throwable t) {
  if (t instanceof RequestLineTooLongException) {
      HttpHeader err = getHttpGenerator ().get414 ();
      sendAndClose (err);
  } else {     
      getLogger ().logInfo ("Exception when reading request: " +
          getStackTrace (t));
      closeDown ();
  }
    }

    private class RequestListener implements HttpHeaderListener {
  public void httpHeaderRead (HttpHeader header, ByteBuffer buffer,
            boolean keepalive, boolean isChunked,
            long dataSize) {
      setKeepalive (keepalive);
      requestRead (header, buffer, isChunked, dataSize);
  }

  public void closed () {
      closeDown ();     
  }

  public void timeout () {
      getLogger ().logInfo ("Timeout when reading client request");
      closeDown ();
  }
 
  public void failed (Exception e) {
      handleFailedRequestRead (e);
  }
    }

    private String getStackTrace (Throwable t) {
  StringWriter sw = new StringWriter ();
  PrintWriter ps = new PrintWriter (sw);
  t.printStackTrace (ps);
  return sw.toString ();
    }

    private void handleInternalError (Throwable t) {
  extraInfo =
      extraInfo != null ? extraInfo + t.toString () : t.toString ();
  String message = getStackTrace (t);
  getLogger ().logError ("Internal Error: " + message);
  HttpHeader internalError = getHttpGenerator ().get500 (t);
  sendAndClose (internalError)
    }

    private void requestRead (HttpHeader request, ByteBuffer buffer,
            boolean isChunked, long dataSize) {
  status = "Request read, processing";
  this.request = request;
  this.requestBuffer = buffer;
  requestVersion = request.getHTTPVersion ();
  if (requestVersion == null) {
      // TODO: fix http/0.9 handling.
      getLogger ().logInfo ("bad header read: " + request);
      closeDown ();
      return;
  }
  requestVersion = requestVersion.toUpperCase ();
  request.addHeader ("Via", requestVersion + " RabbIT");
     
  requestLine = request.getRequestLine ();
  getCounter ().inc ("Requests");
 
  try {
      // SSL requests are special in a way...
      // Don't depend upon being able to build URLs from the header...
      if (request.isSSLRequest ()) {
    status = "Handling ssl request";
    checkAndHandleSSL (requestBuffer);
    return;
      }

      // Read in any posted data.
      if (isChunked) {
    setMayUseCache (false);
    setMayCache (false);
    status = "Request read, reading chunked data";
    setupChunkedContent ();
      }

      String ct = null;
      ct = request.getHeader ("Content-Type");
      if (request.getContent () == null
    && (ct == null || !ct.startsWith ("multipart/byteranges"))
    && dataSize > -1) {
    setupClientResourceHandler (dataSize);
      }

      if (ct != null) {
    status = "Request read, reading multipart data";
    readMultiPart (ct);
      }
      filterAndHandleRequest ();
  } catch (Throwable t) {
      handleInternalError (t);
  }
    }

    /** Filter the request and handle it.
     * @param header the request
     */
    // TODO: filtering here may block! be prepared to run filters in a
    // TODO: separate thread.
    private void filterAndHandleRequest () {
  // Filter the request based on the header.
  // A response means that the request is blocked.
  // For ad blocking, bad header configuration (http/1.1 correctness) ...
  HttpHeaderFilterer filterer = proxy.getHttpHeaderFilterer ();
  HttpHeader badresponse = filterer.filterHttpIn (this, channel, request);
  if (badresponse != null) {
      statusCode = badresponse.getStatusCode ();
      sendAndClose (badresponse);
  } else {
      status = "Handling request";
      if (getMeta ())
    handleMeta ();
      else
    handleRequest ();
  }
    }

    /** Handle a meta page.
     */
    public void handleMeta () {
  status = "Handling meta page";
  MetaHandlerHandler mhh = new MetaHandlerHandler ();
  try {
      mhh.handleMeta (this, request, tlh.getProxy (), tlh.getClient ());
  } catch (IOException ex) {
      logAndClose (null);
  }
    }
   
    /** A container to send around less parameters.*/
    class RequestHandler {
  public ResourceSource content = null;
  public ByteBuffer webBuffer = null;
  public HttpHeader webHeader = null;
  public CacheEntry<HttpHeader, HttpHeader> entry = null;
  public HttpHeader dataHook = null; // the entrys datahook if any.
  public HandlerFactory handlerFactory = null;
  public long size = -1;
  public WebConnection wc = null
  public long requestTime = -1;
  public ConditionalChecker cond = new ConditionalChecker (getLogger ());
  public boolean conditional;
    }

    private void checkNoStore (CacheEntry<HttpHeader, HttpHeader> entry) {
  if (entry == null)
      return;
  for (String cc : request.getHeaders ("Cache-Control"))
      if (cc.equals ("no-store"))
    proxy.getCache ().remove (entry.getKey ());
    }
   
    private boolean checkMaxAge (RequestHandler rh) {
  return rh.cond.checkMaxAge (this, rh.dataHook, rh);
    }

    /** Handle a request by getting the datastream (from the cache or the web).
     *  After getting the handler for the mimetype, send it.
     */
    public void handleRequest () {
  // TODO: move this method to separate thread,
  // TODO: it may block in many places.
  RequestHandler rh = new RequestHandler ()
  Cache<HttpHeader, HttpHeader> cache = proxy.getCache ();
  String method = request.getMethod ();
  if (!method.equals ("GET") && !method.equals ("HEAD"))
      cache.remove (request);
   
  rh.entry = cache.getEntry (request);
  if (rh.entry != null)
      rh.dataHook = rh.entry.getDataHook (proxy.getCache ());

  checkNoStore (rh.entry);
  if (!rh.cond.checkMaxStale (request, rh) && checkMaxAge (rh))
      setMayUseCache (false);
 
  rh.conditional = rh.cond.checkConditional (this, request, rh);
  if (partialContent (rh))
      fillupContent (rh);
  checkIfRange (rh);

  boolean mc = getMayCache ();
  if (getMayUseCache ()) {
      // in cache?
      if (rh.entry != null) {
    CacheChecker cc = new CacheChecker ();
    if (cc.checkCachedEntry (this, request, rh)) {
        return;
    }
      }
  }
 
  if (rh.content == null) {
      // Ok cache did not have a usable resource,
      // so get the resource from the net.
      // reset value to one before we thought we could use cache...
      mayCache = mc;
      SWC swc = new SWC (this, proxy.getOffset (), request, requestBuffer,
             tlh, clientResourceHandler, rh);
      swc.establish ();
  } else {
      resourceEstablished (rh);
  }
    }

    void webConnectionSetupFailed (RequestHandler rh, Exception cause) {
  getLogger ().logWarn ("strange error setting up web connection: " +
            cause.toString ());
  tryStaleEntry (rh, cause);
    }

    private void setMayCacheFromCC (RequestHandler rh) {
  HttpHeader resp = rh.webHeader;
  for (String val : resp.getHeaders ("Cache-Control")) {
      if ("public".equals (val)
    || "must-revalidate".equals (val)
    || val.startsWith ("s-maxage=")) {
    String auth = request.getHeader ("Authorization");   
    if (auth != null) {
        // TODO this ignores no-store and a few other things...
        mayCache = true;
        break;
    }
      }
  }
    }

    /** Check if we must tunnel a request.
     *  Currently will only check if the Authorization starts with NTLM or Negotiate.
     * @param rh the request handler.
     */
    protected boolean mustTunnel (RequestHandler rh) {
  String auth = request.getHeader ("Authorization");
  if (auth != null) {
      if (auth.startsWith ("NTLM") || auth.startsWith ("Negotiate"))
    return true;
  }
  return false;
    }
   
    void webConnectionEstablished (RequestHandler rh) {
  getProxy ().markForPipelining (rh.wc);
  setMayCacheFromCC (rh);
  resourceEstablished (rh);
    }

    private void tunnel (RequestHandler rh) {
  try {
      TunnelDoneListener tdl = new TDL (rh);
      Tunnel tunnel =
    new Tunnel (selector, getLogger (), channel, requestBuffer,
          tlh.getClient (), rh.wc.getChannel (),
          rh.webBuffer, tlh.getNetwork (), tdl);
  } catch (IOException ex) {
      logAndClose (rh);
  }
    }
   
    private void resourceEstablished (RequestHandler rh) {
  try {
      // and now we filter the response header if any.
      if (!request.isDot9Request ()) {
    if (mustTunnel (rh)) {
        tunnel (rh);
        return;
    }
   
    String status = rh.webHeader.getStatusCode ().trim ();
    rh.cond.checkStaleCache (request, this, rh);
    CacheChecker cc = new CacheChecker ();
    cc.removeOtherStaleCaches (request, rh.webHeader,
             proxy.getCache (), getLogger ());
    if (status.equals ("304")) {
        NotModifiedHandler nmh = new NotModifiedHandler ();
        nmh.updateHeader (rh, getLogger ());
        if (rh.entry != null) {
      proxy.getCache ().entryChanged (rh.entry,
              request, rh.dataHook);
        }
    }

    HttpHeader bad = cc.checkExpectations (this, request, rh.webHeader);
    if (bad == null) {
        HttpHeaderFilterer filterer =
      proxy.getHttpHeaderFilterer ();
        bad = filterer.filterHttpOut (this, channel, rh.webHeader);
    }
    if (bad != null) {
        rh.content.release (this);
        sendAndClose (bad);
        return;
    }
   
    rh.entry = proxy.getCache ().getEntry (request);
    if (rh.conditional && rh.entry != null
        && status.equals ("304")) {
        if (handleConditional (rh)) {
      return;
        }
    } else if (status != null && status.length () > 0) {
        if (status.equals ("304") || status.equals ("204")
      || status.charAt (0) == '1') {
      rh.content.release (this);
      sendAndClose (rh.webHeader);
      return;
        }
    }
      }
     
      setHandlerFactory (rh);
      Handler handler =
    rh.handlerFactory.getNewInstance (this, tlh,
              request, requestBuffer,
              rh.webHeader, rh.content,
              getMayCache (),
              getMayFilter (), rh.size);
      if (handler == null) {
    doError (500, "Something fishy with that handler....");
      } else {
    finalFixesOnWebHeader (rh, handler);
   
    // HTTP/0.9 does not support HEAD, so webheader should be valid.
    if (request.isHeadOnlyRequest ()) {
        rh.content.release (this);
        sendAndRestart (rh.webHeader);
    } else {
        handler.handle ();
    }
      }
  } catch (Throwable t) {
      handleInternalError (t);
  }
    }
 
    private void finalFixesOnWebHeader (RequestHandler rh, Handler handler) {
  if (chunk) {
      if (rh.size < 0 || handler.changesContentSize ()) {
    rh.webHeader.removeHeader ("Content-Length");
    rh.webHeader.setHeader ("Transfer-Encoding", "chunked");
      } else {
    setChunking (false);
      }
  } else {
      if (getKeepalive ()) {
    rh.webHeader.setHeader ("Proxy-Connection", "Keep-Alive");
    rh.webHeader.setHeader ("Connection", "Keep-Alive");
      } else {
    rh.webHeader.setHeader ("Proxy-Connection", "close");
    rh.webHeader.setHeader ("Connection", "close");
      }
  }
    }

    private void setHandlerFactory (RequestHandler rh) {
  if (rh.handlerFactory == null) {
      if (rh.webHeader != null) {
    String ct = rh.webHeader.getHeader ("Content-Type");
    if (ct != null) {
        ct = ct.toLowerCase ();
        if (getMayFilter ())
      rh.handlerFactory =
          proxy.getHandlerFactory (ct.toLowerCase ());
        if (rh.handlerFactory == null
      && ct.startsWith ("multipart/byteranges"))
      rh.handlerFactory = new MultiPartHandler ();
    }
      }
      if (rh.handlerFactory == null) {              // still null
    rh.handlerFactory = new BaseHandler ();   // fallback...
      }
  }  
    }

    private boolean handleConditional (RequestHandler rh) throws IOException {
  HttpHeader cachedHeader = rh.dataHook;
  proxy.releaseWebConnection (rh.wc);
  if (addedINM)
      request.removeHeader ("If-None-Match");
  if (addedIMS)
      request.removeHeader ("If-Modified-Since");

  if (checkWeakEtag (cachedHeader, rh.webHeader)) {
      NotModifiedHandler nmh = new NotModifiedHandler ();
      nmh.updateHeader (rh, getLogger ());
      setMayCache (false);
      try {
    HttpHeader res304 = nmh.is304 (request, this, rh);
    if (res304 != null) {
        sendAndClose (res304);
        return true;
    } else {
        if (rh.content != null)
      rh.content.release (this);
        setupCachedEntry (rh);
    }
      } catch (IOException e) {
    getLogger ().logWarn ("Conditional request: IOException (" +
              request.getRequestURI () + ",: " + e);
      }
  } else {
      // retry...
      request.removeHeader ("If-None-Match");
      proxy.getCache ().remove (request);
      handleRequest ();
      return true;
  }
 
  // send the cached entry.
  return false;
    }

    private class TDL implements TunnelDoneListener {
  private RequestHandler rh;
 
  public TDL (RequestHandler rh) {
      this.rh = rh;
  }
 
  public void tunnelClosed () {
      logAndClose (rh);
  }
    }

    private void tryStaleEntry (RequestHandler rh, Exception e) {
  // do we have a stale entry?
  if (rh.entry != null && rh.conditional && !mustRevalidate) {
      handleStaleEntry (rh);
  } else {
      doError (504, e);
      return;
  }
    }

    private void handleStaleEntry (RequestHandler rh) {
  setMayCache (false);
  try {
      setupCachedEntry (rh);
      rh.webHeader.addHeader ("Warning",
            "110 RabbIT \"Response is stale\"");
      resourceEstablished (rh);
  } catch (IOException ex) {
      doError (504, ex);
      return;
  }
    }
   
    HttpHeader setupCachedEntry (RequestHandler rh) throws IOException {
  SCC swc = new SCC (this, request, requestBuffer, rh);
  HttpHeader ret = swc.establish ();
  return ret;
    }

    private void setupChunkedContent () throws IOException {
  setMayUseCache (false);
  setMayCache (false);
  clientResourceHandler =
      new ChunkedContentTransferHandler (this, requestBuffer, tlh);
    }

    private void setupClientResourceHandler (long dataSize) {
  setMayUseCache (false);
  setMayCache (false);
  clientResourceHandler =
      new ContentTransferHandler (this, requestBuffer, dataSize, tlh);
    }

    private void readMultiPart (String ct) {
  // Content-Type: multipart/byteranges; boundary=B-qpuvxclkeavxeywbqupw
  if (ct.startsWith ("multipart/byteranges")) {
      setMayUseCache (false);
      setMayCache (false);
     
      clientResourceHandler =
    new MultiPartTransferHandler (this, requestBuffer, tlh, ct);
  }
    }

    /** Handles the case where the request does not have a valid body.
     * @param header the request made.
     */
    private void handleBadContent (HttpHeader header, String desc) {
  getLogger ().logDebug ("bad content for:\n" + header);
  doError (400, "Bad content: " + desc);
    }

    private boolean partialContent (RequestHandler rh) {
  if (rh.entry == null)
      return false;
  String method = request.getMethod ();
  if (!method.equals ("GET"))
      return false;
  HttpHeader resp = rh.dataHook;
  String realLength = resp.getHeader ("RabbIT-Partial");
  return (realLength != null);
    }
   
    private void fillupContent (RequestHandler rh) {
  setMayUseCache (false);
  setMayCache (true);
  // TODO: if the need arise, think about implementing smart partial updates.
    }

    private void checkIfRange (RequestHandler rh) {
  CacheEntry entry = rh.entry;
  if (entry == null)
      return;
  String ifRange = request.getHeader ("If-Range");
  if (ifRange == null)
      return;
  String range = request.getHeader ("Range");
  if (range == null)
      return;
  Date d = HttpDateParser.getDate (ifRange);
  HttpHeader oldresp = rh.dataHook;
  if (d == null) {
      // we have an etag...
      String etag = oldresp.getHeader ("Etag");
      if (etag == null || !checkWeakEtag (etag, ifRange))
    setMayUseCache (false);
  }
    }

    /** Send an error (400 Bad Request) to the client.
     * @param status the status code of the error.
     * @param message the error message to tell the client.
     */
    public void doError (int status, String message) {
  this.statusCode = Integer.toString (status);
  HttpHeader header = responseHandler.getHeader ("HTTP/1.0 400 Bad Request");
  StringBuilder error =
      new StringBuilder (HtmlPage.getPageHeader (this, "400 Bad Request") +
             "Unable to handle request:<br><b>" +
             message +
             "</b></body></html>\n");
  header.setContent (error.toString ());
  sendAndClose (header);
    }

    /** Send an error (400 Bad Request or 504) to the client.
     * @param statuscode the status code of the error.
     * @param e the exception to tell the client.
     */
    public void doError (int statuscode, Exception e) {
  StringWriter sw = new StringWriter ();
  PrintWriter ps = new PrintWriter (sw);
  e.printStackTrace (ps);
  String message = sw.toString ();
  this.statusCode = Integer.toString (statuscode);
  extraInfo = (extraInfo != null ?
         extraInfo + e.toString () :
         e.toString ());
  HttpHeader header = null;
  if (statuscode == 504)
      header = getHttpGenerator ().get504 (e, requestLine);
  else
      header = getHttpGenerator ().getHeader ("HTTP/1.0 400 Bad Request");
 
  StringBuilder sb = new StringBuilder ();
  sb.append (HtmlPage.getPageHeader (this, statuscode + " " +
             header.getReasonPhrase ()) +
       "Unable to handle request:<br><b>" +
       e.getMessage () +
       (header.getContent () != null ?
        "<br>" + header.getContent () :
        "") +
       "</b><br><xmp>" + message + "</xmp></body></html>\n");
  header.setContent (sb.toString ());
  sendAndClose (header);
    }

    private void checkAndHandleSSL (ByteBuffer buffer) {
  SSLHandler sslh = new SSLHandler (proxy, this, request, tlh);
  if (sslh.isAllowed ()) {
      sslh.handle (channel, selector, buffer);
  } else {
      HttpHeader badresponse = responseHandler.get403 ();
      sendAndClose (badresponse);
  }
    }      

    public SocketChannel getChannel () {
  return channel;
    }

    public Selector getSelector () {
  return selector;
    }

    public HttpProxy getProxy () {
  return proxy;
    }

    private void closeDown () {
  try {
      channel.close ();
  } catch (IOException e) {
      getLogger ().logWarn ("Failed to close down connection: " + e);
  }
  proxy.removeCurrentConnection (this);
    }

    public Logger getLogger () {
  return proxy.getLogger ();
    }

    private ConnectionLogger getConnectionLogger () {
  return proxy.getConnectionLogger ();
    }

    public Counter getCounter () {
  return proxy.getCounter ();
    }

    /** Resets the statuses for this connection.
     */
    private void clearStatuses () {
  status         = "Reading request";
  started        = System.currentTimeMillis ();
  request        = null;
  keepalive      = true;
  meta           = false;
  chunk          = true;
  mayUseCache    = true;
  mayCache       = true;
  mayFilter      = true;
  mustRevalidate = false;
  addedINM       = false;
  addedIMS       = false;
  userName       = null;
  password       = null;
  requestLine    = "?";
  statusCode     = "200";
  extraInfo      = null;
  contentLength  = "-";
  clientResourceHandler = null;
    }

    /** Set keepalive to a new value. Note that keepalive can only be
     *  promoted down.
     * @param keepalive the new keepalive value.
     */
    public void setKeepalive (boolean keepalive) {
  this.keepalive = (this.keepalive && keepalive);
    }
   
    /** Get the keepalive value.
     * @return true if keepalive should be done, false otherwise.
     */
    public boolean getKeepalive () {
  return keepalive;
    }

    public String getUserName () {
  return userName;
    }

    public void setUserName (String userName) {
  this.userName = userName;
    }
   
    public String getPassword () {
  return password;
    }

    public void setPassword (String password) {
  this.password = password;
    }

    public String getRequestLine () {
  return requestLine;
    }

    /** Get the http version that the client used.
     *  We modify the request header to hold HTTP/1.1 since that is
     *  what rabbit uses, but the real client may have sent a 1.0 header.
     */
    public String getRequestVersion () {
  return requestVersion;
    }

    public String getStatus () {
  return status;
    }

    public String getStatusCode () {
  return statusCode;
    }

    public String getContentLength () {
  return contentLength;
    }

    public String getExtraInfo () {
  return extraInfo;
    }

    /** Set the extra info.
     * @param info the new info.
     */
    public void setExtraInfo (String info) {
  this.extraInfo = info;
    }
   
    /** Get the time this connection was started. */
    public long getStarted () {
  return started;
    }

    /** Set the chunking option.
     * @param b if true this connection should use chunking.
     */
    public void setChunking (boolean b) {
  chunk = b;
    }

    /** Get the chunking option.
     * @return if this connection is using chunking.
     */
    public boolean getChunking () {
  return chunk;
    }

    /** Get the state of this request.
     * @return true if this is a metapage request, false otherwise.
     */
    public boolean getMeta () {
  return meta;
    }

    /** Set the state of this request.
     * @param meta true if this request is a metapage request, false otherwise.
     */
    public void setMeta (boolean meta) {
  this.meta = meta;
    }   

    /** Set the state of this request. This can only be promoted down..
     * @param useCache true if we may use the cache for this request,
     *        false otherwise.
     */
    public void setMayUseCache (boolean useCache) {
  mayUseCache = mayUseCache && useCache;
    }

    /** Get the state of this request.
     * @return true if we may use the cache for this request, false otherwise.
     */
    public boolean getMayUseCache () {
  return mayUseCache;
    }
   
    /** Set the state of this request. This can only be promoted down.
     * @param cacheAllowed true if we may cache the response, false otherwise.
     */
    public void setMayCache (boolean cacheAllowed) {
  mayCache = cacheAllowed && mayCache;
    }

    /** Get the state of this request.
     * @return true if we may cache the response, false otherwise.
     */
    public boolean getMayCache () {
  return mayCache;
    }

    /** Get the state of this request. This can only be promoted down.
     * @param filterAllowed true if we may filter the response, false otherwise.
     */
    public void setMayFilter (boolean filterAllowed) {
  mayFilter = filterAllowed && mayFilter;
    }
   
    /** Get the state of the request.
     * @return true if we may filter the response, false otherwise.
     */
    public boolean getMayFilter () {
  return mayFilter;
    }

    void setAddedINM (boolean b) {
  addedINM = b;
    }

    void setAddedIMS (boolean b) {
  addedIMS = b;
    }

    void setMustRevalidate (boolean b) {
  mustRevalidate = b;
    }

    /** Set the content length of the response.
     * @param contentLength the new content length.
     */
    public void setContentLength (String contentLength) {
  this.contentLength = contentLength;
    }

    void setStatusCode (String statusCode) {
  this.statusCode = statusCode;
    }

    private void setStatusesFromHeader (HttpHeader header) {
  statusCode = header.getStatusCode ();
  String cl = header.getHeader ("Content-Length");
  if (cl != null)
      contentLength  = cl;
    }

    void sendAndRestart (HttpHeader header) {
  status = "Sending response.";
  setStatusesFromHeader (header);
  if (!keepalive) {
      sendAndClose (header);
  } else {
      HttpHeaderSentListener sar = new SendAndRestartListener ();
      try {
    HttpHeaderSender sender =
        new HttpHeaderSender (channel, selector, getLogger (),
            tlh.getClient (), header, false, sar);
      } catch (IOException e) {
    getLogger ().logWarn ("IOException when sending header: " + e);
    closeDown ();
      }     
  }
    }

    public boolean useFullURI () {
  return proxy.isProxyConnected ();
    }

    private class SendAndRestartListener implements HttpHeaderSentListener {
  public void httpHeaderSent () {
      logConnection ();
      readRequest ();
  }
 
  public void timeout () {
      getLogger ().logInfo ("Timeout when sending http header");
      logAndClose (null);
  }

  public void failed (Exception e) {
      getLogger ().logInfo ("Exception when sending http header: " + e);
      logAndClose (null);
  }
    }


    void sendAndClose (HttpHeader header) {
  status = "Sending response and closing.";
  setStatusesFromHeader (header);
  keepalive = false;
  HttpHeaderSentListener scl = new SendAndCloseListener ();
  try {
      HttpHeaderSender sender =
    new HttpHeaderSender (channel, selector, getLogger (),
              tlh.getClient (), header, false, scl);
  } catch (IOException e) {
      getLogger ().logWarn ("IOException when sending header: " + e);
      closeDown ();
  }
    }
   
    public void logAndClose (RequestHandler rh) {
  if (rh != null && rh.wc != null) {
      proxy.releaseWebConnection (rh.wc);
  }
  logConnection ();
  closeDown ();
    }

    public void logAndRestart () {
  logConnection ()
  if (getKeepalive ())
      readRequest ();
  else
      closeDown ();
    }

    private class SendAndCloseListener implements HttpHeaderSentListener {
  public void httpHeaderSent () {
      logAndClose (null);
  }
 
  public void timeout () {
      getLogger ().logInfo ("Timeout when sending http header");
      logAndClose (null);
  }

  public void failed (Exception e) {
      getLogger ().logInfo ("Exception when sending http header: " + e);
      logAndClose (null);
  }
    }

    boolean isWeak (String t) {
  return t.startsWith ("W/");
    }

    boolean checkStrongEtag (String et, String im) {
  return !isWeak (im) && im.equals (et);
    }

    boolean checkWeakEtag (HttpHeader h1, HttpHeader h2) {
  String et1 = h1.getHeader ("Etag");
  String et2 = h2.getHeader ("Etag");
  if (et1 == null || et2 == null)
      return true;
  return checkWeakEtag (et1, et2);
    }

    boolean checkWeakEtag (String et, String im) {
  if (et == null || im == null)
      return false;
  if (isWeak (et))
      et = et.substring (2);
  if (isWeak (im))
      im = im.substring (2);
  return im.equals (et);
    }

    public HttpGenerator getHttpGenerator () {
  return responseHandler;
    }

    private void logConnection () {
  getConnectionLogger ().logConnection (Connection.this);
  proxy.updateTrafficLog (tlh);
  tlh.clear ();
    }
}
TOP

Related Classes of rabbit.proxy.Connection

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.