private CompositeBuffer toCompositeInputContentBuffer() { if (!inputContentBuffer.isComposite()) { final CompositeBuffer compositeBuffer = CompositeBuffer.newBuffer(connection.getMemoryManager()); compositeBuffer.allowBufferDispose(true); compositeBuffer.allowInternalBuffersDispose(true); int posAlign = 0; if (readAheadLimit > 0) { // the simple inputContentBuffer is marked // make the marked data still available inputContentBuffer.position(inputContentBuffer.position() - readCount); posAlign = readCount; markPos = 0; // for the CompositeBuffer markPos is 0 } compositeBuffer.append(inputContentBuffer); compositeBuffer.position(posAlign); inputContentBuffer = compositeBuffer; } return (CompositeBuffer) inputContentBuffer; }
@Override public NextAction handleWrite(FilterChainContext context) throws IOException { Connection<?> connection = context.getConnection(); GrizzlyChannel channel = GrizzlyChannel.getOrAddChannel(connection, url, handler); try { UnsafeByteArrayOutputStream output = new UnsafeByteArrayOutputStream(1024); // 不需要关闭 if (!(context.getMessage() instanceof Response)) { downstreamCodec.encode(channel, output, context.getMessage()); } else { upstreamCodec.encode(channel, output, context.getMessage()); } GrizzlyChannel.removeChannelIfDisconnectd(connection); byte[] bytes = output.toByteArray(); Buffer buffer = connection.getTransport().getMemoryManager().allocate(bytes.length); buffer.put(bytes); buffer.flip(); buffer.allowBufferDispose(true); context.setMessage(buffer); } finally { GrizzlyChannel.removeChannelIfDisconnectd(connection); } return context.getInvokeAction(); }
/** * 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; }
/** {@inheritDoc} */ public String getResponseBodyExcerpt(int maxLength, String charset) throws IOException { charset = calculateCharset(charset); final Buffer responseBody = getResponseBody0(); final int len = Math.min(responseBody.remaining(), maxLength); final int pos = responseBody.position(); return responseBody.toStringContent(getCharset(charset), pos, len + pos); }
/** @see java.io.InputStream#read(byte[], int, int) */ public int read(final byte b[], final int off, final int len) throws IOException { if (LOGGER.isLoggable(LOGGER_LEVEL)) { log( "InputBuffer %s read byte array of len: %s. Ready content: %s", this, len, inputContentBuffer); } if (closed) { throw new IOException(); } if (len == 0) { return 0; } if (!inputContentBuffer.hasRemaining()) { if (fill(1) == -1) { return -1; } } int nlen = Math.min(inputContentBuffer.remaining(), len); inputContentBuffer.get(b, off, nlen); if (!checkMarkAfterRead(nlen)) { inputContentBuffer.shrink(); } return nlen; }
/** * 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; }
@Test public void testFormParameters() throws Exception { final Map<String, String[]> patternMap = new HashMap<String, String[]>(); patternMap.put("title", new String[] {"Developing PaaS Components"}); patternMap.put("authors", new String[] {"Shalini M"}); patternMap.put("price", new String[] {"100$"}); final NetworkListener listener = httpServer.getListener(LISTENER_NAME); startHttpServer( new HttpHandler() { @Override public void service(Request request, Response response) throws Exception { final Map<String, String[]> paramMap = request.getParameterMap(); boolean isOk = paramMap.size() == patternMap.size(); if (isOk) { // if sizes are equal - compare content for (Map.Entry<String, String[]> patternEntry : patternMap.entrySet()) { final String key = patternEntry.getKey(); final String[] value = patternEntry.getValue(); isOk = paramMap.containsKey(key) && Arrays.equals(value, paramMap.get(key)); if (!isOk) break; } } if (isOk) { response.setStatus(200, "FINE"); } else { response.setStatus(500, "Attributes don't match"); } } }, "/bookstore/BookStoreServlet"); final MemoryManager mm = listener.getTransport().getMemoryManager(); final Buffer requestPart1 = Buffers.wrap(mm, Utils.loadResourceFile("form-params-payload1.dat")); final Buffer requestPart2 = Buffers.wrap(mm, Utils.loadResourceFile("form-params-payload2.dat")); Buffer responseBuffer = send("localhost", PORT, Buffers.appendBuffers(mm, requestPart1, requestPart2)) .get(10, TimeUnit.SECONDS); // Successful response length is 37 bytes. This includes the status // line and a content-length boolean isFailure = responseBuffer.remaining() != 37; if (isFailure) { byte[] response = new byte[responseBuffer.remaining()]; responseBuffer.get(response); String hex = toHexString(response); fail("unexpected response length=" + response.length + " content=[" + hex + "]"); } }
public Buffer asClientCookieBuffer(MemoryManager memoryManager) { if (memoryManager == null) memoryManager = MemoryManager.DEFAULT_MEMORY_MANAGER; final Buffer buffer = memoryManager.allocate(4096); CookieSerializerUtils.serializeClientCookies(buffer, this); buffer.trim(); return buffer; }
/** {@inheritDoc} */ @Override public byte[] getResponseBodyAsBytes() throws IOException { final Buffer responseBody = getResponseBody0(); final byte[] responseBodyBytes = new byte[responseBody.remaining()]; final int origPos = responseBody.position(); responseBody.get(responseBodyBytes); responseBody.position(origPos); return responseBodyBytes; }
private static Buffer putShort( final MemoryManager memoryManager, Buffer dstBuffer, final int value) { if (dstBuffer.remaining() < 2) { dstBuffer = resizeBuffer(memoryManager, dstBuffer, 2); } dstBuffer.putShort((short) value); return dstBuffer; }
static void put(Buffer dstBuffer, String s) { final int size = s.length(); if (dstBuffer.remaining() < size) { throw new BufferOverflowException(); } for (int i = 0; i < size; i++) { dstBuffer.put((byte) s.charAt(i)); } }
@Test public void testSslParams() throws Exception { final NetworkListener listener = httpServer.getListener(LISTENER_NAME); startHttpServer( new HttpHandler() { @Override public void service(Request request, Response response) throws Exception { boolean isOk = request.isSecure(); String error = "unknown"; if (isOk) { try { assertEquals( (Integer) 256, (Integer) request.getAttribute(SSLSupport.KEY_SIZE_KEY)); assertNotNull(request.getAttribute(SSLSupport.SESSION_ID_KEY)); assertNotNull(request.getAttribute(SSLSupport.CIPHER_SUITE_KEY)); assertNotNull(request.getAttribute(SSLSupport.CERTIFICATE_KEY)); } catch (Exception e) { error = e.getClass().getName() + ": " + e.getMessage(); isOk = false; } } if (isOk) { response.setStatus(200, "FINE"); } else { response.setStatus(500, error); } } }); final MemoryManager mm = listener.getTransport().getMemoryManager(); final Buffer request = Buffers.wrap(mm, Utils.loadResourceFile("get-secured.dat")); Buffer responseBuffer = send("localhost", PORT, request).get(10, TimeUnit.SECONDS); // Successful response length is 37 bytes. This includes the status // line and a content-length boolean isFailure = responseBuffer.remaining() != 37; if (isFailure) { byte[] response = new byte[responseBuffer.remaining()]; responseBuffer.get(response); String hex = toHexString(response); fail("unexpected response length=" + response.length + " content=[" + hex + "]"); } }
@Test public void testShutdownHandler() throws Exception { final FutureImpl<Boolean> shutdownFuture = SafeFutureImpl.create(); final ShutdownHandler shutDownHandler = new ShutdownHandler() { @Override public void onShutdown(Connection initiator) { shutdownFuture.result(true); } }; AjpAddOn myAjpAddon = new AjpAddOn() { @Override protected AjpHandlerFilter createAjpHandlerFilter() { final AjpHandlerFilter filter = new AjpHandlerFilter(); filter.addShutdownHandler(shutDownHandler); return filter; } }; final NetworkListener listener = httpServer.getListener(LISTENER_NAME); listener.deregisterAddOn(ajpAddon); listener.registerAddOn(myAjpAddon); startHttpServer( new HttpHandler() { @Override public void service(Request request, Response response) throws Exception {} }, "/"); final MemoryManager mm = listener.getTransport().getMemoryManager(); final Buffer request = mm.allocate(512); request.put((byte) 0x12); request.put((byte) 0x34); request.putShort((short) 1); request.put(AjpConstants.JK_AJP13_SHUTDOWN); request.flip(); send("localhost", PORT, request); final Boolean b = shutdownFuture.get(10, TimeUnit.SECONDS); assertTrue(b); }
/** Recycle this <code>InputBuffer</code> for reuse. */ public void recycle() { inputContentBuffer.tryDispose(); inputContentBuffer = null; singleCharBuf.position(singleCharBuf.limit()); connection = null; decoder = null; ctx = null; handler = null; processingChars = false; closed = false; contentRead = false; markPos = -1; readAheadLimit = -1; requestedSize = -1; readCount = 0; averageCharsPerByte = 1.0f; isWaitingDataAsynchronously = false; encoding = DEFAULT_HTTP_CHARACTER_ENCODING; }
protected void updateInputContentBuffer(final Buffer buffer) { buffer.allowBufferDispose(true); if (inputContentBuffer == null) { inputContentBuffer = buffer; } else if (inputContentBuffer.hasRemaining() || readAheadLimit > 0) { // if the stream is marked - we can't dispose the inputContentBuffer, even if // it's been read off toCompositeInputContentBuffer().append(buffer); } else { inputContentBuffer.tryDispose(); inputContentBuffer = buffer; } }
@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 }
/** * @return the underlying {@link Buffer} used to buffer incoming request data. Unlike {@link * #getBuffer()}, this method detaches the returned {@link Buffer}, so user code becomes * responsible for handling the {@link Buffer}. */ public Buffer readBuffer() { if (LOGGER.isLoggable(LOGGER_LEVEL)) { log("InputBuffer %s readBuffer. Ready content: %s", this, inputContentBuffer); } return readBuffer(inputContentBuffer.remaining()); }
/** * Returns the duplicate of the underlying {@link Buffer} used to buffer incoming request data. * The content of the returned buffer will be that of the underlying buffer. Changes to returned * buffer's content will be visible in the underlying buffer, and vice versa; the two buffers' * position, limit, and mark values will be independent. * * @return the duplicate of the underlying {@link Buffer} used to buffer incoming request data. */ public Buffer getBuffer() { if (LOGGER.isLoggable(LOGGER_LEVEL)) { log("InputBuffer %s getBuffer. Ready content: %s", this, inputContentBuffer); } return inputContentBuffer.duplicate(); }
public Result find(PUContext puc, FilterChainContext fcc) { final Buffer buffer = fcc.getMessage(); if (buffer.remaining() >= signature.length) { final int start = buffer.position(); for (int i = 0; i < signature.length; i++) { if (buffer.get(start + i) != signature[i]) { return Result.NOT_FOUND; } } return Result.FOUND; } return Result.NEED_MORE_DATA; }
private static Buffer putBytes( final MemoryManager memoryManager, Buffer dstBuffer, final byte[] bytes) { final int size = bytes.length; // Don't forget the terminating \0 (that's why "+ 1") if (dstBuffer.remaining() < size + 2 + 1) { dstBuffer = resizeBuffer(memoryManager, dstBuffer, size + 2 + 1); } dstBuffer.putShort((short) size); dstBuffer = put(memoryManager, dstBuffer, bytes); // Don't forget the terminating \0 dstBuffer.put((byte) 0); return dstBuffer; }
/** * Supported with binary and character data. * * @see java.io.InputStream#mark(int) * @see java.io.Reader#mark(int) */ public void mark(final int readAheadLimit) { if (readAheadLimit > 0) { markPos = inputContentBuffer.position(); readCount = 0; this.readAheadLimit = readAheadLimit; } }
/** * This method always blocks. * * @see java.io.InputStream#read() */ public int readByte() throws IOException { if (LOGGER.isLoggable(LOGGER_LEVEL)) { log("InputBuffer %s readByte. Ready content: %s", this, inputContentBuffer); } if (closed) { throw new IOException(); } if (!inputContentBuffer.hasRemaining()) { if (fill(1) == -1) { return -1; } } checkMarkAfterRead(1); return inputContentBuffer.get() & 0xFF; }
@Override public HttpContent encode(Connection connection, HttpContent httpContent) { final HttpHeader httpHeader = httpContent.getHttpHeader(); final Buffer input = httpContent.getContent(); if (httpContent.isLast() && !input.hasRemaining()) { return httpContent; } final TransformationResult<Buffer, Buffer> result = encoder.transform(httpContent.getHttpHeader(), input); input.tryDispose(); try { switch (result.getStatus()) { case COMPLETE: encoder.finish(httpHeader); case INCOMPLETE: { Buffer encodedBuffer = result.getMessage(); if (encodedBuffer != null) { httpContent.setContent(encodedBuffer); return httpContent; } else { return null; } } case ERROR: { throw new IllegalStateException( "LZMA encode error. Code: " + result.getErrorCode() + " Description: " + result.getErrorDescription()); } default: throw new IllegalStateException("Unexpected status: " + result.getStatus()); } } finally { result.recycle(); } }
@Override public ParsingResult decode(Connection connection, HttpContent httpContent) { final HttpHeader httpHeader = httpContent.getHttpHeader(); final Buffer input = httpContent.getContent(); final TransformationResult<Buffer, Buffer> result = decoder.transform(httpHeader, input); Buffer remainder = result.getExternalRemainder(); if (remainder == null || !remainder.hasRemaining()) { input.tryDispose(); remainder = null; } else { input.shrink(); } try { switch (result.getStatus()) { case COMPLETE: { httpContent.setContent(result.getMessage()); decoder.finish(httpHeader); return ParsingResult.create(httpContent, remainder); } case INCOMPLETE: { return ParsingResult.create(null, remainder); } case ERROR: { throw new IllegalStateException( "LZMA decode error. Code: " + result.getErrorCode() + " Description: " + result.getErrorDescription()); } default: throw new IllegalStateException("Unexpected status: " + result.getStatus()); } } finally { result.recycle(); } }
/** @see java.io.Reader#ready() */ public boolean ready() { if (closed) { return false; } if (!processingChars) { throw new IllegalStateException(); } return (inputContentBuffer.hasRemaining() || httpHeader.isExpectContent()); }
@Override public void notifyDirectUpdate() { if (type == Type.Buffer) { final int start = getStart(); final int end = getEnd(); final byte[] bytes = new byte[end - start]; final Buffer currentBuffer = getBufferChunk().getBuffer(); final int pos = currentBuffer.position(); final int lim = currentBuffer.limit(); Buffers.setPositionLimit(currentBuffer, start, end); currentBuffer.get(bytes); Buffers.setPositionLimit(currentBuffer, pos, lim); setBytes(bytes); } }
@Override public NextAction handleRead(FilterChainContext context) throws IOException { Object message = context.getMessage(); Connection<?> connection = context.getConnection(); Channel channel = GrizzlyChannel.getOrAddChannel(connection, url, handler); try { if (message instanceof Buffer) { // 收到新的数据包 Buffer buffer = (Buffer) message; // 缓存 int readable = buffer.capacity(); // 本次可读取新数据的大小 if (readable == 0) { return context.getStopAction(); } byte[] bytes; // byte[]缓存区,将Buffer转成byte[],再转成UnsafeByteArrayInputStream int offset; // 指向已用数据的偏移量,off之前的数据都是已用过的 int limit; // 有效长度,limit之后的长度是空白或无效数据,off到limit之间的数据是准备使用的有效数据 Object[] remainder = (Object[]) channel.getAttribute(BUFFER_KEY); // 上次序列化剩下的数据 channel.removeAttribute(BUFFER_KEY); if (remainder == null) { // 如果没有,创建新的bytes缓存 bytes = new byte[bufferSize]; offset = 0; limit = 0; } else { // 如果有,使用剩下的bytes缓存 bytes = (byte[]) remainder[0]; offset = (Integer) remainder[1]; limit = (Integer) remainder[2]; } return receive(context, channel, buffer, readable, bytes, offset, limit); } else if (message instanceof Object[]) { // 同一Buffer多轮Filter,即:一个Buffer里有多个请求 Object[] remainder = (Object[]) message; Buffer buffer = (Buffer) remainder[0]; int readable = (Integer) remainder[1]; byte[] bytes = (byte[]) remainder[2]; int offset = (Integer) remainder[3]; int limit = (Integer) remainder[4]; return receive(context, channel, buffer, readable, bytes, offset, limit); } else { // 其它事件直接往下传 return context.getInvokeAction(); } } finally { GrizzlyChannel.removeChannelIfDisconnectd(connection); } }
/** * Used to convert pre-read (buffered) bytes to chars. * * @param requestedLen how much content should attempt to be read * @return the number of chars actually read */ private int fillAvailableChars(final int requestedLen, final CharBuffer dst) { final CharsetDecoder decoderLocal = getDecoder(); final ByteBuffer bb = inputContentBuffer.toByteBuffer(); final int oldBBPos = bb.position(); int producedChars = 0; int consumedBytes = 0; int producedCharsNow; int consumedBytesNow; CoderResult result; int remaining = requestedLen; do { final int charPos = dst.position(); final int bbPos = bb.position(); result = decoderLocal.decode(bb, dst, false); producedCharsNow = dst.position() - charPos; consumedBytesNow = bb.position() - bbPos; producedChars += producedCharsNow; consumedBytes += consumedBytesNow; remaining -= producedCharsNow; } while (remaining > 0 && (producedCharsNow > 0 || consumedBytesNow > 0) && bb.hasRemaining() && result == CoderResult.UNDERFLOW); bb.position(oldBBPos); inputContentBuffer.position(inputContentBuffer.position() + consumedBytes); if (readAheadLimit == -1) { inputContentBuffer.shrink(); } return producedChars; }
static void decodeRequest( final Buffer requestContent, final AjpHttpRequest req, final boolean tomcatAuthentication) throws IOException { // FORWARD_REQUEST handler int offset = requestContent.position(); // Translate the HTTP method code to a String. byte methodCode = requestContent.get(offset++); if (methodCode != AjpConstants.SC_M_JK_STORED) { String mName = AjpConstants.methodTransArray[(int) methodCode - 1]; req.getMethodDC().setString(mName); } offset = getBytesToDataChunk(requestContent, offset, req.getProtocolDC()); final int requestURILen = readShort(requestContent, offset); if (!isNullLength(requestURILen)) { req.getRequestURIRef().init(requestContent, offset + 2, offset + 2 + requestURILen); } // Don't forget to skip the terminating \0 (that's why "+ 1") offset += 2 + requestURILen + 1; offset = getBytesToDataChunk(requestContent, offset, req.remoteAddr()); offset = getBytesToDataChunk(requestContent, offset, req.remoteHostRaw()); offset = getBytesToDataChunk(requestContent, offset, req.localName()); req.setLocalPort(readShort(requestContent, offset)); offset += 2; final boolean isSSL = requestContent.get(offset++) != 0; req.setSecure(isSSL); req.getResponse().setSecure(isSSL); offset = decodeHeaders(requestContent, offset, req); decodeAttributes(requestContent, offset, req, tomcatAuthentication); req.setUnparsedHostHeader(req.getHeaders().getValue("host")); }
@Test public void testNullAttribute() throws Exception { final NetworkListener listener = httpServer.getListener(LISTENER_NAME); startHttpServer( new HttpHandler() { @Override public void service(Request request, Response response) throws Exception { final Set<String> attributeNames = request.getAttributeNames(); final boolean isOk = attributeNames.contains("JK_LB_ACTIVATION") && request.getAttribute("JK_LB_ACTIVATION") == null && attributeNames.contains("AJP_REMOTE_PORT") && "60955".equals(request.getAttribute("AJP_REMOTE_PORT")); if (isOk) { response.setStatus(200, "FINE"); } else { response.setStatus(500, "Attributes don't match"); } } }, "/SimpleWebApp/SimpleServlet"); final MemoryManager mm = listener.getTransport().getMemoryManager(); final Buffer request = Buffers.wrap(mm, Utils.loadResourceFile("null-attr-payload.dat")); Buffer responseBuffer = send("localhost", PORT, request).get(10, TimeUnit.SECONDS); // Successful response length is 37 bytes. This includes the status // line and a content-length boolean isFailure = responseBuffer.remaining() != 37; if (isFailure) { byte[] response = new byte[responseBuffer.remaining()]; responseBuffer.get(response); String hex = toHexString(response); fail("unexpected response length=" + response.length + " content=[" + hex + "]"); } }