All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.caucho.v5.http.protocol2.RequestHttp2 Maven / Gradle / Ivy

There is a newer version: 1.0.1
Show newest version
/*
 * Copyright (c) 1998-2015 Caucho Technology -- all rights reserved
 *
 * This file is part of Baratine(TM)
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Baratine is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Baratine 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, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Baratine; if not, write to the
 *
 *   Free Software Foundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.v5.http.protocol2;

import java.io.IOException;
import java.io.InputStream;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.caucho.v5.amp.thread.ThreadPool;
import com.caucho.v5.health.shutdown.Shutdown;
import com.caucho.v5.http.container.HttpContainer;
import com.caucho.v5.http.dispatch.Invocation;
import com.caucho.v5.http.protocol.OutResponseBase;
import com.caucho.v5.http.protocol.ProtocolHttp;
import com.caucho.v5.http.protocol.RequestFacade;
import com.caucho.v5.http.protocol.RequestHttp;
import com.caucho.v5.http.protocol.RequestHttpBase;
import com.caucho.v5.io.ReadBuffer;
import com.caucho.v5.io.TempBuffer;
import com.caucho.v5.io.WriteBuffer;
import com.caucho.v5.network.port.ConnectionTcp;
import com.caucho.v5.util.ByteArrayBuffer;
import com.caucho.v5.util.CharBuffer;
import com.caucho.v5.util.CharSegment;
import com.caucho.v5.util.CurrentTime;

/**
 * Handles requests for a HTTP 2 stream request.
 */
