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

com.caucho.v5.http.protocol2.InHttp 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.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.caucho.v5.io.IoUtil;
import com.caucho.v5.io.ReadBuffer;
import com.caucho.v5.io.TempBuffer;
import com.caucho.v5.util.BitsUtil;
import com.caucho.v5.util.L10N;

/**
 * InputStreamHttp reads a single HTTP frame.
 */
public class InHttp
{
  private static final Logger log = Logger.getLogger(InHttp.class.getName());
  private static final L10N L = new L10N(InHttp.class);

  private final ConnectionHttp2 _conn;
  private final InHttpHandler _inHandler;
  
  private SettingsHttp _settings = new SettingsHttp();
  
  private SettingsHttp _peerSettings = new SettingsHttp();
  
  private ReadBuffer _is;
  private InHeader _inHeader;
  private final byte []_header = new byte[8];
  
  // reference count for peer-initiated streams.
  private final AtomicInteger _openStream = new AtomicInteger();
  
  private boolean _isGoAway;

  public InHttp(ConnectionHttp2 conn, 
                InHttpHandler inHandler)
  {
    Objects.requireNonNull(conn);
    Objects.requireNonNull(inHandler);
    
    _conn = conn;
    
    _inHandler = inHandler;
  }

  public InHttp(ReadBuffer is, InHttpHandler inHandler)
  {
    this(new DummyConnection(inHandler), inHandler);
    
    init(is);
  }
  
  protected PeerHttp getPeer()
  {
    return _conn.getPeer();
  }

  public SettingsHttp getSettings()
  {
    return _settings;
  }
  
  public SettingsHttp getPeerSettings()
  {
    return _peerSettings;
  }

  public void setWindow(int window)
  {
    _settings.setInitialWindowSize(window);
  }
  
  void init(ReadBuffer is)
  {
    Objects.requireNonNull(is);
    
    _is = is;
    
    switch (getPeer()) {
    case SERVER:
      _inHeader = new InHeaderRequest(_is);
      break;
      
    case CLIENT:
      _inHeader = new InHeaderResponse(_is);
      break;
      
    default:
      throw new IllegalStateException();
    }
      
    _openStream.set(1);
    _isGoAway = false;
    _peerSettings.setInitialWindowSize(64 * 1024);
  }
  
  public boolean onDataAvailable()
    throws IOException
  {
    int isData = -1;
    
    ReadBuffer is = _is;
    byte []header = _header;

    if (is == null || is.available() <= 0 || _conn.isClosed()) {
      return false;
    }

    do {
      if (! readFrame(is, header)) {
        return false;
      }
        
      // isData = 1;
    } while ((isData = is.available()) > 0);
        
    return isData >= 0;
  }
  
  boolean onPeerOpen()
  {
    int value;

    do {
      value = _openStream.get();
    } while (value > 0 && ! _openStream.compareAndSet(value, value + 1));

    return value > 0;
  }
  
  int onCloseStream()
  {
    return _openStream.decrementAndGet();
  }
  
  private boolean readFrame(ReadBuffer is, byte []header)
    throws IOException
  {
    if (is == null) {
      return false;
    }
    
    int length = BitsUtil.readInt16(is);
    int type = is.read();
    int flags = is.read();
    
    int streamId = BitsUtil.readInt(is);
    
    // log.warning("FRAME: ty:" + type + " ln:" + length + " sid:" + streamId + " fl:" + flags);
    // System.out.println("FRAME: ty:" + type + " ln:" + length + " sid:" + streamId + " fl:" + flags);

    if (streamId < 0) {
      return false;
    }
    
    switch (type) {
    case Http2Constants.FRAME_DATA:
      return readData(is, length, flags, streamId);
      
    case Http2Constants.FRAME_HEADERS:
      return readHeader(is, length, flags, streamId);
      
    case Http2Constants.FRAME_SETTINGS:
      return readSettings(is, length);
      
    case Http2Constants.FRAME_WINDOW_UPDATE:
      return readFlow(is, length, streamId);
      
    case Http2Constants.FRAME_RST_STREAM:
      return readReset(is, length, streamId);
      
    case Http2Constants.FRAME_PRIORITY:
      return readPriority(is, length, streamId);
      
    case Http2Constants.FRAME_GOAWAY:
      return readGoAway(is, length);
      
    case Http2Constants.FRAME_BLOCKED:
      return readBlocked(is, length, streamId);
      
    default:
      error("Unknown frame type {0}", type);
      return false;
    }
  }
  
