/** 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();
        }
      }
    }
  }
  /** Non blocking if the socket is not blocking. */
  private boolean _drain() {
    while (true) {
      synchronized (outQueue) {
        if (out == null) {
          out = prioriyQueue.poll();
          if (out == null) {
            out = outQueue.poll();
          }
          if (out == null) {
            return false;
          }
          if (goAway < out.streamId) {
            // TODO
          }
          try {
            if (!out.c) {
              // late: IDs are assigned as we send ( priorities may affect
              // the transmission order )
              if (out.stream != null) {
                out.streamId = out.stream.getRequest().streamId;
              }
            } else if (out.type == TYPE_SYN_STREAM) {
              out.fixNV(18);
              if (compressSupport != null) {
                compressSupport.compress(out, 18);
              }
            } else if (out.type == TYPE_SYN_REPLY || out.type == TYPE_HEADERS) {
              out.fixNV(14);
              if (compressSupport != null) {
                compressSupport.compress(out, 14);
              }
            }
          } catch (IOException ex) {
            abort("Compress error");
            return false;
          }
          if (out.type == TYPE_SYN_STREAM) {
            out.streamId = outStreamId;
            outStreamId += 2;
            synchronized (channels) {
              channels.put(Integer.valueOf(out.streamId), out.stream);
            }
          }

          out.serializeHead();
        }
        if (out.endData == out.off) {
          out = null;
          continue;
        }
      }

      if (SpdyContext.debug) {
        trace("> " + out);
      }

      try {
        int toWrite = out.endData - out.off;
        int wr;
        while (toWrite > 0) {
          wr = write(out.data, out.off, toWrite);
          if (wr < 0) {
            return false;
          }
          if (wr == 0) {
            return true; // non blocking or to
          }
          if (wr <= toWrite) {
            out.off += wr;
            toWrite -= wr;
          }
        }

        synchronized (channels) {
          if (out.stream != null) {
            if (out.isHalfClose()) {
              out.stream.finSent = true;
            }
            if (out.stream.finRcvd && out.stream.finSent) {
              channels.remove(Integer.valueOf(out.streamId));
            }
          }
        }
        out = null;
      } catch (IOException e) {
        // connection closed - abort all streams
        e.printStackTrace();
        onClose();
        return false;
      }
    }
  }