All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.jetty.spdy.server.proxy.HTTPProxyEngine Maven / Gradle / Ivy

There is a newer version: 11.0.0.beta1
Show newest version
//
//  ========================================================================
//  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
//  ------------------------------------------------------------------------
//  All rights reserved. This program and the accompanying materials
//  are made available under the terms of the Eclipse Public License v1.0
//  and Apache License v2.0 which accompanies this distribution.
//
//      The Eclipse Public License is available at
//      http://www.eclipse.org/legal/epl-v10.html
//
//      The Apache License v2.0 is available at
//      http://www.opensource.org/licenses/apache2.0.php
//
//  You may elect to redistribute this code under either of these licenses.
//  ========================================================================
//


package org.eclipse.jetty.spdy.server.proxy;

import java.nio.ByteBuffer;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;

import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.util.DeferredContentProvider;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
import org.eclipse.jetty.spdy.api.DataInfo;
import org.eclipse.jetty.spdy.api.HeadersInfo;
import org.eclipse.jetty.spdy.api.ReplyInfo;
import org.eclipse.jetty.spdy.api.RstInfo;
import org.eclipse.jetty.spdy.api.Stream;
import org.eclipse.jetty.spdy.api.StreamFrameListener;
import org.eclipse.jetty.spdy.api.StreamStatus;
import org.eclipse.jetty.spdy.api.SynInfo;
import org.eclipse.jetty.spdy.server.http.HTTPSPDYHeader;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Fields;
import org.eclipse.jetty.util.HttpCookieStore;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;