  public boolean readSettings()
    throws IOException
  {
    ReadBuffer is = _is;
    
    int length = BitsUtil.readInt16(is);
    int type = is.read();
    int flags = is.read();
    
    int streamId = BitsUtil.readInt(is);
    
    if (streamId < 0) {
      return false;
    }
    
    if (type != Http2Constants.FRAME_SETTINGS) {
      error("Expected settings at type={0} length={1} streamId={2}",
            type, length, streamId);
      
      return false;
    }
    
    return readSettings(_is, length);
  }
  
  /**
   * settings
   */
  private boolean readSettings(ReadBuffer is, int length)
    throws IOException
  {
    if (length % 5 != 0) {
      error("Invalid settings length {0}", length);
      return false;
    }
    
    int count = length / 5;
    
    for (int i = 0; i < count; i++) {
      int code = is.read() & 0xff;
      int value = BitsUtil.readInt(is);
      
      switch (code) {
      case Http2Constants.SETTINGS_INITIAL_WINDOW_SIZE:
        _peerSettings.setInitialWindowSize(value);
        break;
      }
    }
    
    return true;
  }
  
  /**
   * window-update
   */
  private boolean readFlow(ReadBuffer is, int length, int streamId)
    throws IOException
  {
    if (length != 4) {
      error("Invalid window update length {0}", length);
      return false;
    }
    
    int credit = BitsUtil.readInt(is);
    
    if (streamId == 0) {
      _conn.getChannelZero().addSendCredit(credit);
      return true;
    }
    
    ChannelHttp2 channel = _conn.getChannel(streamId);
    
    if (channel == null) {
      return true;
    }
    
    channel.getOutChannel().addSendCredit(_conn.getOutHttp(), credit);
    
    return true;
  }
  
  /**
   * blocked
   */
  private boolean readBlocked(ReadBuffer is, int length, int streamId)
    throws IOException
  {
    if (length != 0) {
      error("Invalid blocked length {0}", length);
      return false;
    }

    if (log.isLoggable(Level.FINE)) {
      log.fine(L.l("Stream {0} is blocked (in {1})", streamId, this));
    }

    return true;
  }
  
  /**
   * go-away
   */
  private boolean readGoAway(ReadBuffer is, int length)
    throws IOException
  {
    int lastStream = BitsUtil.readInt(is);
    int errorCode = BitsUtil.readInt(is);
    
    is.skip(length - 8);
    
    _isGoAway = true;
    
    _conn.onReadGoAway();
    
    if (onCloseStream() <= 0) {
      _inHandler.onGoAway();
    }
    
    return true;
  }
  
  /**
   * rst_stream
   */
  private boolean readReset(ReadBuffer is, int length, int streamId)
    throws IOException
  {
    int errorCode = BitsUtil.readInt(is);
    
    is.skip(length - 4);
    
    _isGoAway = true;
    
    if (streamId == 0) {
      _inHandler.onGoAway();
      IoUtil.close(is);
      // XXX: close
      return true;
    }

    ChannelHttp2 channel = _conn.getChannel(streamId);
    
    if (channel != null) {
      channel.getInChannel().resetStream(errorCode);
 
      if (onCloseStream() <= 0) {
        _inHandler.onGoAway();
      }
     }
    
    return true;
  }
  
  /**
   * priority
   */
  private boolean readPriority(ReadBuffer is, int length, int streamId)
    throws IOException
  {
    int streamRef = BitsUtil.readInt(is);
    int weight = is.read();
    
    is.skip(length - 5);
    
    return true;
  }
  
  /**
   * data (op=0)
   */
  private boolean readData(ReadBuffer is,
                           int length,
                           int flags,
                           int streamId)
    throws IOException
  {
    ChannelHttp2 channel = _conn.getChannel(streamId);
    
    if (channel == null) {
      throw new IllegalStateException(L.l("Stream {0} is closed", streamId));
    }
    
    ChannelInHttp2 inChannel = channel.getInChannel();
    
    ChannelFlowHttp2 channelZero = _conn.getChannelZero();
    channelZero.onData(length, _conn, 0);
    
    boolean isEndStream = (flags & Http2Constants.END_STREAM) != 0;

    if (! isEndStream) {
      inChannel.onData(length, _conn, streamId);
    }
    
    while (length > 0) {
      TempBuffer tBuf;
    
      if (length < TempBuffer.SMALL_SIZE) {
        tBuf = TempBuffer.allocateSmall();
      }
      else {
        tBuf = TempBuffer.allocate();
      }
      
      byte []buffer = tBuf.buffer();
      
      int sublen = Math.min(length, buffer.length);
      
      sublen = is.readAll(buffer, 0, sublen);
      
      if (sublen < 0) {
        tBuf.freeSelf();
        
        return false;
      }
      
      tBuf.length(sublen);
      inChannel.onData(tBuf);
      
      length -= sublen;
    }
    
    if (isEndStream) {
      inChannel.close();
      
      if (onCloseStream() <= 0) {
        _inHandler.onGoAway();
      }
    }
    
    return true;
  }
  
  InRequest openInitialHeader(boolean isEndStream)
  {
    if (! onPeerOpen()) {
      log.warning("peer-open failed " + this);
      // stream open request after goaway
      return null;
    }
    
    int streamId = 1;
    
    InRequest request = _inHandler.newRequest();
    
    ChannelHttp2 channel = request.getChannel(); // new InChannelHttp2(_conn, streamId, request);
    
    channel.getInChannel().addReceiveCredit(_settings.getInitialWindowSize());

    channel.init(_conn, streamId);
    
    if (isEndStream) {
      channel.closeRead();
    }
    
    return request;
  }
  
  /**
   * header (op=1)
   */
  private boolean readHeader(ReadBuffer is,
                             int length,
                             int flags,
                             int streamId)
    throws IOException
  {
    if (! onPeerOpen()) {
      log.warning("peer-open failed " + this);
      // stream open request after goaway
      return false;
    }
    
    if (streamId <= 0) {
      throw new IllegalStateException("Invalid stream: " + streamId);
    }
    
    
    ChannelHttp2 channel = _conn.getChannel(streamId);
    
    InRequest request;
    
    if (channel != null) {
      request = channel.getRequest();
    }
    else {
      request = _inHandler.newRequest();
    }
    
    // _conn, streamId);
    if (! _inHeader.readHeaders(request, length, flags)) {
      return false;
    }
    
    if (channel == null) {
      channel = request.getChannel(); // new InChannelHttp2(_conn, streamId, request);
      // stream.addReceiveCredit(_settings.getInitialWindowSize());
    
      channel.init(_conn, streamId);
    }
    
    channel.onHeader(streamId);
    
    channel.getInChannel().addReceiveCredit(_settings.getInitialWindowSize());
    
    request.dispatch();
    
    boolean isEndStream = (flags & Http2Constants.END_STREAM) != 0;
    
    if (isEndStream) {
      channel.closeRead();
      
      if (onCloseStream() <= 0) {
        _inHandler.onGoAway();
      }
    }
    
    return true;
  }
  
  private void error(String msg, Object ...args)
  {
    System.out.println(L.l(msg, args));
  }
  
  @Override
  public String toString()
  {
    return getClass().getSimpleName() + "[" + _conn + "]";
  }
  
  private static class DummyConnection extends ConnectionHttp2
  {
    protected DummyConnection(InHttpHandler handler)
    {
      super(handler, PeerHttp.SERVER);
    }
    
    @Override
    public boolean isClosed()
    {
      return false;
    }
    
    @Override
    public void putChannel(int streamId, ChannelHttp2 channel)
    {
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy