private void queueFrame(SpdyFrame oframe, SpdyStream proc, LinkedList<SpdyFrame> queue) { oframe.endData = oframe.off; oframe.off = 0; // We can't assing a stream ID until it is sent - priorities // we can't compress either - it's stateful. oframe.stream = proc; // all sync for adding/removing is on outQueue synchronized (outQueue) { queue.add(oframe); } }
/** * Process a SPDY connection. Called in the input thread, should not block. * * @throws IOException */ protected int handleFrame() throws IOException { if (inFrame.c) { switch (inFrame.type) { case TYPE_SETTINGS: { int cnt = inFrame.readInt(); for (int i = 0; i < cnt; i++) { inFrame.readByte(); inFrame.read24(); inFrame.readInt(); } // TODO: save/interpret settings break; } case TYPE_GOAWAY: { int lastStream = inFrame.readInt(); log.info("GOAWAY last=" + lastStream); // Server will shut down - but will keep processing the current requests, // up to lastStream. If we sent any new ones - they need to be canceled. abort("GO_AWAY", lastStream); goAway = lastStream; return CLOSE; } case TYPE_RST_STREAM: { inFrame.streamId = inFrame.read32(); int errCode = inFrame.read32(); if (SpdyContext.debug) { trace( "> RST " + inFrame.streamId + " " + ((errCode < RST_ERRORS.length) ? RST_ERRORS[errCode] : Integer.valueOf(errCode))); } SpdyStream sch; synchronized (channels) { sch = channels.remove(Integer.valueOf(inFrame.streamId)); } // if RST stream is for a closed channel - we can ignore. if (sch != null) { sch.onReset(); } inFrame = null; break; } case TYPE_SYN_STREAM: { SpdyStream ch = getSpdyContext().getStream(this); synchronized (channels) { channels.put(Integer.valueOf(inFrame.streamId), ch); } try { ch.onCtlFrame(inFrame); inFrame = null; } catch (Throwable t) { log.log(Level.SEVERE, "Error parsing head SYN_STREAM", t); abort("Error reading headers " + t); return CLOSE; } spdyContext.onStream(this, ch); break; } case TYPE_SYN_REPLY: { SpdyStream sch; synchronized (channels) { sch = channels.get(Integer.valueOf(inFrame.streamId)); } if (sch == null) { abort("Missing channel"); return CLOSE; } try { sch.onCtlFrame(inFrame); inFrame = null; } catch (Throwable t) { log.info("Error parsing head SYN_STREAM" + t); abort("Error reading headers " + t); return CLOSE; } break; } case TYPE_PING: { SpdyFrame oframe = getSpdyContext().getFrame(); oframe.type = TYPE_PING; oframe.c = true; oframe.append32(inFrame.read32()); oframe.pri = 0x80; send(oframe, null); break; } } } else { // Data frame SpdyStream sch; synchronized (channels) { sch = channels.get(Integer.valueOf(inFrame.streamId)); } if (sch == null) { abort("Missing channel"); return CLOSE; } sch.onDataFrame(inFrame); synchronized (channels) { if (sch.finRcvd && sch.finSent) { channels.remove(Integer.valueOf(inFrame.streamId)); } } inFrame = null; } return LONG; }
/** 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; } } }
public SpdyFrame getFrame(int type) { SpdyFrame frame = getSpdyContext().getFrame(); frame.c = true; frame.type = type; return frame; }