/** @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; }
@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 + "]"); } }
/** {@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; }
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")); }
/* * 接收 * * @param context 上下文 * @param channel 通道 * @param buffer 缓存 * @param readable 缓存可读 * @param bytes 输入缓存 * @param offset 指向已读数据的偏移量,off之前的数据都是已用过的 * @param limit 有效长度,limit之后的长度是空白或无效数据,off到limit之间的数据是准备使用的数据 * @return 后续动作 * @throws IOException */ private NextAction receive( FilterChainContext context, Channel channel, Buffer buffer, int readable, byte[] bytes, int offset, int limit) throws IOException { for (; ; ) { int read = Math.min(readable, bytes.length - limit); // 取bytes缓存空闲区,和可读取新数据,的最小值,即:此次最多读写数据的大小 buffer.get(bytes, limit, read); // 从可读取新数据中,读取数据,尽量填满bytes缓存空闲区 limit += read; // 有效数据变长 readable -= read; // 可读数据变少 UnsafeByteArrayInputStream input = new UnsafeByteArrayInputStream( bytes, offset, limit - offset); // 将bytes缓存转成InputStream,不需要关闭 Object msg = upstreamCodec.decode(channel, input); // 调用Codec接口,解码数据 if (msg == Codec.NEED_MORE_INPUT) { // 如果Codec觉得数据不够,不足以解码成一个对象 if (readable == 0) { // 如果没有更多可读数据 channel.setAttribute( BUFFER_KEY, new Object[] {bytes, offset, limit}); // 放入通道属性中,等待下一个Buffer的到来 return context.getStopAction(); } else { // 扩充或挪出空闲区,并循环,直到可读数据都加载到bytes缓存 if (offset == 0) { // 如果bytes缓存全部没有被使用,如果这时数据还不够 bytes = Bytes.copyOf(bytes, bytes.length << 1); // 将bytes缓存扩大一倍 } else { // 如果bytes缓存有一段数据已被使用 int len = limit - offset; // 计算有效数据长度 System.arraycopy( bytes, offset, bytes, 0, len); // 将数据向前移到,压缩到已使用的部分,这样limit后面就会多出一些空闲,可以放数据 offset = 0; // 移到后,bytes缓存没有数据被使用 limit = len; // 移到后,有效数据都在bytes缓存最前面 } } } else { // 如果解析出一个结果 int position = input.position(); // 记录InputStream用了多少 if (position == offset) { // 如果InputStream没有被读过,就返回了数据,直接报错,否则InputStream永远读不完,会死递归 throw new IOException("Decode without read data."); } offset = position; // 记录已读数据 context.setMessage(msg); // 将消息改为解码后的对象,以便被后面的Filter使用。 if (limit - offset > 0 || readable > 0) { // 如果有效数据没有被读完,或者Buffer区还有未读数据 return context.getInvokeAction( new Object[] { buffer, readable, bytes, offset, limit }); // 正常执行完Filter,并重新发起一轮Filter,继续读 } else { // 否则所有数据读完 return context.getInvokeAction(); // 正常执行完Filter } } } }
@Test public void testPingPong() throws Exception { startHttpServer( new HttpHandler() { @Override public void service(Request request, Response response) throws Exception { response.setStatus(200, "FINE"); } }, "/"); final MemoryManager mm = httpServer.getListener(LISTENER_NAME).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_CPING_REQUEST); request.flip(); final Future<Buffer> responseFuture = send("localhost", PORT, request); Buffer response = responseFuture.get(10, TimeUnit.SECONDS); assertEquals('A', response.get()); assertEquals('B', response.get()); assertEquals((short) 1, response.getShort()); assertEquals(AjpConstants.JK_AJP13_CPONG_REPLY, response.get()); final AjpForwardRequestPacket headersPacket = new AjpForwardRequestPacket("GET", "/TestServlet/normal", 80, PORT); headersPacket.addHeader("Host", "localhost:80"); send(headersPacket.toByteArray()); AjpResponse ajpResponse = Utils.parseResponse(readAjpMessage()); Assert.assertEquals("FINE", ajpResponse.getResponseMessage()); }
@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 + "]"); } }
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; }
/** * 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 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); } }
@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 + "]"); } }
@Override public void process(Buffer source, HeaderFieldTable.DecTable table, HeaderListener handler) { ObjectHolder<String> s = new ObjectHolder<>(); String name; int beginning = source.position(); byte b = source.get(); if ((b & 0b111111) == 0) { readString(source, s); name = s.getObj(); } else { source.position(beginning); int index = readInteger(source, 6); HeaderField e = table.get(index); name = e.getName(); } readString(source, s); String value = s.getObj(); HeaderField f = new HeaderField(name, value); table.put(f); handler.onDecodedHeader(name, value); }
/** * Create a multipath based NFSv4.1 file layout address. * * @param stripingPattern of the device * @param deviceAddress * @return device address */ public static device_addr4 deviceAddrOf( StripingPattern<InetSocketAddress[]> stripingPattern, InetSocketAddress[]... deviceAddress) { nfsv4_1_file_layout_ds_addr4 file_type = new nfsv4_1_file_layout_ds_addr4(); file_type.nflda_multipath_ds_list = new multipath_list4[deviceAddress.length]; for (int i = 0; i < deviceAddress.length; i++) { file_type.nflda_multipath_ds_list[i] = toMultipath(deviceAddress[i]); } file_type.nflda_stripe_indices = stripingPattern.getPattern(deviceAddress); XdrBuffer xdr = new XdrBuffer(128); try { xdr.beginEncoding(); file_type.xdrEncode(xdr); xdr.endEncoding(); } catch (OncRpcException e) { /* forced by interface, should never happen. */ throw new RuntimeException("Unexpected OncRpcException:", e); } catch (IOException e) { /* forced by interface, should never happen. */ throw new RuntimeException("Unexpected IOException:", e); } Buffer body = xdr.asBuffer(); byte[] retBytes = new byte[body.remaining()]; body.get(retBytes); device_addr4 addr = new device_addr4(); addr.da_layout_type = layouttype4.LAYOUT4_NFSV4_1_FILES; addr.da_addr_body = retBytes; return addr; }
@Test public void testAddresses() throws Exception { final String expectedRemoteAddr = "10.163.27.8"; final String expectedLocalAddr = "10.163.25.1"; final NetworkListener listener = httpServer.getListener(LISTENER_NAME); startHttpServer( new HttpHandler() { @Override public void service(Request request, Response response) throws Exception { boolean isOk = false; final StringBuilder errorBuilder = new StringBuilder(); try { String result = request.getRemoteAddr(); isOk = expectedRemoteAddr.equals(result); if (!isOk) { errorBuilder .append("Remote host don't match. Expected ") .append(expectedRemoteAddr) .append(" but was ") .append(result) .append('\n'); } String localName = request.getLocalName(); String localAddr = request.getLocalAddr(); isOk = expectedLocalAddr.equals(localName) && localName.equals(localAddr); if (!isOk) { errorBuilder .append("Local address and host don't match. Expected=") .append(expectedLocalAddr) .append(" Addr=") .append(localAddr) .append(" name=") .append(localName) .append('\n'); } } catch (Exception e) { errorBuilder.append(e.toString()); } if (isOk) { response.setStatus(200, "FINE"); } else { LOGGER.warning(errorBuilder.toString()); response.setStatus(500, "ERROR"); } } }); final MemoryManager mm = listener.getTransport().getMemoryManager(); final Buffer request = Buffers.wrap(mm, Utils.loadResourceFile("peer-addr-check.dat")); Buffer responseBuffer = send("localhost", PORT, request).get(60, 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 + "]"); } }
/** Parse host. */ static void parseHost( final DataChunk hostDC, final DataChunk serverNameDC, final HttpRequestPacket request) { if (hostDC == null) { // HTTP/1.0 // Default is what the socket tells us. Overriden if a host is // found/parsed request.setServerPort(request.getLocalPort()); serverNameDC.setString(request.getLocalName()); return; } final BufferChunk valueBC = hostDC.getBufferChunk(); final int valueS = valueBC.getStart(); final int valueL = valueBC.getEnd() - valueS; int colonPos = -1; final Buffer valueB = valueBC.getBuffer(); final boolean ipv6 = (valueB.get(valueS) == '['); boolean bracketClosed = false; for (int i = 0; i < valueL; i++) { final byte b = valueB.get(i + valueS); if (b == ']') { bracketClosed = true; } else if (b == ':') { if (!ipv6 || bracketClosed) { colonPos = i; break; } } } if (colonPos < 0) { if (!request.isSecure()) { // 80 - Default HTTTP port request.setServerPort(80); } else { // 443 - Default HTTPS port request.setServerPort(443); } serverNameDC.setBuffer(valueB, valueS, valueS + valueL); } else { serverNameDC.setBuffer(valueB, valueS, valueS + colonPos); int port = 0; int mult = 1; for (int i = valueL - 1; i > colonPos; i--) { int charValue = DEC[(int) valueB.get(i + valueS)]; if (charValue == -1) { // Invalid character throw new IllegalStateException( String.format( "Host header %s contained a non-decimal value in the port definition.", hostDC.toString())); } port = port + (charValue * mult); mult = 10 * mult; } request.setServerPort(port); } }
private static int decodeAttributes( final Buffer requestContent, int offset, final AjpHttpRequest req, final boolean tomcatAuthentication) { final DataChunk tmpDataChunk = req.tmpDataChunk; boolean moreAttr = true; while (moreAttr) { final byte attributeCode = requestContent.get(offset++); if (attributeCode == AjpConstants.SC_A_ARE_DONE) { return offset; } /* Special case ( XXX in future API make it separate type !) */ if (attributeCode == AjpConstants.SC_A_SSL_KEY_SIZE) { // Bug 1326: it's an Integer. req.setAttribute(SSLSupport.KEY_SIZE_KEY, readShort(requestContent, offset)); offset += 2; } if (attributeCode == AjpConstants.SC_A_REQ_ATTRIBUTE) { // 2 strings ???... offset = setStringAttribute(req, requestContent, offset); } // 1 string attributes switch (attributeCode) { case AjpConstants.SC_A_CONTEXT: // nothing offset = skipBytes(requestContent, offset); break; case AjpConstants.SC_A_REMOTE_USER: if (tomcatAuthentication) { // ignore server offset = skipBytes(requestContent, offset); } else { offset = getBytesToDataChunk(requestContent, offset, req.remoteUser()); } break; case AjpConstants.SC_A_AUTH_TYPE: if (tomcatAuthentication) { // ignore server offset = skipBytes(requestContent, offset); } else { offset = getBytesToDataChunk(requestContent, offset, req.authType()); } break; case AjpConstants.SC_A_QUERY_STRING: offset = getBytesToDataChunk(requestContent, offset, req.getQueryStringDC()); break; case AjpConstants.SC_A_JVM_ROUTE: offset = getBytesToDataChunk(requestContent, offset, req.instanceId()); break; case AjpConstants.SC_A_SSL_CERT: req.setSecure(true); // SSL certificate extraction is costy, initialize on demand offset = getBytesToDataChunk(requestContent, offset, req.sslCert()); break; case AjpConstants.SC_A_SSL_CIPHER: req.setSecure(true); offset = setStringAttributeValue(req, SSLSupport.CIPHER_SUITE_KEY, requestContent, offset); break; case AjpConstants.SC_A_SSL_SESSION: req.setSecure(true); offset = setStringAttributeValue(req, SSLSupport.SESSION_ID_KEY, requestContent, offset); break; case AjpConstants.SC_A_SECRET: offset = getBytesToDataChunk(requestContent, offset, tmpDataChunk); req.setSecret(tmpDataChunk.toString()); tmpDataChunk.recycle(); break; case AjpConstants.SC_A_STORED_METHOD: offset = getBytesToDataChunk(requestContent, offset, req.getMethodDC()); break; default: break; // ignore, we don't know about it - backward compat } } return offset; }