private boolean exitAfterSpecialCases( final HttpResponse response, final Channel channel, final NettyResponseFuture<?> future) throws Exception { HttpRequest httpRequest = future.getNettyRequest().getHttpRequest(); ProxyServer proxyServer = future.getProxyServer(); int statusCode = response.getStatus().code(); Request request = future.getCurrentRequest(); Realm realm = request.getRealm() != null ? request.getRealm() : config.getRealm(); if (statusCode == UNAUTHORIZED_401) { return exitAfterHandling401( channel, future, response, request, statusCode, realm, proxyServer, httpRequest); } else if (statusCode == PROXY_AUTHENTICATION_REQUIRED_407) { return exitAfterHandling407( channel, future, response, request, statusCode, proxyServer, httpRequest); } else if (statusCode == CONTINUE_100) { return exitAfterHandling100(channel, future, statusCode); } else if (REDIRECT_STATUSES.contains(statusCode)) { return exitAfterHandlingRedirect(channel, future, response, request, statusCode, realm); } else if (httpRequest.getMethod() == HttpMethod.CONNECT && statusCode == OK_200) { return exitAfterHandlingConnect( channel, future, request, proxyServer, statusCode, httpRequest); } return false; }
private void ntlmChallenge( String authenticateHeader, // Request request, // HttpHeaders headers, // Realm realm, // NettyResponseFuture<?> future) { if (authenticateHeader.equals("NTLM")) { // server replied bare NTLM => we didn't preemptively sent Type1Msg String challengeHeader = NtlmEngine.INSTANCE.generateType1Msg(); // FIXME we might want to filter current NTLM and add (leave other // Authorization headers untouched) headers.set(HttpHeaders.Names.AUTHORIZATION, "NTLM " + challengeHeader); future.getInAuth().set(false); } else { String serverChallenge = authenticateHeader.substring("NTLM ".length()).trim(); String challengeHeader = NtlmEngine.INSTANCE.generateType3Msg( realm.getPrincipal(), realm.getPassword(), realm.getNtlmDomain(), realm.getNtlmHost(), serverChallenge); // FIXME we might want to filter current NTLM and add (leave other // Authorization headers untouched) headers.set(HttpHeaders.Names.AUTHORIZATION, "NTLM " + challengeHeader); } }
private boolean exitAfterHandling100( final Channel channel, final NettyResponseFuture<?> future, int statusCode) { future.setHeadersAlreadyWrittenOnContinue(true); future.setDontWriteBodyBecauseExpectContinue(false); // directly send the body Channels.setAttribute( channel, new Callback(future) { @Override public void call() throws IOException { Channels.setAttribute(channel, future); requestSender.writeRequest(future, channel); } }); return true; }
private boolean updateBodyAndInterrupt( NettyResponseFuture<?> future, AsyncHandler<?> handler, HttpResponseBodyPart bodyPart) throws Exception { boolean interrupt = handler.onBodyPartReceived(bodyPart) != State.CONTINUE; if (interrupt) future.setKeepAlive(false); return interrupt; }
private void finishUpdate( final NettyResponseFuture<?> future, Channel channel, boolean expectOtherChunks) throws IOException { future.cancelTimeouts(); boolean keepAlive = future.isKeepAlive(); if (expectOtherChunks && keepAlive) channelManager.drainChannelAndOffer(channel, future); else channelManager.tryToOfferChannelToPool( channel, future.getAsyncHandler(), keepAlive, future.getPartitionKey()); try { future.done(); } catch (Exception t) { // Never propagate exception once we know we are done. logger.debug(t.getMessage(), t); } }
private boolean exitAfterHandlingStatus( Channel channel, NettyResponseFuture<?> future, HttpResponse response, AsyncHandler<?> handler, NettyResponseStatus status, HttpRequest httpRequest) throws IOException, Exception { return !future.getAndSetStatusReceived(true) && handler.onStatusReceived(status) != State.CONTINUE; }
@Override public void handle(final Channel channel, final NettyResponseFuture<?> future, final Object e) throws Exception { future.touch(); // future is already done because of an exception or a timeout if (future.isDone()) { // FIXME isn't the channel already properly closed? channelManager.closeChannel(channel); return; } AsyncHandler<?> handler = future.getAsyncHandler(); try { if (e instanceof HttpResponse) { if (handleHttpResponse((HttpResponse) e, channel, future, handler)) return; } else if (e instanceof HttpContent) { handleChunk((HttpContent) e, channel, future, handler); } } catch (Exception t) { // e.g. an IOException when trying to open a connection and send the // next request if (hasIOExceptionFilters // && t instanceof IOException // && requestSender.applyIoExceptionFiltersAndReplayRequest( future, IOException.class.cast(t), channel)) { return; } try { requestSender.abort(channel, future, t); } catch (Exception abortException) { logger.debug("Abort failed", abortException); } finally { finishUpdate(future, channel, false); } throw t; } }
private boolean exitAfterHandlingConnect( // final Channel channel, // final NettyResponseFuture<?> future, // final Request request, // ProxyServer proxyServer, // int statusCode, // HttpRequest httpRequest) throws IOException { if (future.isKeepAlive()) future.attachChannel(channel, true); Uri requestUri = request.getUri(); logger.debug("Connecting to proxy {} for scheme {}", proxyServer, requestUri.getScheme()); channelManager.upgradeProtocol(channel.pipeline(), requestUri); future.setReuseChannel(true); future.setConnectAllowed(false); requestSender.drainChannelAndExecuteNextRequest( channel, future, new RequestBuilder(future.getTargetRequest()).build()); return true; }
@Override public void handle(Channel channel, NettyResponseFuture<?> future, Object e) throws Exception { if (e instanceof HttpResponse) { HttpResponse response = (HttpResponse) e; Channels.setAttribute(channel, new UpgradeCallback(future, channel, response)); } else if (e instanceof WebSocketFrame) { final WebSocketFrame frame = (WebSocketFrame) e; WebSocketUpgradeHandler handler = WebSocketUpgradeHandler.class.cast(future.getAsyncHandler()); NettyWebSocket webSocket = NettyWebSocket.class.cast(handler.onCompleted()); invokeOnSucces(channel, handler); if (webSocket != null) { if (frame instanceof CloseWebSocketFrame) { Channels.setDiscard(channel); CloseWebSocketFrame closeFrame = CloseWebSocketFrame.class.cast(frame); webSocket.onClose(closeFrame.statusCode(), closeFrame.reasonText()); } else { ByteBuf buf = frame.content(); if (buf != null && buf.readableBytes() > 0) { try { NettyResponseBodyPart part = nettyConfig .getBodyPartFactory() .newResponseBodyPart(buf, frame.isFinalFragment()); handler.onBodyPartReceived(part); if (frame instanceof BinaryWebSocketFrame) { webSocket.onBinaryFragment(part); } else if (frame instanceof TextWebSocketFrame) { webSocket.onTextFragment(part); } else if (frame instanceof PingWebSocketFrame) { webSocket.onPing(part); } else if (frame instanceof PongWebSocketFrame) { webSocket.onPong(part); } } finally { buf.release(); } } } } else { logger.debug("UpgradeHandler returned a null NettyWebSocket "); } } else { logger.error("Invalid message {}", e); } }
private boolean handleHttpResponse( final HttpResponse response, final Channel channel, final NettyResponseFuture<?> future, AsyncHandler<?> handler) throws Exception { HttpRequest httpRequest = future.getNettyRequest().getHttpRequest(); logger.debug("\n\nRequest {}\n\nResponse {}\n", httpRequest, response); future.setKeepAlive( config.getKeepAliveStrategy().keepAlive(future.getTargetRequest(), httpRequest, response)); NettyResponseStatus status = new NettyResponseStatus(future.getUri(), config, response, channel); HttpResponseHeaders responseHeaders = new HttpResponseHeaders(response.headers()); return exitAfterProcessingFilters(channel, future, handler, status, responseHeaders) || // exitAfterSpecialCases(response, channel, future) || // exitAfterHandler(channel, future, response, handler, status, httpRequest, responseHeaders); }
@Override public void onClose(NettyResponseFuture<?> future) { logger.trace("onClose"); try { WebSocketUpgradeHandler h = (WebSocketUpgradeHandler) future.getAsyncHandler(); NettyWebSocket webSocket = NettyWebSocket.class.cast(h.onCompleted()); logger.trace("Connection was closed abnormally (that is, with no close frame being sent)."); if (webSocket != null) webSocket.close( 1006, "Connection was closed abnormally (that is, with no close frame being sent)."); } catch (Throwable t) { logger.error("onError", t); } }
@Override public void onError(NettyResponseFuture<?> future, Throwable e) { logger.warn("onError {}", e); try { WebSocketUpgradeHandler h = (WebSocketUpgradeHandler) future.getAsyncHandler(); NettyWebSocket webSocket = NettyWebSocket.class.cast(h.onCompleted()); if (webSocket != null) { webSocket.onError(e.getCause()); webSocket.close(); } } catch (Throwable t) { logger.error("onError", t); } }
private boolean exitAfterHandling407( // Channel channel, // NettyResponseFuture<?> future, // HttpResponse response, // Request request, // int statusCode, // ProxyServer proxyServer, // HttpRequest httpRequest) { if (future.getInProxyAuth().getAndSet(true)) { logger.info("Can't handle 407 as auth was already performed"); return false; } Realm proxyRealm = future.getProxyRealm(); if (proxyRealm == null) { logger.info("Can't handle 407 as there's no proxyRealm"); return false; } List<String> proxyAuthHeaders = response.headers().getAll(HttpHeaders.Names.PROXY_AUTHENTICATE); if (proxyAuthHeaders.isEmpty()) { logger.info("Can't handle 407 as response doesn't contain Proxy-Authenticate headers"); return false; } // FIXME what's this??? future.setChannelState(ChannelState.NEW); HttpHeaders requestHeaders = new DefaultHttpHeaders(false).add(request.getHeaders()); switch (proxyRealm.getScheme()) { case BASIC: if (getHeaderWithPrefix(proxyAuthHeaders, "Basic") == null) { logger.info( "Can't handle 407 with Basic realm as Proxy-Authenticate headers don't match"); return false; } if (proxyRealm.isUsePreemptiveAuth()) { // FIXME do we need this, as future.getAndSetAuth // was tested above? // auth was already performed, most likely auth // failed logger.info( "Can't handle 407 with Basic realm as auth was preemptive and already performed"); return false; } // FIXME do we want to update the realm, or directly // set the header? Realm newBasicRealm = realm(proxyRealm) // .setUsePreemptiveAuth(true) // .build(); future.setProxyRealm(newBasicRealm); break; case DIGEST: String digestHeader = getHeaderWithPrefix(proxyAuthHeaders, "Digest"); if (digestHeader == null) { logger.info( "Can't handle 407 with Digest realm as Proxy-Authenticate headers don't match"); return false; } Realm newDigestRealm = realm(proxyRealm) // .setUri(request.getUri()) // .setMethodName(request.getMethod()) // .setUsePreemptiveAuth(true) // .parseProxyAuthenticateHeader(digestHeader) // .build(); future.setProxyRealm(newDigestRealm); break; case NTLM: String ntlmHeader = getHeaderWithPrefix(proxyAuthHeaders, "NTLM"); if (ntlmHeader == null) { logger.info("Can't handle 407 with NTLM realm as Proxy-Authenticate headers don't match"); return false; } ntlmProxyChallenge(ntlmHeader, request, proxyRealm, requestHeaders, future); Realm newNtlmRealm = realm(proxyRealm) // .setUsePreemptiveAuth(true) // .build(); future.setProxyRealm(newNtlmRealm); break; case KERBEROS: case SPNEGO: if (getHeaderWithPrefix(proxyAuthHeaders, "Negociate") == null) { logger.info( "Can't handle 407 with Kerberos or Spnego realm as Proxy-Authenticate headers don't match"); return false; } try { kerberosProxyChallenge( channel, proxyAuthHeaders, request, proxyServer, proxyRealm, requestHeaders, future); } catch (SpnegoEngineException e) { // FIXME String ntlmHeader2 = getHeaderWithPrefix(proxyAuthHeaders, "NTLM"); if (ntlmHeader2 != null) { logger.warn("Kerberos/Spnego proxy auth failed, proceeding with NTLM"); ntlmChallenge(ntlmHeader2, request, requestHeaders, proxyRealm, future); Realm newNtlmRealm2 = realm(proxyRealm) // .setScheme(AuthScheme.NTLM) // .setUsePreemptiveAuth(true) // .build(); future.setProxyRealm(newNtlmRealm2); } else { requestSender.abort(channel, future, e); return false; } } break; default: throw new IllegalStateException("Invalid Authentication scheme " + proxyRealm.getScheme()); } RequestBuilder nextRequestBuilder = new RequestBuilder(future.getCurrentRequest()).setHeaders(requestHeaders); if (future.getCurrentRequest().getUri().isSecured()) { nextRequestBuilder.setMethod(CONNECT); } final Request nextRequest = nextRequestBuilder.build(); logger.debug("Sending proxy authentication to {}", request.getUri()); if (future.isKeepAlive() // && HttpHeaders.isKeepAlive(httpRequest) // && HttpHeaders.isKeepAlive(response) // // support broken Proxy-Connection && !response.headers().contains("Proxy-Connection", HttpHeaders.Values.CLOSE, true) // && !HttpHeaders.isTransferEncodingChunked(httpRequest) // && !HttpHeaders.isTransferEncodingChunked(response)) { future.setConnectAllowed(true); future.setReuseChannel(true); requestSender.drainChannelAndExecuteNextRequest(channel, future, nextRequest); } else { channelManager.closeChannel(channel); requestSender.sendNextRequest(nextRequest, future); } return true; }