/** Non-blocking method, read as much as possible and return. */
  public int processInput() throws IOException {
    while (true) {
      if (inFrame == null) {
        inFrame = getSpdyContext().getFrame();
      }

      if (inFrame.data == null) {
        inFrame.data = new byte[16 * 1024];
      }
      // we might already have data from previous frame
      if (inFrame.endReadData < 8
          || // we don't have the header
          inFrame.endReadData < inFrame.endData) {

        int rd = read(inFrame.data, inFrame.endReadData, inFrame.data.length - inFrame.endReadData);
        if (rd == -1) {
          if (channels.size() == 0) {
            return CLOSE;
          } else {
            abort("Closed");
          }
        } else if (rd < 0) {
          abort("Closed - read error");
          return CLOSE;
        } else if (rd == 0) {
          return LONG;
          // Non-blocking channel - will resume reading at off
        }
        inFrame.endReadData += rd;
      }
      if (inFrame.endReadData < 8) {
        continue; // keep reading
      }
      if (inFrame.endData == 0) {
        inFrame.parse();
        if (inFrame.version != 2) {
          abort("Wrong version");
          return CLOSE;
        }

        // MAX_FRAME_SIZE
        if (inFrame.endData < 0 || inFrame.endData > 32000) {
          abort("Framing error, size = " + inFrame.endData);
          return CLOSE;
        }

        // TODO: if data, split it in 2 frames
        // grow the buffer if needed.
        if (inFrame.data.length < inFrame.endData) {
          byte[] tmp = new byte[inFrame.endData];
          System.arraycopy(inFrame.data, 0, tmp, 0, inFrame.endReadData);
          inFrame.data = tmp;
        }
      }

      if (inFrame.endReadData < inFrame.endData) {
        continue; // keep reading to fill current frame
      }
      // else: we have at least the current frame
      int extra = inFrame.endReadData - inFrame.endData;
      if (extra > 0) {
        // and a bit more - to keep things simple for now we
        // copy them to next frame, at least we saved reads.
        // it is possible to avoid copy - but later.
        nextFrame = getSpdyContext().getFrame();
        nextFrame.makeSpace(extra);
        System.arraycopy(inFrame.data, inFrame.endData, nextFrame.data, 0, extra);
        nextFrame.endReadData = extra;
        inFrame.endReadData = inFrame.endData;
      }

      // decompress
      if (inFrame.type == TYPE_SYN_STREAM) {
        inFrame.streamId = inFrame.readInt(); // 4
        lastChannel = inFrame.streamId;
        inFrame.associated = inFrame.readInt(); // 8
        inFrame.pri = inFrame.read16(); // 10 pri and unused
        if (compressSupport != null) {
          compressSupport.decompress(inFrame, 18);
        }
        inFrame.nvCount = inFrame.read16();

      } else if (inFrame.type == TYPE_SYN_REPLY || inFrame.type == TYPE_HEADERS) {
        inFrame.streamId = inFrame.readInt(); // 4
        inFrame.read16();
        if (compressSupport != null) {
          compressSupport.decompress(inFrame, 14);
        }
        inFrame.nvCount = inFrame.read16();
      }

      if (SpdyContext.debug) {
        trace("< " + inFrame);
      }

      try {
        int state = handleFrame();
        if (state == CLOSE) {
          return state;
        }
      } catch (Throwable t) {
        abort("Error handling frame");
        t.printStackTrace();
        return CLOSE;
      }

      if (inFrame != null) {
        inFrame.recyle();
        if (nextFrame != null) {
          inFrame = nextFrame;
          nextFrame = null;
        }
      } else {
        inFrame = nextFrame;
        nextFrame = null;
        if (inFrame == null) {
          inFrame = getSpdyContext().getFrame();
        }
      }
    }
  }