Skip to content
This repository has been archived by the owner on May 7, 2020. It is now read-only.

Use Jetty's ProxyServlet implementation #2753

Merged
merged 5 commits into from
Feb 13, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bundles/ui/org.eclipse.smarthome.ui/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Import-Package: javax.imageio,
org.eclipse.jetty.client.api,
org.eclipse.jetty.client.util,
org.eclipse.jetty.http,
org.eclipse.jetty.proxy;resolution:=optional,
org.eclipse.jetty.util,
org.eclipse.jetty.util.component,
org.eclipse.jetty.util.ssl,
Expand Down
5 changes: 4 additions & 1 deletion bundles/ui/org.eclipse.smarthome.ui/OSGI-INF/proxy.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@

-->
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="activate" deactivate="deactivate" immediate="true" name="org.eclipse.smarthome.ui.proxy">
<implementation class="org.eclipse.smarthome.ui.internal.proxy.ProxyServlet"/>
<implementation class="org.eclipse.smarthome.ui.internal.proxy.ProxyServletService"/>

<property name="service.pid" type="String" value="org.eclipse.smarthome.proxy"/>

<reference bind="setItemUIRegistry" cardinality="1..1" interface="org.eclipse.smarthome.ui.items.ItemUIRegistry" name="ItemUIRegistry" policy="dynamic" unbind="unsetItemUIRegistry"/>
<reference bind="setHttpService" cardinality="1..1" interface="org.osgi.service.http.HttpService" name="HttpService" policy="dynamic" unbind="unsetHttpService"/>
<reference bind="setModelRepository" cardinality="1..1" interface="org.eclipse.smarthome.model.core.ModelRepository" name="ModelRepository" policy="static" unbind="unsetModelRepository"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/**
* Copyright (c) 2014-2016 by the respective copyright holders.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.smarthome.ui.internal.proxy;

import java.util.Objects;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.util.ssl.SslContextFactory;

/**
* This version of the proxy servlet uses asynchronous I/O and request processing, and is based on Jetty's proxy
* servlets. It depends on Servlet API 3.0 or later.
*
* @author John Cocula - new version that uses Jetty proxy classes
*/
public class AsyncProxyServlet extends org.eclipse.jetty.proxy.AsyncProxyServlet {

private static final long serialVersionUID = -4716754591953017795L;

private final ProxyServletService service;

AsyncProxyServlet(ProxyServletService service) {
super();
this.service = service;
}

@Override
public String getServletInfo() {
return "Proxy (async)";
}

/**
* {@inheritDoc}
*
* Override <code>newHttpClient</code> so we can proxy to HTTPS URIs.
*/
@Override
protected HttpClient newHttpClient() {
return new HttpClient(new SslContextFactory());
}

/**
* {@inheritDoc}
*
* Add Basic Authentication header to request if user and password are specified in URI.
*
* After Jetty is upgraded past 9.2.9, change to <code>copyRequestHeaders</code> to avoid deprecation warning.
*/
@SuppressWarnings("deprecation")
@Override
protected void copyHeaders(HttpServletRequest clientRequest, Request proxyRequest) {
super.copyHeaders(clientRequest, proxyRequest);

service.maybeAppendAuthHeader(service.uriFromRequest(clientRequest), proxyRequest);
}

/**
* {@inheritDoc}
*/
@Override
protected String rewriteTarget(HttpServletRequest request) {
return Objects.toString(service.uriFromRequest(request), null);
}

/**
* {@inheritDoc}
*/
@Override
protected void onProxyRewriteFailed(HttpServletRequest request, HttpServletResponse response) {
service.sendError(request, response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/**
* Copyright (c) 2014-2016 by the respective copyright holders.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.smarthome.ui.internal.proxy;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.Iterator;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;
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.InputStreamResponseListener;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* A blocking version of the proxy servlet that complies with Servlet API 2.4.
*
* @author Kai Kreuzer - Initial contribution and API
* @author Svilen Valkanov - Replaced Apache HttpClient with Jetty
* @author John Cocula - refactored to support alternate implementation
*/
public class BlockingProxyServlet extends HttpServlet {

private final Logger logger = LoggerFactory.getLogger(BlockingProxyServlet.class);

private static final long serialVersionUID = -4716754591953017794L;

private final ProxyServletService service;

private static HttpClient httpClient = new HttpClient(new SslContextFactory());

/** Timeout for HTTP requests in ms */
private static final int TIMEOUT = 15000;

BlockingProxyServlet(ProxyServletService service) {
super();
this.service = service;
if (!httpClient.isStarted()) {
try {
httpClient.start();
} catch (Exception e) {
logger.warn("Cannot start HttpClient!", e);
}
}
}

@Override
public String getServletInfo() {
return "Proxy (blocking)";
}

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

URI uri = service.uriFromRequest(request);

if (uri == null) {
service.sendError(request, response);
} else {
Request httpRequest = httpClient.newRequest(uri);

service.maybeAppendAuthHeader(uri, httpRequest);

InputStreamResponseListener listener = new InputStreamResponseListener();

// do the client request
try {
httpRequest.send(listener);
// wait for the response headers to arrive or the timeout to expire
Response httpResponse = listener.get(TIMEOUT, TimeUnit.MILLISECONDS);

// get response headers
HttpFields headers = httpResponse.getHeaders();
Iterator<HttpField> iterator = headers.iterator();

// copy all headers
while (iterator.hasNext()) {
HttpField header = iterator.next();
response.setHeader(header.getName(), header.getValue());
}
} catch (Exception e) {
if (e instanceof TimeoutException) {
logger.warn("Proxy servlet failed to stream content due to a timeout");
response.sendError(HttpServletResponse.SC_GATEWAY_TIMEOUT);
} else {
logger.warn("Proxy servlet failed to stream content: {}", e.getMessage());
response.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
}
return;
}
// now copy/stream the body content
try (InputStream responseContent = listener.getInputStream()) {
IOUtils.copy(responseContent, response.getOutputStream());
}
}
}
}
Loading