/**
 * 

{@link HTTPProxyEngine} implements a SPDY to HTTP proxy, that is, converts SPDY events received by clients into * HTTP events for the servers.

*/ public class HTTPProxyEngine extends ProxyEngine { private static final Logger LOG = Log.getLogger(HTTPProxyEngine.class); private static final Callback LOGGING_CALLBACK = new LoggingCallback(); private final HttpClient httpClient; public HTTPProxyEngine(HttpClient httpClient) { this.httpClient = httpClient; configureHttpClient(httpClient); } private void configureHttpClient(HttpClient httpClient) { // Redirects must be proxied as is, not followed httpClient.setFollowRedirects(false); // Must not store cookies, otherwise cookies of different clients will mix httpClient.setCookieStore(new HttpCookieStore.Empty()); } public StreamFrameListener proxy(final Stream clientStream, SynInfo clientSynInfo, ProxyEngineSelector.ProxyServerInfo proxyServerInfo) { short version = clientStream.getSession().getVersion(); String method = clientSynInfo.getHeaders().get(HTTPSPDYHeader.METHOD.name(version)).value(); String path = clientSynInfo.getHeaders().get(HTTPSPDYHeader.URI.name(version)).value(); Fields headers = new Fields(clientSynInfo.getHeaders(), false); removeHopHeaders(headers); addRequestProxyHeaders(clientStream, headers); customizeRequestHeaders(clientStream, headers); String host = proxyServerInfo.getHost(); int port = proxyServerInfo.getAddress().getPort(); LOG.debug("Sending HTTP request to: {}", host + ":" + port); final Request request = httpClient.newRequest(host, port) .path(path) .method(HttpMethod.fromString(method)); addNonSpdyHeadersToRequest(version, headers, request); if (!clientSynInfo.isClose()) { request.content(new DeferredContentProvider()); } sendRequest(clientStream, request); return new StreamFrameListener.Adapter() { @Override public void onReply(Stream stream, ReplyInfo replyInfo) { // We proxy to HTTP so we do not receive replies throw new UnsupportedOperationException("Not Yet Implemented"); } @Override public void onHeaders(Stream stream, HeadersInfo headersInfo) { throw new UnsupportedOperationException("Not Yet Implemented"); } @Override public void onData(Stream clientStream, final DataInfo clientDataInfo) { LOG.debug("received clientDataInfo: {} for stream: {}", clientDataInfo, clientStream); DeferredContentProvider contentProvider = (DeferredContentProvider)request.getContent(); contentProvider.offer(clientDataInfo.asByteBuffer(true)); if (clientDataInfo.isClose()) contentProvider.close(); } }; } private void sendRequest(final Stream clientStream, Request request) { request.send(new Response.Listener.Empty() { private volatile boolean committed; @Override public void onHeaders(final Response response) { LOG.debug("onHeaders called with response: {}. Sending replyInfo to client.", response); Fields responseHeaders = createResponseHeaders(clientStream, response); removeHopHeaders(responseHeaders); ReplyInfo replyInfo = new ReplyInfo(responseHeaders, false); clientStream.reply(replyInfo, new Callback.Adapter() { @Override public void failed(Throwable x) { LOG.debug("failed: ", x); response.abort(x); } @Override public void succeeded() { committed = true; } }); } @Override public void onContent(final Response response, ByteBuffer content) { LOG.debug("onContent called with response: {} and content: {}. Sending response content to client.", response, content); final ByteBuffer contentCopy = httpClient.getByteBufferPool().acquire(content.remaining(), true); BufferUtil.flipPutFlip(content, contentCopy); ByteBufferDataInfo dataInfo = new ByteBufferDataInfo(contentCopy, false); clientStream.data(dataInfo, new Callback() { @Override public void failed(Throwable x) { LOG.debug("failed: ", x); releaseBuffer(); response.abort(x); } @Override public void succeeded() { releaseBuffer(); } private void releaseBuffer() { httpClient.getByteBufferPool().release(contentCopy); } }); } @Override public void onSuccess(Response response) { LOG.debug("onSuccess called. Closing client stream."); clientStream.data(new ByteBufferDataInfo(BufferUtil.EMPTY_BUFFER, true), LOGGING_CALLBACK); } @Override public void onFailure(Response response, Throwable failure) { LOG.debug("onFailure called: ", failure); if (committed) { LOG.debug("clientStream already committed. Resetting stream."); try { clientStream.getSession().rst(new RstInfo(clientStream.getId(), StreamStatus.INTERNAL_ERROR)); } catch (InterruptedException | ExecutionException | TimeoutException e) { LOG.debug(e); } } else { if (clientStream.isClosed()) return; Fields responseHeaders = createResponseHeaders(clientStream, response); if (failure instanceof TimeoutException) responseHeaders.add(HTTPSPDYHeader.STATUS.name(clientStream.getSession().getVersion()), String.valueOf(HttpStatus.GATEWAY_TIMEOUT_504)); else responseHeaders.add(HTTPSPDYHeader.STATUS.name(clientStream.getSession().getVersion()), String.valueOf(HttpStatus.BAD_GATEWAY_502)); ReplyInfo replyInfo = new ReplyInfo(responseHeaders, true); clientStream.reply(replyInfo, LOGGING_CALLBACK); } } }); } private Fields createResponseHeaders(Stream clientStream, Response response) { Fields responseHeaders = new Fields(); for (HttpField header : response.getHeaders()) responseHeaders.add(header.getName(), header.getValue()); short version = clientStream.getSession().getVersion(); if (response.getStatus() > 0) responseHeaders.add(HTTPSPDYHeader.STATUS.name(version), String.valueOf(response.getStatus())); responseHeaders.add(HTTPSPDYHeader.VERSION.name(version), HttpVersion.HTTP_1_1.asString()); addResponseProxyHeaders(clientStream, responseHeaders); return responseHeaders; } private void addNonSpdyHeadersToRequest(short version, Fields headers, Request request) { for (Fields.Field header : headers) if (HTTPSPDYHeader.from(version, header.name()) == null) request.header(header.name(), header.value()); } static class LoggingCallback extends Callback.Adapter { @Override public void failed(Throwable x) { LOG.debug(x); } @Override public void succeeded() { LOG.debug("succeeded"); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy