private Realm kerberosChallenge( List<String> proxyAuth, Request request, ProxyServer proxyServer, FluentCaseInsensitiveStringsMap headers, Realm realm, NettyResponseFuture<?> future) throws NTLMEngineException { URI uri = request.getURI(); String host = request.getVirtualHost() == null ? AsyncHttpProviderUtils.getHost(uri) : request.getVirtualHost(); String server = proxyServer == null ? host : proxyServer.getHost(); try { String challengeHeader = SpnegoEngine.instance().generateToken(server); headers.remove(HttpHeaders.Names.AUTHORIZATION); headers.add(HttpHeaders.Names.AUTHORIZATION, "Negotiate " + challengeHeader); return newRealmBuilder(realm) // .setUri(uri.getRawPath()) // .setMethodName(request.getMethod()) // .setScheme(Realm.AuthScheme.KERBEROS) // .build(); } catch (Throwable throwable) { if (isNTLM(proxyAuth)) { return ntlmChallenge(proxyAuth, request, proxyServer, headers, realm, future); } channels.abort(future, throwable); return null; } }
@Test public void testGetWithRequestBuilder() throws UnsupportedEncodingException { ConsumerKey consumer = new ConsumerKey(CONSUMER_KEY, CONSUMER_SECRET); RequestToken user = new RequestToken(TOKEN_KEY, TOKEN_SECRET); OAuthSignatureCalculator calc = new StaticOAuthSignatureCalculator(consumer, user, TIMESTAMP, NONCE); final Request req = get("http://photos.example.net/photos") // .addQueryParam("file", "vacation.jpg") // .addQueryParam("size", "original") // .setSignatureCalculator(calc) // .build(); final List<Param> params = req.getQueryParams(); assertEquals(params.size(), 2); // From the signature tester, the URL should look like: // normalized parameters: // file=vacation.jpg&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk&oauth_version=1.0&size=original // signature base string: // GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal // signature: tR3+Ty81lMeYAr/Fid0kMTYa/WM= // Authorization header: OAuth // realm="",oauth_version="1.0",oauth_consumer_key="dpf43f3p2l4k3l03",oauth_token="nnch734d00sl2jdk",oauth_timestamp="1191242096",oauth_nonce="kllo9940pd9333jh",oauth_signature_method="HMAC-SHA1",oauth_signature="tR3%2BTy81lMeYAr%2FFid0kMTYa%2FWM%3D" String authHeader = req.getHeaders().get(AUTHORIZATION); Matcher m = Pattern.compile("oauth_signature=\"(.+?)\"").matcher(authHeader); assertEquals(m.find(), true); String encodedSig = m.group(1); String sig = URLDecoder.decode(encodedSig, "UTF-8"); assertEquals(sig, "tR3+Ty81lMeYAr/Fid0kMTYa/WM="); assertEquals(req.getUrl(), "http://photos.example.net/photos?file=vacation.jpg&size=original"); }
private Realm ntlmProxyChallenge( List<String> wwwAuth, Request request, ProxyServer proxyServer, FluentCaseInsensitiveStringsMap headers, Realm realm, NettyResponseFuture<?> future) throws NTLMEngineException { future.getAndSetAuth(false); headers.remove(HttpHeaders.Names.PROXY_AUTHORIZATION); addType3NTLMAuthorizationHeader( wwwAuth, headers, proxyServer.getPrincipal(), proxyServer.getPassword(), proxyServer.getNtlmDomain(), proxyServer.getHost()); return newRealmBuilder(realm) // // .setScheme(realm.getAuthScheme()) .setUri(request.getURI().getPath()) // .setMethodName(request.getMethod()) .build(); }
private boolean handleConnectOKAndExit( int statusCode, Realm realm, final Request request, HttpRequest httpRequest, HttpResponse response, final NettyResponseFuture<?> future, ProxyServer proxyServer, final Channel channel) throws IOException { if (statusCode == OK.code() && httpRequest.getMethod() == HttpMethod.CONNECT) { LOGGER.debug("Connected to {}:{}", proxyServer.getHost(), proxyServer.getPort()); if (future.isKeepAlive()) { future.attachChannel(channel, true); } try { LOGGER.debug("Connecting to proxy {} for scheme {}", proxyServer, request.getUrl()); channels.upgradeProtocol(channel.pipeline(), request.getURI().getScheme()); } catch (Throwable ex) { channels.abort(future, ex); } future.setReuseChannel(true); future.setConnectAllowed(false); requestSender.sendNextRequest(new RequestBuilder(future.getRequest()).build(), future); return true; } return false; }
@Test public void testPostCalculateSignature() throws UnsupportedEncodingException { ConsumerKey consumer = new ConsumerKey(CONSUMER_KEY, CONSUMER_SECRET); RequestToken user = new RequestToken(TOKEN_KEY, TOKEN_SECRET); OAuthSignatureCalculator calc = new StaticOAuthSignatureCalculator(consumer, user, TIMESTAMP, NONCE); final Request req = post("http://photos.example.net/photos") // .addFormParam("file", "vacation.jpg") // .addFormParam("size", "original") // .setSignatureCalculator(calc) // .build(); // From the signature tester, POST should look like: // normalized parameters: // file=vacation.jpg&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk&oauth_version=1.0&size=original // signature base string: // POST&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal // signature: wPkvxykrw+BTdCcGqKr+3I+PsiM= // header: OAuth // realm="",oauth_version="1.0",oauth_consumer_key="dpf43f3p2l4k3l03",oauth_token="nnch734d00sl2jdk",oauth_timestamp="1191242096",oauth_nonce="kllo9940pd9333jh",oauth_signature_method="HMAC-SHA1",oauth_signature="wPkvxykrw%2BBTdCcGqKr%2B3I%2BPsiM%3D" String authHeader = req.getHeaders().get(AUTHORIZATION); Matcher m = Pattern.compile("oauth_signature=\"(.+?)\"").matcher(authHeader); assertEquals(m.find(), true); String encodedSig = m.group(1); String sig = URLDecoder.decode(encodedSig, "UTF-8"); assertEquals(sig, "wPkvxykrw+BTdCcGqKr+3I+PsiM="); }
private boolean handleResponseAndExit( final Channel channel, final NettyResponseFuture<?> future, AsyncHandler<?> handler, HttpRequest httpRequest, ProxyServer proxyServer, HttpResponse response) throws Exception { // store the original headers so we can re-send all them to // the handler in case of trailing headers future.setHttpHeaders(response.headers()); future.setKeepAlive( !HttpHeaders.Values.CLOSE.equalsIgnoreCase( response.headers().get(HttpHeaders.Names.CONNECTION))); HttpResponseStatus status = new ResponseStatus(future.getURI(), response, config); int statusCode = response.getStatus().code(); Request request = future.getRequest(); Realm realm = request.getRealm() != null ? request.getRealm() : config.getRealm(); HttpResponseHeaders responseHeaders = new ResponseHeaders(future.getURI(), response.headers()); return handleResponseFiltersReplayRequestAndExit(channel, future, status, responseHeaders) // || handleUnauthorizedAndExit( statusCode, realm, request, response, future, proxyServer, channel) // || handleContinueAndExit(channel, future, statusCode) // || handleProxyAuthenticationRequiredAndExit( statusCode, realm, request, response, future, proxyServer) || handleConnectOKAndExit( statusCode, realm, request, httpRequest, response, future, proxyServer, channel) // || handleRedirectAndExit(request, future, response, channel) // || handleHanderAndExit(channel, future, handler, status, responseHeaders, response); }
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 addGeneralHeaders(final Request request, final HttpRequestPacket requestPacket) { if (request.hasHeaders()) { final FluentCaseInsensitiveStringsMap map = request.getHeaders(); for (final Map.Entry<String, List<String>> entry : map.entrySet()) { final String headerName = entry.getKey(); final List<String> headerValues = entry.getValue(); if (isNonEmpty(headerValues)) { for (int i = 0, len = headerValues.size(); i < len; i++) { requestPacket.addHeader(headerName, headerValues.get(i)); } } } } final MimeHeaders headers = requestPacket.getHeaders(); if (!headers.contains(Header.Connection)) { // final boolean canCache = context.provider.clientConfig.getAllowPoolingConnection(); requestPacket.addHeader(Header.Connection, /*(canCache ? */ "keep-alive" /*: "close")*/); } if (!headers.contains(Header.Accept)) { requestPacket.addHeader(Header.Accept, "*/*"); } if (!headers.contains(Header.UserAgent)) { requestPacket.addHeader(Header.UserAgent, config.getUserAgent()); } }
public static String perConnectionAuthorizationHeader( Request request, ProxyServer proxyServer, Realm realm) { String authorizationHeader = null; if (realm != null && realm.getUsePreemptiveAuth()) { switch (realm.getScheme()) { case NTLM: String msg = NtlmEngine.INSTANCE.generateType1Msg(); authorizationHeader = "NTLM " + msg; break; case KERBEROS: case SPNEGO: String host; if (proxyServer != null) host = proxyServer.getHost(); else if (request.getVirtualHost() != null) host = request.getVirtualHost(); else host = request.getUri().getHost(); authorizationHeader = "Negotiate " + SpnegoEngine.instance().generateToken(host); break; default: break; } } return authorizationHeader; }
private boolean handleProxyAuthenticationRequiredAndExit( int statusCode, Realm realm, final Request request, HttpResponse response, final NettyResponseFuture<?> future, ProxyServer proxyServer) throws Exception { if (statusCode == PROXY_AUTHENTICATION_REQUIRED.code() && realm != null) { List<String> proxyAuthenticateHeaders = response.headers().getAll(HttpHeaders.Names.PROXY_AUTHENTICATE); if (!proxyAuthenticateHeaders.isEmpty() && !future.getAndSetAuth(true)) { LOGGER.debug("Sending proxy authentication to {}", request.getUrl()); future.setState(NettyResponseFuture.STATE.NEW); Realm newRealm = null; boolean negociate = proxyAuthenticateHeaders.contains("Negotiate"); if (!proxyAuthenticateHeaders.contains("Kerberos") && (isNTLM(proxyAuthenticateHeaders) || negociate)) { newRealm = ntlmProxyChallenge( proxyAuthenticateHeaders, request, proxyServer, request.getHeaders(), realm, future); // SPNEGO KERBEROS } else if (negociate) { newRealm = kerberosChallenge( proxyAuthenticateHeaders, request, proxyServer, request.getHeaders(), realm, future); if (newRealm == null) return true; } else { newRealm = future.getRequest().getRealm(); } future.setReuseChannel(true); future.setConnectAllowed(true); requestSender.sendNextRequest( new RequestBuilder(future.getRequest()) .setHeaders(request.getHeaders()) .setRealm(newRealm) .build(), future); return true; } } return false; }
private void kerberosChallenge( Channel channel, // List<String> authHeaders, // Request request, // HttpHeaders headers, // Realm realm, // NettyResponseFuture<?> future) throws SpnegoEngineException { Uri uri = request.getUri(); String host = request.getVirtualHost() == null ? uri.getHost() : request.getVirtualHost(); String challengeHeader = SpnegoEngine.instance().generateToken(host); headers.set(HttpHeaders.Names.AUTHORIZATION, "Negotiate " + challengeHeader); }
private static void addQueryString(final Request request, final HttpRequestPacket requestPacket) { final FluentStringsMap map = request.getQueryParams(); if (isNonEmpty(map)) { StringBuilder sb = new StringBuilder(128); for (final Map.Entry<String, List<String>> entry : map.entrySet()) { final String name = entry.getKey(); final List<String> values = entry.getValue(); if (isNonEmpty(values)) { try { for (int i = 0, len = values.size(); i < len; i++) { final String value = values.get(i); if (isNonEmpty(value)) { sb.append(URLEncoder.encode(name, "UTF-8")) .append('=') .append(URLEncoder.encode(values.get(i), "UTF-8")) .append('&'); } else { sb.append(URLEncoder.encode(name, "UTF-8")).append('&'); } } } catch (UnsupportedEncodingException ignored) { } } } sb.setLength(sb.length() - 1); String queryString = sb.toString(); requestPacket.setQueryString(queryString); } }
private static void initTransferCompletionHandler(final Request request, final AsyncHandler h) throws IOException { if (h instanceof TransferCompletionHandler) { final FluentCaseInsensitiveStringsMap map = new FluentCaseInsensitiveStringsMap(request.getHeaders()); TransferCompletionHandler.class.cast(h).headers(map); } }
@Test(groups = "standalone") public void testNonProxyHost() { // // should avoid, it's in non-proxy hosts Request req = get("http://somewhere.com/foo").build(); ProxyServer proxyServer = proxyServer("foo", 1234).setNonProxyHost("somewhere.com").build(); assertTrue(proxyServer.isIgnoredForHost(req.getUri().getHost())); // // // should avoid, it's in non-proxy hosts (with "*") req = get("http://sub.somewhere.com/foo").build(); proxyServer = proxyServer("foo", 1234).setNonProxyHost("*.somewhere.com").build(); assertTrue(proxyServer.isIgnoredForHost(req.getUri().getHost())); // should use it req = get("http://sub.somewhere.com/foo").build(); proxyServer = proxyServer("foo", 1234).setNonProxyHost("*.somewhere.com").build(); assertTrue(proxyServer.isIgnoredForHost(req.getUri().getHost())); }
private void addCookies(final Request request, final HttpRequestPacket requestPacket) { final Collection<Cookie> cookies = request.getCookies(); if (isNonEmpty(cookies)) { StringBuilder sb = new StringBuilder(128); org.glassfish.grizzly.http.Cookie[] gCookies = new org.glassfish.grizzly.http.Cookie[cookies.size()]; convertCookies(cookies, gCookies); CookieSerializerUtils.serializeClientCookies( sb, false, config.isRfc6265CookieEncoding(), gCookies); requestPacket.addHeader(Header.Cookie, sb.toString()); } }
private static void addHostHeader( final Request request, final URI uri, final HttpRequestPacket requestPacket) { String host = request.getVirtualHost(); if (host != null) { requestPacket.addHeader(Header.Host, host); } else { if (uri.getPort() == -1) { requestPacket.addHeader(Header.Host, uri.getHost()); } else { requestPacket.addHeader(Header.Host, uri.getHost() + ':' + uri.getPort()); } } }
private Realm ntlmChallenge( List<String> wwwAuth, Request request, ProxyServer proxyServer, FluentCaseInsensitiveStringsMap headers, Realm realm, NettyResponseFuture<?> future) throws NTLMEngineException { boolean useRealm = proxyServer == null && realm != null; String ntlmDomain = useRealm ? realm.getNtlmDomain() : proxyServer.getNtlmDomain(); String ntlmHost = useRealm ? realm.getNtlmHost() : proxyServer.getHost(); String principal = useRealm ? realm.getPrincipal() : proxyServer.getPrincipal(); String password = useRealm ? realm.getPassword() : proxyServer.getPassword(); if (realm != null && !realm.isNtlmMessageType2Received()) { String challengeHeader = NTLMEngine.INSTANCE.generateType1Msg(ntlmDomain, ntlmHost); URI uri = request.getURI(); addNTLMAuthorizationHeader(headers, challengeHeader); future.getAndSetAuth(false); return newRealmBuilder(realm) // .setScheme(realm.getAuthScheme()) // .setUri(uri.getRawPath()) // .setMethodName(request.getMethod()) // .setNtlmMessageType2Received(true) // .build(); } else { addType3NTLMAuthorizationHeader(wwwAuth, headers, principal, password, ntlmDomain, ntlmHost); Realm.AuthScheme authScheme = realm != null ? realm.getAuthScheme() : Realm.AuthScheme.NTLM; return newRealmBuilder(realm) // .setScheme(authScheme) // .setUri(request.getURI().getPath()) // .setMethodName(request.getMethod()) // .build(); } }
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; }
@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; }
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; }
protected List<String> getProxyAuthorizationHeader(Request request) { return request.getHeaders().get(PROXY_AUTHORIZATION); }
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); }
private NettyBody body(Request request, boolean connect) throws IOException { NettyBody nettyBody = null; if (!connect) { Charset bodyCharset = request.getBodyCharset() == null ? DEFAULT_CHARSET : request.getBodyCharset(); if (request.getByteData() != null) nettyBody = new NettyByteArrayBody(request.getByteData()); else if (request.getCompositeByteData() != null) nettyBody = new NettyCompositeByteArrayBody(request.getCompositeByteData()); else if (request.getStringData() != null) nettyBody = new NettyByteBufferBody( StringUtils.charSequence2ByteBuffer(request.getStringData(), bodyCharset)); else if (request.getByteBufferData() != null) nettyBody = new NettyByteBufferBody(request.getByteBufferData()); else if (request.getStreamData() != null) nettyBody = new NettyInputStreamBody(request.getStreamData(), config); else if (isNonEmpty(request.getFormParams())) { String contentType = null; if (!request.getHeaders().containsKey(CONTENT_TYPE)) contentType = HttpHeaders.Values.APPLICATION_X_WWW_FORM_URLENCODED; nettyBody = new NettyByteBufferBody( urlEncodeFormParams(request.getFormParams(), bodyCharset), contentType); } else if (isNonEmpty(request.getParts())) nettyBody = new NettyMultipartBody(request.getParts(), request.getHeaders(), config); else if (request.getFile() != null) nettyBody = new NettyFileBody(request.getFile(), config); else if (request.getBodyGenerator() instanceof FileBodyGenerator) { FileBodyGenerator fileBodyGenerator = (FileBodyGenerator) request.getBodyGenerator(); nettyBody = new NettyFileBody( fileBodyGenerator.getFile(), fileBodyGenerator.getRegionSeek(), fileBodyGenerator.getRegionLength(), config); } else if (request.getBodyGenerator() instanceof InputStreamBodyGenerator) nettyBody = new NettyInputStreamBody( InputStreamBodyGenerator.class.cast(request.getBodyGenerator()).getInputStream(), config); else if (request.getBodyGenerator() != null) nettyBody = new NettyBodyBody(request.getBodyGenerator().createBody(), config); } return nettyBody; }
public NettyRequest newNettyRequest( Request request, boolean forceConnect, ProxyServer proxyServer) throws IOException { Uri uri = request.getUri(); HttpMethod method = forceConnect ? HttpMethod.CONNECT : HttpMethod.valueOf(request.getMethod()); boolean connect = method == HttpMethod.CONNECT; boolean allowConnectionPooling = config.isAllowPoolingConnections() && (!HttpUtils.isSecure(uri) || config.isAllowPoolingSslConnections()); HttpVersion httpVersion = !allowConnectionPooling || (connect && proxyServer.isForceHttp10()) ? HttpVersion.HTTP_1_0 : HttpVersion.HTTP_1_1; String requestUri = requestUri(uri, proxyServer, connect); NettyBody body = body(request, connect); HttpRequest httpRequest; NettyRequest nettyRequest; if (body instanceof NettyDirectBody) { ChannelBuffer buffer = NettyDirectBody.class.cast(body).channelBuffer(); httpRequest = new DefaultHttpRequest(httpVersion, method, requestUri); // body is passed as null as it's written directly with the request httpRequest.setContent(buffer); nettyRequest = new NettyRequest(httpRequest, null); } else { httpRequest = new DefaultHttpRequest(httpVersion, method, requestUri); nettyRequest = new NettyRequest(httpRequest, body); } HttpHeaders headers = httpRequest.headers(); if (!connect) { // assign headers as configured on request for (Entry<String, List<String>> header : request.getHeaders()) { headers.set(header.getKey(), header.getValue()); } if (isNonEmpty(request.getCookies())) headers.set(COOKIE, CookieEncoder.encode(request.getCookies())); if (config.isCompressionEnforced() && !headers.contains(ACCEPT_ENCODING)) headers.set(ACCEPT_ENCODING, GZIP_DEFLATE); } if (body != null) { if (body.getContentLength() < 0) headers.set(TRANSFER_ENCODING, HttpHeaders.Values.CHUNKED); else headers.set(CONTENT_LENGTH, body.getContentLength()); if (body.getContentType() != null) headers.set(CONTENT_TYPE, body.getContentType()); } // connection header and friends boolean webSocket = isWebSocket(uri.getScheme()); if (!connect && webSocket) { String origin = "http://" + uri.getHost() + ":" + (uri.getPort() == -1 ? isSecure(uri.getScheme()) ? 443 : 80 : uri.getPort()); headers .set(UPGRADE, HttpHeaders.Values.WEBSOCKET) // .set(CONNECTION, HttpHeaders.Values.UPGRADE) // .set(ORIGIN, origin) // .set(SEC_WEBSOCKET_KEY, getKey()) // .set(SEC_WEBSOCKET_VERSION, "13"); } else if (!headers.contains(CONNECTION)) { String connectionHeaderValue = connectionHeader(allowConnectionPooling, httpVersion == HttpVersion.HTTP_1_1); if (connectionHeaderValue != null) headers.set(CONNECTION, connectionHeaderValue); } if (!headers.contains(HOST)) headers.set(HOST, hostHeader(request, uri)); Realm realm = request.getRealm() != null ? request.getRealm() : config.getRealm(); // don't override authorization but append addAuthorizationHeader(headers, systematicAuthorizationHeader(request, realm)); setProxyAuthorizationHeader( headers, systematicProxyAuthorizationHeader(request, proxyServer, realm, connect)); // Add default accept headers if (!headers.contains(ACCEPT)) headers.set(ACCEPT, "*/*"); // Add default user agent if (!headers.contains(USER_AGENT) && config.getUserAgent() != null) headers.set(USER_AGENT, config.getUserAgent()); return nettyRequest; }
@Override public void call() throws Exception { WebSocketUpgradeHandler handler = WebSocketUpgradeHandler.class.cast(future.getAsyncHandler()); Request request = future.getRequest(); HttpResponseStatus status = new NettyResponseStatus(future.getUri(), config, response, channel); HttpResponseHeaders responseHeaders = new NettyResponseHeaders(response.headers()); Realm realm = request.getRealm() != null ? request.getRealm() : config.getRealm(); if (exitAfterProcessingFilters(channel, future, handler, status, responseHeaders)) { return; } future.setHttpHeaders(response.headers()); if (exitAfterHandlingRedirect( channel, future, response, request, response.getStatus().code(), realm)) return; boolean validStatus = response.getStatus().equals(SWITCHING_PROTOCOLS); boolean validUpgrade = response.headers().get(HttpHeaders.Names.UPGRADE) != null; String connection = response.headers().get(HttpHeaders.Names.CONNECTION); if (connection == null) connection = response.headers().get(HttpHeaders.Names.CONNECTION.toLowerCase(Locale.ENGLISH)); boolean validConnection = HttpHeaders.Values.UPGRADE.equalsIgnoreCase(connection); boolean statusReceived = handler.onStatusReceived(status) == State.UPGRADE; if (!statusReceived) { try { handler.onCompleted(); } finally { future.done(); } return; } final boolean headerOK = handler.onHeadersReceived(responseHeaders) == State.CONTINUE; if (!headerOK || !validStatus || !validUpgrade || !validConnection) { requestSender.abort(channel, future, new IOException("Invalid handshake response")); return; } String accept = response.headers().get(HttpHeaders.Names.SEC_WEBSOCKET_ACCEPT); String key = getAcceptKey( future .getNettyRequest() .getHttpRequest() .headers() .get(HttpHeaders.Names.SEC_WEBSOCKET_KEY)); if (accept == null || !accept.equals(key)) { requestSender.abort( channel, future, new IOException( String.format("Invalid challenge. Actual: %s. Expected: %s", accept, key))); } channelManager.upgradePipelineForWebSockets(channel.pipeline()); invokeOnSucces(channel, handler); future.done(); // set back the future so the protocol gets notified of frames Channels.setAttribute(channel, future); }
private boolean handleUnauthorizedAndExit( int statusCode, Realm realm, final Request request, HttpResponse response, final NettyResponseFuture<?> future, ProxyServer proxyServer, final Channel channel) throws Exception { if (statusCode == UNAUTHORIZED.code() && realm != null) { List<String> authenticateHeaders = response.headers().getAll(HttpHeaders.Names.WWW_AUTHENTICATE); if (!authenticateHeaders.isEmpty() && !future.getAndSetAuth(true)) { future.setState(NettyResponseFuture.STATE.NEW); Realm newRealm = null; // NTLM boolean negociate = authenticateHeaders.contains("Negotiate"); if (!authenticateHeaders.contains("Kerberos") && (isNTLM(authenticateHeaders) || negociate)) { newRealm = ntlmChallenge( authenticateHeaders, request, proxyServer, request.getHeaders(), realm, future); // SPNEGO KERBEROS } else if (negociate) { newRealm = kerberosChallenge( authenticateHeaders, request, proxyServer, request.getHeaders(), realm, future); if (newRealm == null) { return true; } } else { newRealm = new Realm.RealmBuilder() .clone(realm) .setScheme(realm.getAuthScheme()) .setUri(request.getURI().getPath()) .setMethodName(request.getMethod()) .setUsePreemptiveAuth(true) .parseWWWAuthenticateHeader(authenticateHeaders.get(0)) .build(); } Realm nr = new Realm.RealmBuilder() .clone(newRealm) .setUri(URI.create(request.getUrl()).getPath()) .build(); final Request nextRequest = new RequestBuilder(future.getRequest()) .setHeaders(request.getHeaders()) .setRealm(nr) .build(); LOGGER.debug("Sending authentication to {}", request.getUrl()); Callback callback = new Callback(future) { public void call() throws Exception { channels.drainChannel(channel, future); requestSender.sendNextRequest(nextRequest, future); } }; if (future.isKeepAlive() && HttpHeaders.isTransferEncodingChunked(response)) { // We must make sure there is no bytes left // before executing the next request. Channels.setDefaultAttribute(channel, callback); } else { callback.call(); } return true; } } return false; }
private static List<String> getProxyAuthorizationHeader(Request request) { return request.getHeaders().get(PROXY_AUTHORIZATION_HEADER); }
protected boolean exitAfterHandlingRedirect( // Channel channel, // NettyResponseFuture<?> future, // HttpResponse response, // Request request, // int statusCode) throws Exception { if (followRedirect(config, request) && REDIRECT_STATUSES.contains(statusCode)) { if (future.incrementAndGetCurrentRedirectCount() >= config.getMaxRedirects()) { throw new MaxRedirectException("Maximum redirect reached: " + config.getMaxRedirects()); } else { // We must allow 401 handling again. future.getAndSetAuth(false); HttpHeaders responseHeaders = response.headers(); String location = responseHeaders.get(HttpHeaders.Names.LOCATION); Uri uri = Uri.create(future.getUri(), location); if (!uri.equals(future.getUri())) { final RequestBuilder requestBuilder = new RequestBuilder(future.getRequest()); if (!config.isRemoveQueryParamOnRedirect()) requestBuilder.addQueryParams(future.getRequest().getQueryParams()); // if we are to strictly handle 302, we should keep the original method (which browsers // don't) // 303 must force GET if ((statusCode == FOUND.code() && !config.isStrict302Handling()) || statusCode == SEE_OTHER.code()) requestBuilder.setMethod("GET"); // in case of a redirect from HTTP to HTTPS, future attributes might change final boolean initialConnectionKeepAlive = future.isKeepAlive(); final String initialPoolKey = channelManager.getPartitionId(future); future.setUri(uri); String newUrl = uri.toUrl(); if (request.getUri().getScheme().startsWith(WEBSOCKET)) { newUrl = newUrl.replaceFirst(HTTP, WEBSOCKET); } logger.debug("Redirecting to {}", newUrl); for (String cookieStr : responseHeaders.getAll(HttpHeaders.Names.SET_COOKIE)) { Cookie c = CookieDecoder.decode(cookieStr, timeConverter); if (c != null) requestBuilder.addOrReplaceCookie(c); } Callback callback = channelManager.newDrainCallback( future, channel, initialConnectionKeepAlive, initialPoolKey); if (HttpHeaders.isTransferEncodingChunked(response)) { // We must make sure there is no bytes left before // executing the next request. // FIXME investigate this Channels.setAttribute(channel, callback); } else { // FIXME don't understand: this offers the connection to the pool, or even closes it, // while the // request has not been sent, right? callback.call(); } Request redirectRequest = requestBuilder.setUrl(newUrl).build(); // FIXME why not reuse the channel is same host? requestSender.sendNextRequest(redirectRequest, future); return true; } } } return false; }