public class RequestHttp2 
  extends RequestHttpBase
  implements InRequest, Runnable
{
  private static final Logger log
    = Logger.getLogger(RequestHttp2.class.getName());

  private static final int HEADER_CAPACITY = 256;

  static final CharBuffer _getCb = new CharBuffer("GET");
  static final CharBuffer _headCb = new CharBuffer("HEAD");
  static final CharBuffer _postCb = new CharBuffer("POST");

  private String _method;       // "GET"
  // private CharBuffer scheme;       // "http:"
  private CharBuffer _host;            // www.caucho.com
  private ByteArrayBuffer _uri;             // "/path/test.jsp/Junk"

  private CharBuffer _remoteAddr;
  private CharBuffer _remoteHost;
  private CharBuffer _serverName;
  private CharBuffer _serverPort;
  private CharBuffer _remotePort;

  private boolean _isSecure;
  private ByteArrayBuffer _clientCert;

  private CharBuffer []_headerKeys;
  private CharBuffer []_headerValues;
  private int _headerSize;

  private int _serverType;

  // private ErrorPageManager _errorManager;

  private HttpContainer _httpContainer;

  private String _scheme;

  private Invocation _invocation;

  private AtomicReference _stateRef = new AtomicReference<>(StateRequest.ACTIVE);
  
  // private OutHttp _outHttp;

  //private DuplexHandlerHttp2 _conn;

  private InStreamImpl _inStream;

  private final RequestProtocolHttp2 _protocolHttp2 ;

  private final ChannelHttp2 _channel;

  private final StringBuilder _cb = new StringBuilder();

  public RequestHttp2(ProtocolHttp httpProtocol,
                      ConnectionTcp conn,
                      HttpContainer httpContainer,
                      RequestProtocolHttp2 protocol)
  {
    super(httpProtocol); //, conn, null); // XXX: null should be connHttp

    _protocolHttp2 = protocol;
    _httpContainer = httpContainer;

    //_errorManager = new ErrorPageManager(httpContainer);

    _uri = new ByteArrayBuffer();

    _host = new CharBuffer();

    _headerKeys = new CharBuffer[HEADER_CAPACITY];
    _headerValues = new CharBuffer[_headerKeys.length];
    for (int i = 0; i < _headerKeys.length; i++) {
      _headerKeys[i] = new CharBuffer();
      _headerValues[i] = new CharBuffer();
    }

    _remoteHost = new CharBuffer();
    _remoteAddr = new CharBuffer();
    _serverName = new CharBuffer();
    _serverPort = new CharBuffer();
    _remotePort = new CharBuffer();

    _clientCert = new ByteArrayBuffer();
    
    _channel = new ChannelHttp2(this);
    
    _inStream = new InStreamImpl();
  }
  /*
  @Override
  public ConnectionSocket getConnection()
  {
    return _conn.getConnection();
  }
  */
  @Override
  public boolean isSecure()
  {
    return getConnection().socket().isSecure();
  }
  
  /*
  @Override
  public ResponseHttp2 getResponse()
  {
    return (ResponseHttp2) super.getResponse();
  }

  @Override
  public ResponseHttp2 createResponse()
  {
    return new ResponseHttp2(this);
  }
  */
  
  @Override
  public HttpContainer http()
  {
    return _httpContainer;
  }
  
  OutHttp getOutHttp()
  {
    return getChannel().getOutChannel().getOutHttp();
  }
  
  @Override
  public ChannelHttp2 getChannel()
  {
    return _channel;
  }
  
  @Override
  public ChannelOutHttp2 getChannelOut()
  {
    return _channel.getOutChannel();
  }
  
  @Override
  public ChannelInHttp2 getChannelIn()
  {
    return getChannel().getInChannel();
  }

  public int getStreamId()
  {
    return getChannel().getId();
  }

  public void init(RequestProtocolHttp2 reqHttp)
  {
    // _streamId = streamId;
    // _conn = conn;
    // _outHttp = outHttp; // reqHttp.getOut();
    
    // _channel.init(conn, streamId);
    
    initRequest(); // XXX:
    /*
    try {
    } catch (IOException e) {
      e.printStackTrace();
    }
    */
  }
  
  public void fillUpgrade(RequestHttp requestHttp)
  {
    header(":method", requestHttp.getMethod());
    String host = requestHttp.getHeader("Host");
    if (host != null) {
      header(":authority", host);
    }
    
    String path = new String (requestHttp.getUriBuffer(), 0, requestHttp.getUriLength());
    
    header(":path", path);
    header(":scheme", requestHttp.getScheme());
    
    int headerSize = requestHttp.getHeaderSize();
    
    for (int i = 0; i < headerSize; i++) {
      String key = requestHttp.getHeaderKey(i).toString();
      String value = requestHttp.getHeader(key);
      
      header(key, value);
    }
  }

  @Override
  public void header(String key, String value)
  {
    switch (key) {
    case ":method":
      _method = value;
      break;
      
    case ":authority":
      _host.clear();
      _host.append(value);
      break;
      
    case ":path":
      _uri.clear();
      _uri.add(value);
      break;
      
    case ":scheme":
      _scheme = value;
      break;
      
    case "cookie":
      addCookie(value);
      break;
      
    default:
      setHeader(key, value);
      break;
    }
  }
  
  @Override
  public void data(TempBuffer tBuf)
  {
    _inStream.data(tBuf);
  }

  @Override
  public void closeRead()
  {
    _inStream.closeRead();
  }

  @Override
  public void closeReset()
  {
    closeRead();
  }

  @Override
  public void dispatch()
  {
    if (isSecure()) {
      getClientCertificate();
    }

    // setStartDate();

    HttpContainer httpContainer = http();

    if (httpContainer == null || httpContainer.isDestroyed()) {
      log.fine("server is closed (" + dbgId() + ")");
      
      // XXX: go-away

      return;
    }

    try {
      // startRequest();
      // startInvocation();
    
      _invocation = getInvocation(getHost(),
                                 _uri.getBuffer(), _uri.getLength());

      //request().setInvocation(_invocation);
      if (true) throw new UnsupportedOperationException();
      
      // XXX: needs to be throttled
      ThreadPool.current().execute(this);
    } catch (IOException e) {
      e.printStackTrace();
    }
    

    //  invocation.service(getRequestFacade(), getResponseFacade());
  }

  @Override
  public void run()
  {
    try {
      _stateRef.get().toActive(_stateRef);
      
      //StateConnection nextState = request().service();
      throw new UnsupportedOperationException();
    } catch (OutOfMemoryError e) {
      Shutdown.shutdownOutOfMemory("RequestHttp2");
    } catch (Throwable e) {
      e.printStackTrace();
    } finally {
      try {
        //finishInvocation();
      } catch (Exception e) {
        e.printStackTrace();
      }
      
      /*
      try {
        finishRequest();
      } catch (Exception e1) {
        e1.printStackTrace();
      }
      */
      
      closeDispatch();
    }
  }

  /**
   * Initialize the read stream from the raw stream.
   */
  //@Override
  public boolean initStream(ReadBuffer readStream,
                            ReadBuffer rawStream)
    throws IOException
  {
    readStream.init(_inStream);
    
    return true;
  }

  private void getClientCertificate()
  {
    RequestFacade request = null;//request();
    
    if (true) throw new UnsupportedOperationException();

    String cipher = getHeader("SSL_CIPHER");
    
    if (cipher == null) {
      cipher = getHeader("HTTPS_CIPHER");
    }
    
    if (cipher != null) {
      request.setCipherSuite(cipher);
    }

    String keySize = getHeader("SSL_CIPHER_USEKEYSIZE");
    if (keySize == null) {
      keySize = getHeader("SSL_SECRETKEYSIZE");
    }
    
    if (keySize != null) {
      request.setCipherKeySize(Integer.parseInt(keySize));
    }

    if (_clientCert.size() == 0)
      return;

    try {
      CertificateFactory cf = CertificateFactory.getInstance("X.509");
      InputStream is = _clientCert.createInputStream();
      X509Certificate cert = (X509Certificate) cf.generateCertificate(is);
      is.close();
      
      request.setCipherCertificate(new X509Certificate[] { cert });
    } catch (Exception e) {
      log.log(Level.FINE, e.toString(), e);
    }
  }

  protected boolean checkLogin()
  {
    return true;
  }

  /**
   * Clears variables at the start of a new request.
   */
  @Override
  protected void initRequest()
  {
    super.initRequest();

    _uri.clear();
    _host.clear();
    _headerSize = 0;

    _remoteHost.clear();
    _remoteAddr.clear();
    _serverName.clear();
    _serverPort.clear();
    _remotePort.clear();

    _clientCert.clear();
    
    // _isSecure = getConnection().isSecure();
  }

  /*
  @Override
  public void finishRequest() throws IOException
  {
    super.finishRequest();
  }
  */

  private void resizeHeaders()
  {
    CharBuffer []newKeys = new CharBuffer[_headerSize * 2];
    CharBuffer []newValues = new CharBuffer[_headerSize * 2];

    for (int i = 0; i < _headerSize; i++) {
      newKeys[i] = _headerKeys[i];
      newValues[i] = _headerValues[i];
    }

    for (int i = _headerSize; i < newKeys.length; i++) {
      newKeys[i] = new CharBuffer();
      newValues[i] = new CharBuffer();
    }

    _headerKeys = newKeys;
    _headerValues = newValues;
  }

  /**
   * Returns the header.
   */
  @Override
  public String getMethod()
  {
    return _method;
  }

  /**
   * Returns a char buffer containing the host.
   */
  @Override
  protected CharBuffer getHost()
  {
    if (_host.length() > 0) {
      return _host;
    }

    _host.append(_serverName);
    _host.toLowerCase();

    return _host;
  }

  @Override
  public final byte []getUriBuffer()
  {
    return _uri.getBuffer();
  }

  @Override
  public final int getUriLength()
  {
    return _uri.getLength();
  }

  /**
   * Returns the protocol.
   */
  @Override
  public String getProtocol()
  {
    return "HTTP/2.0";
  }

  /**
   * Returns the header.
   */
  @Override
  public String getHeader(String key)
  {
    CharSegment buf = getHeaderBuffer(key);
    if (buf != null)
      return buf.toString();
    else
      return null;
  }

  @Override
  public CharSegment getHeaderBuffer(String key)
  {
    for (int i = 0; i < _headerSize; i++) {
      CharBuffer test = _headerKeys[i];

      if (test.equalsIgnoreCase(key))
        return _headerValues[i];
    }

    return null;
  }

  public CharSegment getHeaderBuffer(char []buf, int length)
  {
    for (int i = 0; i < _headerSize; i++) {
      CharBuffer test = _headerKeys[i];

      if (test.length() != length)
        continue;

      char []keyBuf = test.buffer();
      int j;
      for (j = 0; j < length; j++) {
        char a = buf[j];
        char b = keyBuf[j];
        if (a == b)
          continue;

        if (a >= 'A' && a <= 'Z')
          a += 'a' - 'A';
        if (b >= 'A' && b <= 'Z')
          b += 'a' - 'A';
        if (a != b)
          break;
      }

      if (j == length)
        return _headerValues[i];
    }

    return null;
  }

  @Override
  public void setHeader(String key, String value)
  {
    if (_headerKeys.length <= _headerSize) {
      resizeHeaders();
    }
    
    _headerKeys[_headerSize].clear();
    _headerKeys[_headerSize].append(key);
    _headerValues[_headerSize].clear();
    _headerValues[_headerSize].append(value);
    _headerSize++;
  }

  @Override
  public void getHeaderBuffers(String key, ArrayList values)
  {
    CharBuffer cb = getCharBuffer();

    cb.clear();
    cb.append(key);

    int size = _headerSize;
    for (int i = 0; i < size; i++) {
      CharBuffer test = _headerKeys[i];
      if (test.equalsIgnoreCase(cb))
        values.add(_headerValues[i]);
    }
  }

  public Enumeration getHeaderNames()
  {
    HashSet names = new HashSet();
    for (int i = 0; i < _headerSize; i++)
      names.add(_headerKeys[i].toString());

    return Collections.enumeration(names);
  }

  /**
   * Returns the server name.
   */
  /*
  @Override
  public String getServerName()
  {
    CharBuffer host = getHost();
    if (host == null) {
      InetAddress addr = getConnection().getRemoteAddress();
      return addr.getHostName();
    }

    int p = host.indexOf(':');
    if (p >= 0)
      return host.substring(0, p);
    else
      return host.toString();
  }
  */

  /*
  @Override
  public int getServerPort()
  {
    int len = _serverPort.length();
    int port = 0;
    for (int i = 0; i < len; i++) {
      char ch = _serverPort.charAt(i);
      port = 10 * port + ch - '0';
    }

    return port;
  }
  */

  /*
  @Override
  public String getRemoteAddr()
  {
    return _remoteAddr.toString();
  }
  */

  public void getRemoteAddr(CharBuffer cb)
  {
    cb.append(_remoteAddr);
  }

  /*
  @Override
  public int printRemoteAddr(byte []buffer, int offset)
    throws IOException
  {
    char []buf = _remoteAddr.getBuffer();
    int len = _remoteAddr.getLength();

    for (int i = 0; i < len; i++)
      buffer[offset + i] = (byte) buf[i];

    return offset + len;
  }
  */

  /*
  @Override
  public String getRemoteHost()
  {
    return _remoteHost.toString();
  }
  */

  /**
   * Called for a connection: close
   */
  @Override
  protected void handleConnectionClose()
  {
    // ignore for hmux
  }

  @Override
  public boolean isSuspend()
  {
    return false;
  }

  @Override
  public boolean isDuplex()
  {
    return false;
  }

  /**
   * Returns true if a valid HTTP request has started.
   */
  /*
  @Override
  public boolean hasRequest()
  {
    return true;
  }
  */

  protected String getRequestId()
  {
    HttpContainer http = http();
    
    String id;
    
    if (http != null) {
      id = http.getServerId();
    }
    else {
      id = "";
    }

    if (id.equals(""))
      return "server-" + getConnection().id();
    else
      return "server-" + id + ":" + getConnection().id();
  }
  
  //
  // Close dispatch and close channel are in different threads, but both
  // must complete before the request can be recycled.
  //
  public void closeDispatch()
  {
    if (_stateRef.get().closeDispatch(_stateRef)) {
      _protocolHttp2.freeRequest(this);
    }
  }

  @Override
  public void closeChannel()
  {
    if (_stateRef.get().closeChannel(_stateRef)) {
      _protocolHttp2.freeRequest(this);
    }
  }
  @Override
  protected OutResponseBase createOut()
  {
    return new OutResponseHttp2(this);
  }

  /**
   * headersWritten cannot be undone for hmux
   */
  @Override
  public void setHeaderWritten(boolean isWritten)
  {
    // server/265a
  }
  
  /*
  @Override
  protected void writeHeaders(long length)
    throws IOException
  {
  }
  */
  
  void writeHeaders(OutHeader out)
    throws IOException
  {
    fillHeaders();
    
    RequestFacade response = null;//request();
    
    if (true) throw new UnsupportedOperationException();

    int statusCode = response.getStatus();

    StringBuilder cb = _cb;
    
    switch (statusCode) {
    case 200:
      out.header(":status", "200");
      break;
      
    case 404:
      out.header(":status", "404");
      break;
      
    case 500:
      out.header(":status", "500");
      break;
      
    case 503:
      out.header(":status", "503");
      break;
      
    default:
      cb.setLength(0);
      cb.append((char) ((statusCode / 100) % 10 + '0'));
      cb.append((char) ((statusCode / 10) % 10 + '0'));
      cb.append((char) (statusCode % 10 + '0'));
      
      out.header(":status",  cb.toString());
      break;
    }

    if (statusCode >= 400) {
      removeHeader("ETag");
      removeHeader("Last-Modified");
    }
    else if (response.isNoCache()) {
      removeHeader("ETag");
      removeHeader("Last-Modified");

      setHeaderOut("expires", "Thu, 01 Dec 1994 16:00:00 GMT");
      out.header("cache-control", "no-cache");
    }
    else if (response.isPrivateCache())
      out.header("cache-control", "private");

    ArrayList headerKeys = headerKeysOut();
    ArrayList headerValues = headerValuesOut();
    
    int size = headerKeys.size();
    for (int i = 0; i < size; i++) {
      String key = headerKeys.get(i);
      String value = headerValues.get(i);

      out.header(key.toLowerCase(), value);
    }

    long contentLength = contentLengthOut();
    if (contentLength >= 0) {
      cb.setLength(0);
      cb.append(contentLength);
      out.header("content-length", cb.toString());
    }
    /*
    else if (length >= 0) {
      cb.clear();
      cb.append(length);
      out.header("content-length", cb.toString());
    }
    */

    RequestFacade responseFacade = null;//request();
    if (true) throw new UnsupportedOperationException();

    long now = CurrentTime.currentTime();
    
    responseFacade.fillCookies(out);

    String contentType = responseFacade.getContentTypeImpl();
    String charEncoding = responseFacade.getCharacterEncodingImpl();

    if (contentType != null) {
      if (charEncoding != null)
        out.header("content-type", contentType + "; charset=" + charEncoding);
      else
        out.header("content-type", contentType);
    }
    
    String server = serverHeader();
    
    if (server != null) {
      out.header("server", server);
    }
    else {
      out.header("server", "Resin/5.0");
    }
    
    byte []date = fillDateBuffer(now);
    int length = getDateBufferLength() - 4;
    
    int offset = 8; // "\r\nDate: "
    
    cb.setLength(0);
    for (int i = 8; i < length; i++) {
      cb.append((char) date[i]);
    }
    
    out.headerUnique("date", cb.toString());
  }

  @Override
  public final String dbgId()
  {
    HttpContainer http = http();
    
    String id;
    
    id = http.getServerDisplayName();
    
    int streamId = 0;

    if (id.equals(""))
      return "Http2[" + streamId + "] ";
    else
      return "Http2[" + id + ":" + streamId + "] ";
  }

  @Override
  public String toString()
  {
    return dbgId();
  }
  
  private enum StateRequest {
    ACTIVE {
      @Override
      boolean closeDispatch(AtomicReference stateRef)
      {
        if (stateRef.compareAndSet(ACTIVE, CLOSE_DISPATCH)) {
          return false;
        }
        else {
          return stateRef.get().closeDispatch(stateRef);
        }
      }

      @Override
      boolean closeChannel(AtomicReference stateRef)
      {
        if (stateRef.compareAndSet(ACTIVE, CLOSE_CHANNEL)) {
          return false;
        }
        else {
          return stateRef.get().closeChannel(stateRef);
        }
      }
      
    },
    
    CLOSE_DISPATCH {
      @Override
      boolean closeChannel(AtomicReference stateRef)
      {
        if (stateRef.compareAndSet(CLOSE_DISPATCH, CLOSE)) {
          return true;
        }
        else {
          return stateRef.get().closeChannel(stateRef);
        }
      }
    },
    
    CLOSE_CHANNEL {
      @Override
      boolean closeDispatch(AtomicReference stateRef)
      {
        if (stateRef.compareAndSet(CLOSE_CHANNEL, CLOSE)) {
          return true;
        }
        else {
          return stateRef.get().closeDispatch(stateRef);
        }
      }
    },
    
    CLOSE {
    };
    
    void toActive(AtomicReference stateRef)
    {
      stateRef.set(ACTIVE);
    }
    
    boolean closeDispatch(AtomicReference stateRef)
    {
      throw new UnsupportedOperationException(toString());
    }
    
    boolean closeChannel(AtomicReference stateRef)
    {
      throw new UnsupportedOperationException(toString());
    }
    
  }

  @Override
  public boolean writeFirst(WriteBuffer out, TempBuffer head, long length,
                            boolean isEnd)
  {
    return false;
  }
  
  @Override
  public boolean writeNext(WriteBuffer out, TempBuffer head, boolean isEnd)
  {
    return false;
  }
  
  @Override
  public boolean canWrite(long sequence)
  {
    return true;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy