@Override public NextAction handleClose(final FilterChainContext ctx) throws IOException { Socket tunnelSocket = tunnelSockets.get(ctx.getConnection()); if (tunnelSocket != null) { tunnelSocket.close(); } return ctx.getStopAction(); }
@Override public NextAction handleRead(FilterChainContext ctx) throws IOException { // Take ADD-service response final AddResponseMessage addResponseMessage = ctx.getMessage(); // do output LOGGER.log(Level.INFO, "Result={0}", addResponseMessage.getResult()); return ctx.getStopAction(); }
@Override public NextAction handleRead(FilterChainContext ctx) throws IOException { final Buffer content = ctx.getMessage(); try { future.result(content); } catch (Exception e) { future.failure(e); e.printStackTrace(); } return ctx.getStopAction(); }
/* * 接收 * * @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 } } } }
@Override public NextAction handleRead(final FilterChainContext ctx) throws IOException { final HttpContent httpContent = ctx.getMessage(); if (httpContent.isLast()) { // Perform the cleanup logic if it's the last chunk of the payload final HttpResponsePacket response = (HttpResponsePacket) httpContent.getHttpHeader(); recycleRequestResponsePackets(ctx.getConnection(), response); return ctx.getStopAction(); } return ctx.getInvokeAction(); }
@Override public NextAction handleWrite(final FilterChainContext ctx) throws IOException { Object message = ctx.getMessage(); if (message instanceof RequestInfoHolder) { ctx.setMessage(null); if (!sendAsGrizzlyRequest((RequestInfoHolder) message, ctx)) { return ctx.getSuspendAction(); } } else if (message instanceof Buffer) { return ctx.getInvokeAction(); } 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 }
private static boolean checkHandshakeError( final FilterChainContext ctx, final HttpTxContext httpCtx) { Throwable t = getHandshakeError(ctx.getConnection()); if (t != null) { httpCtx.abort(t); return true; } return false; }
private static boolean checkProxyAuthFailure( final FilterChainContext ctx, final HttpTxContext httpCtx) { final Boolean failed = PROXY_AUTH_FAILURE.get(ctx.getConnection()); if (failed != null && failed) { httpCtx.abort(new IllegalStateException("Unable to authenticate with proxy")); return true; } return false; }
@Override public NextAction handleWrite(FilterChainContext ctx) throws IOException { try { Thread.sleep(random.nextInt(50) + 10); } catch (InterruptedException ignored) { } return ctx.getInvokeAction(); }
@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); } }
@Override public NextAction handleRead(final FilterChainContext ctx) throws IOException { String message = (String) ctx.getMessage(); logger.log(Level.INFO, "First chunk come: {0}", message); intermResultQueue.add(message); Connection connection = ctx.getConnection(); connection.setBlockingReadTimeout(10, TimeUnit.SECONDS); try { for (int i = 0; i < clientMsgs - 1; i++) { final ReadResult rr = ctx.read(); final String blckMsg = (String) rr.getMessage(); rr.recycle(); logger.log(Level.INFO, "Blocking chunk come: {0}", blckMsg); intermResultQueue.add(blckMsg); message += blckMsg; } } catch (Exception e) { intermResultQueue.add(e); return ctx.getStopAction(); } ctx.setMessage(message); return ctx.getInvokeAction(); }
@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(); }
@SuppressWarnings({"unchecked"}) @Override public NextAction handleConnect(FilterChainContext ctx) throws IOException { System.out.println("\nClient connected!\n"); HttpRequestPacket request = createRequest(); System.out.println("Writing request:\n"); System.out.println(request.toString()); ctx.write(request); // write the request // for each of the content parts in CONTENT, wrap in a Buffer, // create the HttpContent to wrap the buffer and write the // content. MemoryManager mm = ctx.getMemoryManager(); for (int i = 0, len = CONTENT.length; i < len; i++) { HttpContent.Builder contentBuilder = request.httpContentBuilder(); Buffer b = Buffers.wrap(mm, CONTENT[i]); contentBuilder.content(b); HttpContent content = contentBuilder.build(); System.out.printf("(Client writing: %s)\n", b.toStringContent()); ctx.write(content); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } // since the request created by createRequest() is chunked, // we need to write the trailer to signify the end of the // POST data ctx.write(request.httpTrailerBuilder().build()); System.out.println("\n"); return ctx.getStopAction(); // discontinue filter chain execution }
@SuppressWarnings("unchecked") public boolean sendRequest( final FilterChainContext ctx, final Request request, final HttpRequestPacket requestPacket) throws IOException { boolean isWriteComplete = true; if (Utils.requestHasEntityBody(request)) { final HttpTxContext context = HttpTxContext.get(ctx); BodyHandler handler = bodyHandlerFactory.getBodyHandler(request); if (requestPacket.getHeaders().contains(Header.Expect) && requestPacket.getHeaders().getValue(1).equalsIgnoreCase("100-Continue")) { // We have to set the content-length now as the headers will be flushed // before the FileBodyHandler is invoked. If we don't do it here, and // the user didn't explicitly set the length, then the transfer-encoding // will be chunked and zero-copy file transfer will not occur. final File f = request.getFile(); if (f != null) { requestPacket.setContentLengthLong(f.length()); } handler = new ExpectHandler(handler); } context.setBodyHandler(handler); if (logger.isDebugEnabled()) { logger.debug("REQUEST: {}", requestPacket); } isWriteComplete = handler.doHandle(ctx, request, requestPacket); } else { HttpContent content = HttpContent.builder(requestPacket).last(true).build(); if (logger.isDebugEnabled()) { logger.debug("REQUEST: {}", requestPacket); } ctx.write(content, ctx.getTransportContext().getCompletionHandler()); } return isWriteComplete; }
/** * 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); } } }
@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(); }
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 FilterChainContext checkAndHandleFilterChainUpdate( final FilterChainContext ctx, final FilterChainContext sendingCtx) { FilterChainContext ctxLocal = sendingCtx; SSLConnectionContext sslCtx = SSLUtils.getSslConnectionContext(ctx.getConnection()); if (sslCtx != null) { FilterChain fc = sslCtx.getNewConnectionFilterChain(); if (fc != null) { // Create a new FilterChain context using the new // FilterChain. // TODO: We need to mark this connection somehow // as being only suitable for this type of // request. ctxLocal = obtainProtocolChainContext(ctx, fc); } } return ctxLocal; }
@SuppressWarnings("unchecked") @Override public NextAction handleEvent(final FilterChainContext ctx, final FilterChainEvent event) throws IOException { final Object type = event.type(); if (type == ContinueEvent.class) { final ContinueEvent continueEvent = (ContinueEvent) event; ((ExpectHandler) continueEvent.getContext().getBodyHandler()).finish(ctx); } else if (type == TunnelRequestEvent.class) { // Disable SSL for the time being... ctx.notifyDownstream(new SSLSwitchingEvent(false, ctx.getConnection())); ctx.suspend(); TunnelRequestEvent tunnelRequestEvent = (TunnelRequestEvent) event; final ProxyServer proxyServer = tunnelRequestEvent.getProxyServer(); final URI requestUri = tunnelRequestEvent.getUri(); RequestBuilder builder = new RequestBuilder(); builder.setMethod(Method.CONNECT.getMethodString()); builder.setUrl("http://" + getAuthority(requestUri)); Request request = builder.build(); AsyncHandler handler = new AsyncCompletionHandler() { @Override public Object onCompleted(Response response) throws Exception { if (response.getStatusCode() != 200) { PROXY_AUTH_FAILURE.set(ctx.getConnection(), Boolean.TRUE); } ctx.notifyDownstream(new SSLSwitchingEvent(true, ctx.getConnection())); ctx.notifyDownstream(event); return response; } }; final GrizzlyResponseFuture future = new GrizzlyResponseFuture(grizzlyAsyncHttpProvider, request, handler, proxyServer); future.setDelegate(SafeFutureImpl.create()); grizzlyAsyncHttpProvider.execute( ctx.getConnection(), request, handler, future, HttpTxContext.get(ctx)); return ctx.getSuspendAction(); } return ctx.getStopAction(); }
@Override public NextAction handleRead(FilterChainContext ctx) throws IOException { final Object message = ctx.getMessage(); if (_logger.isDebugEnabled()) { _logger.debug(String.format("%s: Got [%s] from [%s].", this, message, ctx.getAddress())); } String responseXML = _coreCommandProcessor.processCommand((String) message); if (_logger.isDebugEnabled()) { _logger.debug( String.format("%s: Returning [%s] to [%s].", this, responseXML, ctx.getAddress())); } ctx.write(ctx.getAddress(), responseXML, null); // Close the connection ctx.getConnection().closeSilently(); return ctx.getStopAction(); }
/** * Method is called, when new client {@link Connection} was connected to some endpoint * * @param ctx the filter chain context * @return the next action to be executed by chain * @throws java.io.IOException */ @Override public NextAction handleConnect(FilterChainContext ctx) throws IOException { newConnection(ctx.getConnection()); return ctx.getInvokeAction(); }
/** * Method is called, when the {@link Connection} is getting closed * * @param ctx the filter chain context * @return the next action to be executed by chain * @throws java.io.IOException */ @Override public NextAction handleClose(FilterChainContext ctx) throws IOException { activeConnectionsMap.remove(ctx.getConnection()); return ctx.getInvokeAction(); }
private static FilterChainContext obtainProtocolChainContext( final FilterChainContext ctx, final FilterChain completeProtocolFilterChain) { final FilterChainContext newFilterChainContext = completeProtocolFilterChain.obtainFilterChainContext( ctx.getConnection(), ctx.getStartIdx() + 1, completeProtocolFilterChain.size(), ctx.getFilterIdx() + 1); newFilterChainContext.setAddressHolder(ctx.getAddressHolder()); newFilterChainContext.setMessage(ctx.getMessage()); newFilterChainContext.getInternalContext().setIoEvent(ctx.getInternalContext().getIoEvent()); ctx.getConnection().setProcessor(completeProtocolFilterChain); return newFilterChainContext; }
/** * Initiates asynchronous data receiving. * * <p>This is service method, usually users don't have to call it explicitly. */ public void initiateAsyncronousDataReceiving() { // fork the FilterChainContext execution // keep the current FilterChainContext suspended, but make a copy and resume it ctx.fork(ctx.getStopAction()); }
private boolean sendAsGrizzlyRequest( final RequestInfoHolder requestInfoHolder, final FilterChainContext ctx) throws IOException { HttpTxContext httpTxContext = requestInfoHolder.getHttpTxContext(); if (httpTxContext == null) { httpTxContext = HttpTxContext.create(requestInfoHolder); } if (checkProxyAuthFailure(ctx, httpTxContext)) { return true; } final Request request = httpTxContext.getRequest(); final URI uri = request.isUseRawUrl() ? request.getRawURI() : request.getURI(); boolean secure = Utils.isSecure(uri); // If the request is secure, check to see if an error occurred during // the handshake. We have to do this here, as the error would occur // out of the scope of a HttpTxContext so there would be // no good way to communicate the problem to the caller. if (secure && checkHandshakeError(ctx, httpTxContext)) { return true; } if (isUpgradeRequest(httpTxContext.getHandler()) && isWSRequest(httpTxContext.getRequestUrl())) { httpTxContext.setWSRequest(true); convertToUpgradeRequest(httpTxContext); } HttpRequestPacket requestPacket = requestCache.poll(); if (requestPacket == null) { requestPacket = new HttpRequestPacketImpl(); } requestPacket.setMethod(request.getMethod()); requestPacket.setProtocol(Protocol.HTTP_1_1); // Special handling for CONNECT. if (Method.CONNECT.matchesMethod(request.getMethod())) { final int port = uri.getPort(); requestPacket.setRequestURI(uri.getHost() + ':' + (port == -1 ? 443 : port)); } else { requestPacket.setRequestURI(uri.getPath()); } if (Utils.requestHasEntityBody(request)) { final long contentLength = request.getContentLength(); if (contentLength >= 0) { requestPacket.setContentLengthLong(contentLength); requestPacket.setChunked(false); } else { requestPacket.setChunked(true); } } if (httpTxContext.isWSRequest()) { try { final URI wsURI = new URI(httpTxContext.getWsRequestURI()); httpTxContext.setProtocolHandler(Version.RFC6455.createHandler(true)); httpTxContext.setHandshake(httpTxContext.getProtocolHandler().createHandShake(wsURI)); requestPacket = (HttpRequestPacket) httpTxContext.getHandshake().composeHeaders().getHttpHeader(); } catch (URISyntaxException e) { throw new IllegalArgumentException("Invalid WS URI: " + httpTxContext.getWsRequestURI()); } } requestPacket.setSecure(secure); addQueryString(request, requestPacket); addHostHeader(request, uri, requestPacket); addGeneralHeaders(request, requestPacket); addCookies(request, requestPacket); initTransferCompletionHandler(request, httpTxContext.getHandler()); final HttpRequestPacket requestPacketLocal = requestPacket; FilterChainContext sendingCtx = ctx; if (secure) { // Check to see if the ProtocolNegotiator has given // us a different FilterChain to use. If so, we need // use a different FilterChainContext when invoking sendRequest(). sendingCtx = checkAndHandleFilterChainUpdate(ctx, sendingCtx); } final Connection c = ctx.getConnection(); if (!Utils.isSpdyConnection(c)) { HttpContext.newInstance(ctx, c, c, c); } else { SpdySession session = SpdySession.get(c); final Lock lock = session.getNewClientStreamLock(); try { lock.lock(); SpdyStream stream = session.openStream( requestPacketLocal, session.getNextLocalStreamId(), 0, 0, 0, false, !requestPacketLocal.isExpectContent()); HttpContext.newInstance(ctx, stream, stream, stream); } finally { lock.unlock(); } } HttpTxContext.set(ctx, httpTxContext); return sendRequest(sendingCtx, request, requestPacketLocal); }
/** * Read next chunk of data in this thread, block if needed. * * @return {@link HttpContent} * @throws IOException */ protected HttpContent blockingRead() throws IOException { final ReadResult rr = ctx.read(); final HttpContent c = (HttpContent) rr.getMessage(); rr.recycle(); return c; }
/** * Method invoked when a first massage (Assumed to be HTTP) is received. Normally this would be * HTTP CONNECT and this method processes it and opens a connection to the destination (the server * that the client wants to access). * * <p>This method can be overridden to provide a test-specific handling of the CONNECT method. */ protected NextAction handleConnect(FilterChainContext ctx, HttpContent content) { System.out.println("Handle CONNECT start . . ."); HttpHeader httpHeader = content.getHttpHeader(); HttpRequestPacket requestPacket = (HttpRequestPacket) httpHeader.getHttpHeader(); if (!requestPacket.getMethod().matchesMethod("CONNECT")) { System.out.println("Received method is not CONNECT"); writeHttpResponse(ctx, 400); return ctx.getStopAction(); } String destinationUri = requestPacket.getRequestURI(); // We expect URI in form host:port, this is not flexible, but we use it only to test our clients int colonIdx = destinationUri.indexOf(':'); if (colonIdx == -1) { System.out.println("Destination URI not in host:port format: " + destinationUri); writeHttpResponse(ctx, 400); return ctx.getStopAction(); } String hostName = destinationUri.substring(0, colonIdx); String portStr = destinationUri.substring(colonIdx + 1); int port; try { port = Integer.parseInt(portStr); } catch (Throwable t) { System.out.println("Could not parse destination port: " + portStr); writeHttpResponse(ctx, 400); return ctx.getStopAction(); } try { Socket tunnelSocket = new Socket(hostName, port); Connection grizzlyConnection = ctx.getConnection(); tunnelSockets.set(grizzlyConnection, tunnelSocket); TunnelSocketReader tunnelSocketReader = new TunnelSocketReader(tunnelSocket, grizzlyConnection); executorService.submit(tunnelSocketReader::read); } catch (IOException e) { writeHttpResponse(ctx, 400); return ctx.getStopAction(); } // Grizzly does not like CONNECT method and sets "keep alive" to false, if it is present // This hacks Grizzly, so it will keep the connection open HttpRequestPacket request = getHttpRequest(ctx); request.getResponse().getProcessingState().setKeepAlive(true); request.getResponse().setContentLength(0); request.setMethod("GET"); // end of hack writeHttpResponse(ctx, 200); System.out.println("Connection to proxy established."); return ctx.getStopAction(); }
private void writeHttpResponse(FilterChainContext ctx, int status) { HttpResponsePacket responsePacket = getHttpRequest(ctx).getResponse(); responsePacket.setProtocol(Protocol.HTTP_1_1); responsePacket.setStatus(status); ctx.write(HttpContent.builder(responsePacket).build()); }
private HttpRequestPacket getHttpRequest(FilterChainContext ctx) { return (HttpRequestPacket) ((HttpContent) ctx.getMessage()).getHttpHeader(); }