/** * Used to add additional HTTP message chunk content to {@link #inputContentBuffer}. * * @param requestedLen how much content should attempt to be read, <code>-1</code> means read till * the end of the message. * @return the number of bytes actually read * @throws IOException if an I/O error occurs while reading content */ private int fill(final int requestedLen) throws IOException { int read = 0; while ((requestedLen == -1 || read < requestedLen) && httpHeader.isExpectContent()) { final HttpContent c = blockingRead(); final boolean isLast = c.isLast(); // Check if HttpContent is chunked message trailer w/ headers checkHttpTrailer(c); final Buffer b; try { b = c.getContent(); } catch (HttpBrokenContentException e) { final Throwable cause = e.getCause(); throw Exceptions.makeIOException(cause != null ? cause : e); } read += b.remaining(); updateInputContentBuffer(b); c.recycle(); if (isLast) { finished(); break; } } if (read > 0 || requestedLen == 0) { return read; } return -1; }
/** * Appends the specified {@link Buffer} to the internal composite {@link Buffer}. * * @param httpContent the {@link HttpContent} to append * @return <code>true</code> if {@link ReadHandler} callback was invoked, otherwise returns <code> * false</code>. * @throws IOException if an error occurs appending the {@link Buffer} */ public boolean append(final HttpContent httpContent) throws IOException { // Stop waiting for data asynchronously and enable it again // only if we have a handler registered, which requirement // (expected size) is not met. isWaitingDataAsynchronously = false; // check if it's broken HTTP content message or not if (!HttpContent.isBroken(httpContent)) { final Buffer buffer = httpContent.getContent(); if (closed) { buffer.dispose(); return false; } final ReadHandler localHandler = handler; final boolean isLast = httpContent.isLast(); // if we have a handler registered - switch the flag to true boolean askForMoreDataInThisThread = !isLast && localHandler != null; boolean invokeDataAvailable = false; if (buffer.hasRemaining()) { updateInputContentBuffer(buffer); if (localHandler != null) { final int available = readyData(); if (available >= requestedSize) { invokeDataAvailable = true; askForMoreDataInThisThread = false; } } } if (askForMoreDataInThisThread) { // There is a ReadHandler registered, but it requested more // data to be available before we can notify it - so wait for // more data to come isWaitingDataAsynchronously = true; return true; } handler = null; if (isLast) { checkHttpTrailer(httpContent); } invokeHandlerOnProperThread(localHandler, invokeDataAvailable, isLast); } else { // broken content final ReadHandler localHandler = handler; handler = null; invokeErrorHandlerOnProperThread( localHandler, ((HttpBrokenContent) httpContent).getException()); } return false; }
@Override public NextAction handleRead(FilterChainContext ctx) throws IOException { // Get the parsed HttpContent (we assume prev. filter was HTTP) HttpContent message = ctx.getMessage(); Socket tunnelSocket = tunnelSockets.get(ctx.getConnection()); if (tunnelSocket == null) { // handle connection procedure return GrizzlyModProxy.this.handleConnect(ctx, message); } if (message.getContent().hasRemaining()) { // relay the content to the tunnel connection Buffer buffer = message.getContent(); message.recycle(); tunnelSocket .getOutputStream() .write(buffer.array(), buffer.arrayOffset(), buffer.remaining()); } return ctx.getStopAction(); }
@Override public NextAction handleRead(FilterChainContext ctx) throws IOException { HttpContent c = (HttpContent) ctx.getMessage(); Buffer b = c.getContent(); if (b.hasRemaining()) { sb.append(b.toStringContent()); } // Last content from the server, set the future result so // the client can display the result and gracefully exit. if (c.isLast()) { future.result(sb.toString()); } return ctx.getStopAction(); // discontinue filter chain execution }
/** * Per-request initialization required for the InputBuffer to function properly. * * @param httpHeader the header from which input will be obtained. * @param ctx the FilterChainContext for the chain processing this request */ public void initialize(final HttpHeader httpHeader, final FilterChainContext ctx) { if (ctx == null) { throw new IllegalArgumentException("ctx cannot be null."); } this.httpHeader = httpHeader; this.ctx = ctx; connection = ctx.getConnection(); final Object message = ctx.getMessage(); if (message instanceof HttpContent) { final HttpContent content = (HttpContent) message; // Check if HttpContent is chunked message trailer w/ headers checkHttpTrailer(content); updateInputContentBuffer(content.getContent()); contentRead = content.isLast(); content.recycle(); if (LOGGER.isLoggable(LOGGER_LEVEL)) { log("InputBuffer %s initialize with ready content: %s", this, inputContentBuffer); } } }
/** * Used to convert bytes to chars. * * @param requestedLen how much content should attempt to be read * @return the number of chars actually read * @throws IOException if an I/O error occurs while reading content */ private int fillChars(final int requestedLen, final CharBuffer dst) throws IOException { int read = 0; // 1) Check pre-decoded singleCharBuf if (dst != singleCharBuf && singleCharBuf.hasRemaining()) { dst.put(singleCharBuf.get()); read = 1; } // 2) Decode available byte[] -> char[] if (inputContentBuffer.hasRemaining()) { read += fillAvailableChars(requestedLen - read, dst); } if (read >= requestedLen) { dst.flip(); return read; } // 3) If we don't expect more data - return what we've read so far if (!httpHeader.isExpectContent()) { dst.flip(); return read > 0 ? read : -1; } // 4) Try to read more data (we may block) CharsetDecoder decoderLocal = getDecoder(); boolean isNeedMoreInput = false; // true, if content in composite buffer is not enough to produce even 1 char boolean last = false; while (read < requestedLen && httpHeader.isExpectContent()) { if (isNeedMoreInput || !inputContentBuffer.hasRemaining()) { final HttpContent c = blockingRead(); updateInputContentBuffer(c.getContent()); last = c.isLast(); c.recycle(); isNeedMoreInput = false; } final ByteBuffer bytes = inputContentBuffer.toByteBuffer(); final int bytesPos = bytes.position(); final int dstPos = dst.position(); final CoderResult result = decoderLocal.decode(bytes, dst, false); final int producedChars = dst.position() - dstPos; final int consumedBytes = bytes.position() - bytesPos; read += producedChars; if (consumedBytes > 0) { bytes.position(bytesPos); inputContentBuffer.position(inputContentBuffer.position() + consumedBytes); if (readAheadLimit == -1) { inputContentBuffer.shrink(); } } else { isNeedMoreInput = true; } if (last || result == CoderResult.OVERFLOW) { break; } } dst.flip(); if (last && read == 0) { read = -1; } return read; }