[
https://issues.apache.org/jira/browse/KARAF-5614?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16420119#comment-16420119
]
ASF GitHub Bot commented on KARAF-5614:
---------------------------------------
jbonofre closed pull request #474: [KARAF-5614] Add HTTP ProxyService
URL: https://github.com/apache/karaf/pull/474
This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:
As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):
diff --git a/http/pom.xml b/http/pom.xml
index 4deed4e2de..c021f0c8e8 100644
--- a/http/pom.xml
+++ b/http/pom.xml
@@ -43,6 +43,11 @@
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
<scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.web</groupId>
@@ -69,6 +74,11 @@
<artifactId>org.apache.karaf.shell.core</artifactId>
<optional>true</optional>
</dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <version>4.5.3</version>
+ </dependency>
</dependencies>
<build>
@@ -98,14 +108,17 @@
<configuration>
<instructions>
<Export-Package>
- org.apache.karaf.http.core,
+ org.apache.karaf.http.core
</Export-Package>
<Private-Package>
org.apache.karaf.http.command,
+ org.apache.karaf.http.command.completers,
org.apache.karaf.http.core.internal,
org.apache.karaf.http.core.internal.osgi,
org.apache.felix.utils.version,
org.apache.felix.utils.manifest,
+ org.apache.http*,
+ org.apache.commons.codec*
</Private-Package>
</instructions>
</configuration>
diff --git
a/http/src/main/java/org/apache/karaf/http/command/ProxyAddCommand.java
b/http/src/main/java/org/apache/karaf/http/command/ProxyAddCommand.java
new file mode 100644
index 0000000000..467ce4c584
--- /dev/null
+++ b/http/src/main/java/org/apache/karaf/http/command/ProxyAddCommand.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.http.command;
+
+import org.apache.karaf.http.core.ProxyService;
+import org.apache.karaf.shell.api.action.Action;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Option;
+import org.apache.karaf.shell.api.action.lifecycle.Reference;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+
+@Command(scope = "http", name = "proxy-add", description = "Add a new HTTP
proxy")
+@Service
+public class ProxyAddCommand implements Action {
+
+ @Reference
+ private ProxyService proxyService;
+
+ @Argument(index = 0, name = "url", description = "HTTP proxy URL",
required = true, multiValued = false)
+ String url;
+
+ @Argument(index = 1, name = "proxyTo", description = "HTTP location to
proxy on the prefix", required = true, multiValued = false)
+ String proxyTo;
+
+ @Override
+ public Object execute() throws Exception {
+ proxyService.addProxy(url, proxyTo);
+ return null;
+ }
+
+}
diff --git
a/http/src/main/java/org/apache/karaf/http/command/ProxyListCommand.java
b/http/src/main/java/org/apache/karaf/http/command/ProxyListCommand.java
new file mode 100644
index 0000000000..2e5380df39
--- /dev/null
+++ b/http/src/main/java/org/apache/karaf/http/command/ProxyListCommand.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.http.command;
+
+import org.apache.karaf.http.core.ProxyService;
+import org.apache.karaf.shell.api.action.Action;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.lifecycle.Reference;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.apache.karaf.shell.support.table.ShellTable;
+
+@Command(scope = "http", name = "proxies", description = "List the HTTP
proxies")
+@Service
+public class ProxyListCommand implements Action {
+
+ @Reference
+ private ProxyService proxyService;
+
+ @Override
+ public Object execute() throws Exception {
+ ShellTable table = new ShellTable();
+ table.column("URL");
+ table.column("ProxyTo");
+ for (String url : proxyService.getProxies().keySet()) {
+ table.addRow().addContent(url,
proxyService.getProxies().get(url));
+ }
+ table.print(System.out);
+ return null;
+ }
+
+}
diff --git
a/http/src/main/java/org/apache/karaf/http/command/ProxyRemoveCommand.java
b/http/src/main/java/org/apache/karaf/http/command/ProxyRemoveCommand.java
new file mode 100644
index 0000000000..540a7ed79a
--- /dev/null
+++ b/http/src/main/java/org/apache/karaf/http/command/ProxyRemoveCommand.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.http.command;
+
+import org.apache.karaf.http.command.completers.ProxyUrlCompleter;
+import org.apache.karaf.http.core.ProxyService;
+import org.apache.karaf.shell.api.action.Action;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Reference;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+
+@Command(scope = "http", name = "proxy-remove", description = "Remove an
existing HTTP proxy")
+@Service
+public class ProxyRemoveCommand implements Action {
+
+ @Reference
+ private ProxyService proxyService;
+
+ @Argument(name = "prefix", description = "The HTTP proxy prefix", required
= true, multiValued = false)
+ @Completion(ProxyUrlCompleter.class)
+ String prefix;
+
+ @Override
+ public Object execute() throws Exception {
+ proxyService.removeProxy(prefix);
+ return null;
+ }
+
+}
diff --git
a/http/src/main/java/org/apache/karaf/http/command/completers/ProxyUrlCompleter.java
b/http/src/main/java/org/apache/karaf/http/command/completers/ProxyUrlCompleter.java
new file mode 100644
index 0000000000..8250e70673
--- /dev/null
+++
b/http/src/main/java/org/apache/karaf/http/command/completers/ProxyUrlCompleter.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.http.command.completers;
+
+import org.apache.karaf.http.core.ProxyService;
+import org.apache.karaf.shell.api.action.lifecycle.Reference;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.apache.karaf.shell.api.console.CommandLine;
+import org.apache.karaf.shell.api.console.Completer;
+import org.apache.karaf.shell.api.console.Session;
+import org.apache.karaf.shell.support.completers.StringsCompleter;
+
+import java.util.List;
+import java.util.Set;
+
+@Service
+public class ProxyUrlCompleter implements Completer {
+
+ @Reference
+ private ProxyService proxyService;
+
+ @Override
+ public int complete(Session session, CommandLine commandLine, List<String>
candidates) {
+ StringsCompleter delegate = new StringsCompleter();
+ try {
+ Set<String> urls = proxyService.getProxies().keySet();
+ for (String url : urls) {
+ delegate.getStrings().add(url);
+ }
+ } catch (Exception e) {
+ // nothing to do
+ }
+ return delegate.complete(session, commandLine, candidates);
+ }
+
+}
diff --git a/http/src/main/java/org/apache/karaf/http/core/HttpMBean.java
b/http/src/main/java/org/apache/karaf/http/core/HttpMBean.java
index eddc64702c..f2246941e9 100644
--- a/http/src/main/java/org/apache/karaf/http/core/HttpMBean.java
+++ b/http/src/main/java/org/apache/karaf/http/core/HttpMBean.java
@@ -18,6 +18,7 @@
import javax.management.MBeanException;
import javax.management.openmbean.TabularData;
+import java.util.Map;
/**
* HTTP MBean.
@@ -32,4 +33,19 @@
*/
TabularData getServlets() throws MBeanException;
+ /**
+ * List configured HTTP proxies.
+ */
+ Map<String, String> getProxies() throws MBeanException;
+
+ /**
+ * Add a new HTTP proxy using URL, proxyTo and prefix.
+ */
+ void addProxy(String url, String proxyTo) throws MBeanException;
+
+ /**
+ * Remove an existing HTTP proxy identified by URL.
+ */
+ void removeProxy(String url) throws MBeanException;
+
}
diff --git a/http/src/main/java/org/apache/karaf/http/core/ProxyService.java
b/http/src/main/java/org/apache/karaf/http/core/ProxyService.java
new file mode 100644
index 0000000000..63d03fe858
--- /dev/null
+++ b/http/src/main/java/org/apache/karaf/http/core/ProxyService.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.http.core;
+
+import java.util.Map;
+
+public interface ProxyService {
+
+ Map<String, String> getProxies();
+
+ void addProxy(String url, String proxyTo) throws Exception;
+
+ void removeProxy(String url) throws Exception;
+
+}
diff --git
a/http/src/main/java/org/apache/karaf/http/core/internal/HttpMBeanImpl.java
b/http/src/main/java/org/apache/karaf/http/core/internal/HttpMBeanImpl.java
index f290aef034..6717db2331 100644
--- a/http/src/main/java/org/apache/karaf/http/core/internal/HttpMBeanImpl.java
+++ b/http/src/main/java/org/apache/karaf/http/core/internal/HttpMBeanImpl.java
@@ -17,7 +17,9 @@
package org.apache.karaf.http.core.internal;
import java.util.Arrays;
+import java.util.Collection;
import java.util.List;
+import java.util.Map;
import javax.management.MBeanException;
import javax.management.NotCompliantMBeanException;
@@ -31,21 +33,23 @@
import javax.management.openmbean.TabularDataSupport;
import javax.management.openmbean.TabularType;
-import org.apache.karaf.http.core.HttpMBean;
-import org.apache.karaf.http.core.ServletInfo;
-import org.apache.karaf.http.core.ServletService;
+import org.apache.karaf.http.core.*;
/**
* Implementation of the HTTP MBean.
*/
public class HttpMBeanImpl extends StandardMBean implements HttpMBean {
+
private ServletService servletService;
+ private ProxyService proxyService;
- public HttpMBeanImpl(ServletService servletService) throws
NotCompliantMBeanException {
+ public HttpMBeanImpl(ServletService servletService, ProxyService
proxyService) throws NotCompliantMBeanException {
super(HttpMBean.class);
this.servletService = servletService;
+ this.proxyService = proxyService;
}
+ @Override
public TabularData getServlets() throws MBeanException {
try {
CompositeType servletType = new CompositeType("Servlet", "HTTP
Servlet",
@@ -56,12 +60,10 @@ public TabularData getServlets() throws MBeanException {
TabularData table = new TabularDataSupport(tableType);
List<ServletInfo> servletInfos = servletService.getServlets();
for (ServletInfo info : servletInfos) {
-
CompositeData data = new CompositeDataSupport(servletType,
new String[]{"Bundle-ID", "Servlet", "Servlet Name",
"State", "Alias", "URL"},
new Object[]{info.getBundleId(), info.getClassName(),
info.getName(), info.getStateString(), info.getAlias(),
Arrays.toString(info.getUrls())});
table.put(data);
-
}
return table;
} catch (Exception e) {
@@ -69,4 +71,27 @@ public TabularData getServlets() throws MBeanException {
}
}
+ @Override
+ public Map<String, String> getProxies() throws MBeanException {
+ return proxyService.getProxies();
+ }
+
+ @Override
+ public void addProxy(String url, String proxyTo) throws MBeanException {
+ try {
+ proxyService.addProxy(url, proxyTo);
+ } catch (Exception e) {
+ throw new MBeanException(null, e.toString());
+ }
+ }
+
+ @Override
+ public void removeProxy(String url) throws MBeanException {
+ try {
+ proxyService.removeProxy(url);
+ } catch (Exception e) {
+ throw new MBeanException(null, e.toString());
+ }
+ }
+
}
diff --git
a/http/src/main/java/org/apache/karaf/http/core/internal/ProxyServiceImpl.java
b/http/src/main/java/org/apache/karaf/http/core/internal/ProxyServiceImpl.java
new file mode 100644
index 0000000000..16e481f04a
--- /dev/null
+++
b/http/src/main/java/org/apache/karaf/http/core/internal/ProxyServiceImpl.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.http.core.internal;
+
+import org.apache.karaf.http.core.ProxyService;
+import org.osgi.service.http.HttpService;
+
+import java.util.*;
+
+public class ProxyServiceImpl implements ProxyService {
+
+ private HttpService httpService;
+ private Map<String, String> proxies;
+
+ public ProxyServiceImpl(HttpService httpService) {
+ this.httpService = httpService;
+ this.proxies = new HashMap<>();
+ }
+
+ @Override
+ public Map<String, String> getProxies() {
+ return proxies;
+ }
+
+ @Override
+ public void addProxy(String url, String proxyTo) throws Exception {
+ ProxyServlet proxyServlet = new ProxyServlet();
+ proxyServlet.setProxyTo(proxyTo);
+ httpService.registerServlet(url, proxyServlet, new Hashtable(), null);
+ proxies.put(url, proxyTo);
+ }
+
+ @Override
+ public void removeProxy(String url) throws Exception {
+ httpService.unregister(url);
+ proxies.remove(url);
+ }
+}
diff --git
a/http/src/main/java/org/apache/karaf/http/core/internal/ProxyServlet.java
b/http/src/main/java/org/apache/karaf/http/core/internal/ProxyServlet.java
new file mode 100644
index 0000000000..597e5dd313
--- /dev/null
+++ b/http/src/main/java/org/apache/karaf/http/core/internal/ProxyServlet.java
@@ -0,0 +1,485 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.http.core.internal;
+
+import org.apache.http.*;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.AbortableHttpRequest;
+import org.apache.http.client.params.ClientPNames;
+import org.apache.http.client.params.CookiePolicy;
+import org.apache.http.client.utils.URIUtils;
+import org.apache.http.cookie.SM;
+import org.apache.http.entity.InputStreamEntity;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.message.BasicHttpEntityEnclosingRequest;
+import org.apache.http.message.BasicHttpRequest;
+import org.apache.http.message.HeaderGroup;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.util.EntityUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.reflect.Constructor;
+import java.net.HttpCookie;
+import java.net.URI;
+import java.util.BitSet;
+import java.util.Enumeration;
+import java.util.Formatter;
+import java.util.List;
+
+/**
+ * This is a simple servlet acting as a HTTP reverse proxy/gateway. It works
with any webcontainer as it's a regular servlet.
+ */
+public class ProxyServlet extends HttpServlet {
+
+ private final static Logger LOGGER =
LoggerFactory.getLogger(ProxyServlet.class);
+
+ protected String proxyTo;
+ protected boolean doForwardIP = true;
+ protected boolean doSendUrlFragment = true;
+
+ private HttpClient proxyClient;
+
+ public void setIPForwarding(boolean ipForwarding) {
+ this.doForwardIP = ipForwarding;
+ }
+
+ public void setProxyTo(String proxyTo) {
+ this.proxyTo = proxyTo;
+ }
+
+ @Override
+ public String getServletInfo() {
+ return "Apache Karaf Proxy Servlet";
+ }
+
+ @Override
+ public void init() throws ServletException {
+ HttpParams hcParams = new BasicHttpParams();
+ hcParams.setParameter(ClientPNames.COOKIE_POLICY,
CookiePolicy.IGNORE_COOKIES);
+ proxyClient = createHttpClient(hcParams);
+ }
+
+ protected HttpClient createHttpClient(HttpParams hcParams) {
+ try {
+ Class clientClazz =
Class.forName("og.apache.http.impl.client.SystemDefaultHttpClient");
+ Constructor constructor =
clientClazz.getConstructor(HttpParams.class);
+ return (HttpClient) constructor.newInstance(hcParams);
+ } catch (ClassNotFoundException e) {
+ // not a problem, we fallback on the "old" client
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ // fallback on using "old" client style
+ return new DefaultHttpClient(new ThreadSafeClientConnManager(),
hcParams);
+ }
+
+ @Override
+ public void destroy() {
+ // starting for HttpComponents 4.3, clients implements Closeable
+ if (proxyClient instanceof Closeable) {
+ try {
+ ((Closeable) proxyClient).close();
+ } catch (IOException e) {
+ log("Error occurred when closing HTTP client in the proxy
servlet destroy", e);
+ }
+ } else {
+ // older HttpComponents version requires to close the client
+ if (proxyClient != null) {
+ proxyClient.getConnectionManager().shutdown();
+ }
+ }
+ super.destroy();
+ }
+
+ @Override
+ protected void service(HttpServletRequest servletRequest,
HttpServletResponse servletResponse) throws ServletException, IOException {
+ URI locationUri = URI.create(proxyTo);
+ HttpHost host = URIUtils.extractHost(locationUri);
+
+ LOGGER.debug("Proxy to {} (host {})", locationUri, host);
+
+ String method = servletRequest.getMethod();
+ String proxyRequestUri = rewriteUrlFromRequest(servletRequest,
proxyTo);
+ HttpRequest proxyRequest;
+
+ // spec: RFC 2616, sec 4.3: either of these two headers means there is
a message body
+ if (servletRequest.getHeader(HttpHeaders.CONTENT_LENGTH) != null ||
servletRequest.getHeader(HttpHeaders.TRANSFER_ENCODING) != null) {
+ HttpEntityEnclosingRequest entityProxyRequest = new
BasicHttpEntityEnclosingRequest(method, proxyRequestUri);
+ entityProxyRequest.setEntity(new
InputStreamEntity(servletRequest.getInputStream(),
servletRequest.getContentLength()));
+ proxyRequest = entityProxyRequest;
+ } else {
+ proxyRequest = new BasicHttpRequest(method, proxyRequestUri);
+ }
+
+ copyRequestHeaders(servletRequest, proxyRequest, host);
+
+ setXForwardedForHeader(servletRequest, proxyRequest);
+
+ HttpResponse proxyResponse = null;
+ try {
+ // execute the request
+ proxyResponse = proxyClient.execute(host, proxyRequest);
+
+ // process the response
+ int statusCode = proxyResponse.getStatusLine().getStatusCode();
+
+ // copying response headers to make sure SESSIONID or other Cookie
which comes from the remote host
+ // will be saved in client when the proxied URL was redirect to
another one.
+ copyResponseHeaders(proxyResponse, servletRequest,
servletResponse);
+
+ if (doResponseRedirect(servletRequest, servletResponse,
proxyResponse, statusCode, proxyTo)) {
+ // the response is already "committed" now without any body to
send
+ return;
+ }
+
+ // pass the response code
+ servletResponse.setStatus(statusCode,
proxyResponse.getStatusLine().getReasonPhrase());
+
+ // send the content to the client
+ copyResponseEntity(proxyResponse, servletResponse);
+ } catch (Exception e) {
+ // abort request
+ if (proxyRequest instanceof AbortableHttpRequest) {
+ AbortableHttpRequest abortableHttpRequest =
(AbortableHttpRequest) proxyRequest;
+ abortableHttpRequest.abort();
+ }
+ if (e instanceof RuntimeException) {
+ throw (RuntimeException) e;
+ }
+ if (e instanceof ServletException) {
+ throw (IOException) e;
+ }
+ throw new RuntimeException(e);
+ } finally {
+ if (proxyResponse != null) {
+ consumeQuietly(proxyResponse.getEntity());
+ }
+ }
+ }
+
+ protected boolean doResponseRedirect(HttpServletRequest servletRequest,
HttpServletResponse servletResponse, HttpResponse proxyResponse, int
statusCode, String proxyTo) throws ServletException, IOException {
+ // check if the proxy is a redirect
+ if (statusCode >= HttpServletResponse.SC_MULTIPLE_CHOICES &&
statusCode < HttpServletResponse.SC_NOT_MODIFIED) {
+ Header locationHeader =
proxyResponse.getLastHeader(HttpHeaders.LOCATION);
+ if (locationHeader != null) {
+ throw new ServletException("Received a redirect (" +
statusCode + ") but without location (" + HttpHeaders.LOCATION + " header)");
+ }
+ // modify the redirect to go to this proxy servlet rather than the
proxied host
+ String locationString = rewriteUrlFromResponse(servletRequest,
locationHeader.getValue(), proxyTo);
+ servletResponse.sendRedirect(locationString);
+ return true;
+ }
+ // 304 needs special handling. See:
+ // http://www.ics.uci.edu/pub/ietf/http/rfc1945.html#Code304
+ // We get a 304 whenever passed an 'If-Modified-Since'
+ // header and the data on disk has not changed; server
+ // responds w/ a 304 saying I'm not going to send the
+ // body because the file has not changed.
+ if (statusCode == HttpServletResponse.SC_NOT_MODIFIED) {
+ servletResponse.setIntHeader(HttpHeaders.CONTENT_LENGTH, 0);
+ servletResponse.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
+ return true;
+ }
+ return false;
+ }
+
+ protected void close(Closeable closeable) {
+ try {
+ closeable.close();
+ } catch (IOException e) {
+ log(e.getMessage(), e);
+ }
+ }
+
+ protected void consumeQuietly(HttpEntity httpEntity) {
+ try {
+ EntityUtils.consume(httpEntity);
+ } catch (IOException e) {
+ log(e.getMessage(), e);
+ }
+ }
+
+ /**
+ * These are the "hop-by-hop" headers that should not be copied.
+ * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html
+ * We use an HttpClient HeaderGroup class instead of Set of String because
this
+ * approach does case insensitive lookup faster.
+ */
+ protected static final HeaderGroup hopByHopHeaders;
+
+ static {
+ hopByHopHeaders = new HeaderGroup();
+ String[] headers = new String[] {"Connection", "Keep-Alive",
"Proxy-Authenticate", "Proxy-Authorization", "TE", "Trailers",
"Transfer-Encoding", "Upgrade"};
+ for (String header : headers) {
+ hopByHopHeaders.addHeader(new BasicHeader(header, null));
+ }
+ }
+
+ /**
+ * Copy request headers from the servlet client to the proxy request.
+ */
+ protected void copyRequestHeaders(HttpServletRequest servletRequest,
HttpRequest proxyRequest, HttpHost host) {
+ Enumeration headerNames = servletRequest.getHeaderNames();
+ while (headerNames.hasMoreElements()) {
+ String headerName = (String) headerNames.nextElement();
+ // instead the content-length is effectively set via
InputStreamEntity
+ if (headerName.equalsIgnoreCase(HttpHeaders.CONTENT_LENGTH)) {
+ continue;
+ }
+ if (hopByHopHeaders.containsHeader(headerName)) {
+ continue;
+ }
+
+ Enumeration headers = servletRequest.getHeaders(headerName);
+ while (headers.hasMoreElements()) {
+ String headerValue = (String) headers.nextElement();
+ // in case the proxy host is running multiple virtual servers,
rewrite the Host header to guarantee we get content from the correct virtual
server
+ if (headerName.equalsIgnoreCase(HttpHeaders.HOST)) {
+ headerValue = host.getHostName();
+ if (host.getPort() != -1) {
+ headerValue += ":" + host.getPort();
+ }
+ } else if (headerName.equalsIgnoreCase(SM.COOKIE)) {
+ headerValue = getRealCookie(headerValue);
+ }
+ proxyRequest.addHeader(headerName, headerValue);
+ }
+ }
+ }
+
+ private void setXForwardedForHeader(HttpServletRequest servletRequest,
HttpRequest proxyRequest) {
+ if (doForwardIP) {
+ String newHeader = servletRequest.getRemoteAddr();
+ String existingHeader =
servletRequest.getHeader("X-Forwarded-For");
+ if (existingHeader != null) {
+ newHeader = existingHeader + ", " + newHeader;
+ }
+ proxyRequest.setHeader("X-Forwarded-For", newHeader);
+ }
+ }
+
+ protected void copyResponseHeaders(HttpResponse proxyResponse,
HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
+ for (Header header : proxyResponse.getAllHeaders()) {
+ if (hopByHopHeaders.containsHeader(header.getName())) {
+ continue;
+ }
+ if (header.getName().equalsIgnoreCase(SM.SET_COOKIE) ||
header.getName().equalsIgnoreCase(SM.SET_COOKIE2)) {
+ copyProxyCookie(servletRequest, servletResponse, header);
+ } else {
+ servletResponse.setHeader(header.getName(), header.getValue());
+ }
+ }
+ }
+
+ /**
+ * Copy cookie from the proxy to the servlet client.
+ * Replaces cookie path to local path and renames cookie to avoid
collisions.
+ */
+ protected void copyProxyCookie(HttpServletRequest servletRequest,
+ HttpServletResponse servletResponse, Header
header) {
+ List<HttpCookie> cookies = HttpCookie.parse(header.getValue());
+ String path = servletRequest.getContextPath(); // path starts with /
or is empty string
+ path += servletRequest.getServletPath(); // servlet path starts with /
or is empty string
+
+ for (HttpCookie cookie : cookies) {
+ //set cookie name prefixed w/ a proxy value so it won't collide w/
other cookies
+ String proxyCookieName = getCookieNamePrefix() + cookie.getName();
+ Cookie servletCookie = new Cookie(proxyCookieName,
cookie.getValue());
+ servletCookie.setComment(cookie.getComment());
+ servletCookie.setMaxAge((int) cookie.getMaxAge());
+ servletCookie.setPath(path); //set to the path of the proxy servlet
+ // don't set cookie domain
+ servletCookie.setSecure(cookie.getSecure());
+ servletCookie.setVersion(cookie.getVersion());
+ servletResponse.addCookie(servletCookie);
+ }
+ }
+
+ /**
+ * Take any client cookies that were originally from the proxy and prepare
them to send to the
+ * proxy. This relies on cookie headers being set correctly according to
RFC 6265 Sec 5.4.
+ * This also blocks any local cookies from being sent to the proxy.
+ */
+ protected String getRealCookie(String cookieValue) {
+ StringBuilder escapedCookie = new StringBuilder();
+ String cookies[] = cookieValue.split("; ");
+ for (String cookie : cookies) {
+ String cookieSplit[] = cookie.split("=");
+ if (cookieSplit.length == 2) {
+ String cookieName = cookieSplit[0];
+ if (cookieName.startsWith(getCookieNamePrefix())) {
+ cookieName =
cookieName.substring(getCookieNamePrefix().length());
+ if (escapedCookie.length() > 0) {
+ escapedCookie.append("; ");
+ }
+
escapedCookie.append(cookieName).append("=").append(cookieSplit[1]);
+ }
+ }
+
+ cookieValue = escapedCookie.toString();
+ }
+ return cookieValue;
+ }
+
+ /**
+ * The string prefixing rewritten cookies.
+ */
+ protected String getCookieNamePrefix() {
+ return "!Proxy!" + getServletConfig().getServletName();
+ }
+
+ /**
+ * Copy response body data (the entity) from the proxy to the servlet
client.
+ */
+ protected void copyResponseEntity(HttpResponse proxyResponse,
HttpServletResponse servletResponse) throws IOException {
+ HttpEntity entity = proxyResponse.getEntity();
+ if (entity != null) {
+ OutputStream servletOutputStream =
servletResponse.getOutputStream();
+ entity.writeTo(servletOutputStream);
+ }
+ }
+
+ /**
+ * Reads the request URI from {@code servletRequest} and rewrites it,
considering targetUri.
+ * It's used to make the new request.
+ */
+ protected String rewriteUrlFromRequest(HttpServletRequest servletRequest,
String location) {
+ StringBuilder uri = new StringBuilder(500);
+ uri.append(location);
+ // Handle the path given to the servlet
+ if (servletRequest.getPathInfo() != null) {//ex: /my/path.html
+ uri.append(encodeUriQuery(servletRequest.getPathInfo()));
+ }
+ // Handle the query string & fragment
+ String queryString = servletRequest.getQueryString();//ex:(following
'?'): name=value&foo=bar#fragment
+ String fragment = null;
+ //split off fragment from queryString, updating queryString if found
+ if (queryString != null) {
+ int fragIdx = queryString.indexOf('#');
+ if (fragIdx >= 0) {
+ fragment = queryString.substring(fragIdx + 1);
+ queryString = queryString.substring(0, fragIdx);
+ }
+ }
+
+ queryString = rewriteQueryStringFromRequest(servletRequest,
queryString);
+ if (queryString != null && queryString.length() > 0) {
+ uri.append('?');
+ uri.append(encodeUriQuery(queryString));
+ }
+
+ if (doSendUrlFragment && fragment != null) {
+ uri.append('#');
+ uri.append(encodeUriQuery(fragment));
+ }
+ return uri.toString();
+ }
+
+ protected String rewriteQueryStringFromRequest(HttpServletRequest
servletRequest, String queryString) {
+ return queryString;
+ }
+
+ /**
+ * For a redirect response from the target server, this translates {@code
theUrl} to redirect to
+ * and translates it to one the original client can use.
+ */
+ protected String rewriteUrlFromResponse(HttpServletRequest servletRequest,
String theUrl, String location) {
+ if (theUrl.startsWith(location)) {
+ String curUrl = servletRequest.getRequestURL().toString(); // no
query
+ String pathInfo = servletRequest.getPathInfo();
+ if (pathInfo != null) {
+ assert curUrl.endsWith(pathInfo);
+ curUrl = curUrl.substring(0, curUrl.length() -
pathInfo.length()); // take pathInfo off
+ }
+ theUrl = curUrl + theUrl.substring(location.length());
+ }
+ return theUrl;
+ }
+
+ /**
+ * Encodes characters in the query or fragment part of the URI.
+ *
+ * <p>Unfortunately, an incoming URI sometimes has characters disallowed
by the spec. HttpClient
+ * insists that the outgoing proxied request has a valid URI because it
uses Java's {@link URI}.
+ * To be more forgiving, we must escape the problematic characters. See
the URI class for the
+ * spec.</p>
+ *
+ * @param in The {@link CharSequence} to encode.
+ */
+ protected static CharSequence encodeUriQuery(CharSequence in) {
+ //Note that I can't simply use URI.java to encode because it will
escape pre-existing escaped things.
+ StringBuilder outBuf = null;
+ Formatter formatter = null;
+ for (int i = 0; i < in.length(); i++) {
+ char c = in.charAt(i);
+ boolean escape = true;
+ if (c < 128) {
+ if (asciiQueryChars.get((int) c)) {
+ escape = false;
+ }
+ } else if (!Character.isISOControl(c) &&
!Character.isSpaceChar(c)) {//not-ascii
+ escape = false;
+ }
+ if (!escape) {
+ if (outBuf != null)
+ outBuf.append(c);
+ } else {
+ //escape
+ if (outBuf == null) {
+ outBuf = new StringBuilder(in.length() + 5 * 3);
+ outBuf.append(in, 0, i);
+ formatter = new Formatter(outBuf);
+ }
+ //leading %, 0 padded, width 2, capital hex
+ formatter.format("%%%02X", (int) c);//TODO
+ }
+ }
+ return outBuf != null ? outBuf : in;
+ }
+
+ protected static final BitSet asciiQueryChars;
+
+ static {
+ char[] c_unreserved = "_-!.~'()*".toCharArray();//plus alphanum
+ char[] c_punct = ",;:$&+=".toCharArray();
+ char[] c_reserved = "?/[]@".toCharArray();//plus punct
+
+ asciiQueryChars = new BitSet(128);
+ for (char c = 'a'; c <= 'z'; c++) asciiQueryChars.set((int) c);
+ for (char c = 'A'; c <= 'Z'; c++) asciiQueryChars.set((int) c);
+ for (char c = '0'; c <= '9'; c++) asciiQueryChars.set((int) c);
+ for (char c : c_unreserved) asciiQueryChars.set((int) c);
+ for (char c : c_punct) asciiQueryChars.set((int) c);
+ for (char c : c_reserved) asciiQueryChars.set((int) c);
+
+ asciiQueryChars.set((int) '%');//leave existing percent escapes in
place
+ }
+
+}
diff --git
a/http/src/main/java/org/apache/karaf/http/core/internal/osgi/Activator.java
b/http/src/main/java/org/apache/karaf/http/core/internal/osgi/Activator.java
index 9bff3b4b1a..8c68b03120 100644
--- a/http/src/main/java/org/apache/karaf/http/core/internal/osgi/Activator.java
+++ b/http/src/main/java/org/apache/karaf/http/core/internal/osgi/Activator.java
@@ -16,24 +16,41 @@
*/
package org.apache.karaf.http.core.internal.osgi;
+import org.apache.karaf.http.core.ProxyService;
import org.apache.karaf.http.core.ServletService;
import org.apache.karaf.http.core.internal.HttpMBeanImpl;
+import org.apache.karaf.http.core.internal.ProxyServiceImpl;
import org.apache.karaf.http.core.internal.ServletEventHandler;
import org.apache.karaf.http.core.internal.ServletServiceImpl;
import org.apache.karaf.util.tracker.BaseActivator;
import org.apache.karaf.util.tracker.annotation.ProvideService;
+import org.apache.karaf.util.tracker.annotation.RequireService;
import org.apache.karaf.util.tracker.annotation.Services;
import org.ops4j.pax.web.service.spi.ServletListener;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleListener;
+import org.osgi.service.http.HttpService;
-@Services(provides = @ProvideService(ServletService.class))
+@Services(
+ requires = {
+ @RequireService(HttpService.class)
+ },
+ provides = {
+ @ProvideService(ServletService.class),
+ @ProvideService(ProxyService.class)
+ }
+)
public class Activator extends BaseActivator {
private BundleListener listener;
@Override
protected void doStart() throws Exception {
+ HttpService httpService = getTrackedService(HttpService.class);
+ if (httpService == null) {
+ return;
+ }
+
final ServletEventHandler servletEventHandler = new
ServletEventHandler();
register(ServletListener.class, servletEventHandler);
@@ -49,13 +66,19 @@ protected void doStart() throws Exception {
};
bundleContext.addBundleListener(listener);
- HttpMBeanImpl httpMBean = new HttpMBeanImpl(servletService);
+ ProxyServiceImpl proxyService = new ProxyServiceImpl(httpService);
+ register(ProxyService.class, proxyService);
+
+ HttpMBeanImpl httpMBean = new HttpMBeanImpl(servletService,
proxyService);
registerMBean(httpMBean, "type=http");
}
@Override
protected void doStop() {
- bundleContext.removeBundleListener(listener);
+ if (listener != null) {
+ bundleContext.removeBundleListener(listener);
+ listener = null;
+ }
super.doStop();
}
}
diff --git
a/http/src/test/java/org/apache/karaf/http/core/internal/HttpMBeanImplTest.java
b/http/src/test/java/org/apache/karaf/http/core/internal/HttpMBeanImplTest.java
index cb2f8fd56e..91d13f9585 100644
---
a/http/src/test/java/org/apache/karaf/http/core/internal/HttpMBeanImplTest.java
+++
b/http/src/test/java/org/apache/karaf/http/core/internal/HttpMBeanImplTest.java
@@ -28,7 +28,7 @@
@Test
public void testRegisterMBean() throws Exception {
- HttpMBeanImpl httpMBean = new HttpMBeanImpl(new ServletServiceImpl(new
ServletEventHandler()));
+ HttpMBeanImpl httpMBean = new HttpMBeanImpl(new ServletServiceImpl(new
ServletEventHandler()), new ProxyServiceImpl(null));
MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
mbeanServer.registerMBean(httpMBean, new
ObjectName("org.apache.karaf:type=http,name=root"));
diff --git a/manual/src/main/asciidoc/user-guide/webcontainer.adoc
b/manual/src/main/asciidoc/user-guide/webcontainer.adoc
index 53f4432f24..e743756712 100644
--- a/manual/src/main/asciidoc/user-guide/webcontainer.adoc
+++ b/manual/src/main/asciidoc/user-guide/webcontainer.adoc
@@ -229,6 +229,13 @@ karaf@root()> bundle:install -s
"webbundle:http://tomcat.apache.org/tomcat-7.0-d
You can note the `webbundle` prefix, and the `Bundle-SymbolicName` and
`Web-ContextPath` headers on the URL.
+====== HTTP proxy
+
+Apache Karaf provides a HTTP proxy service. It allows you to proxy any HTTP
URLs within Karaf. It allows you to expose
+remote web applications in Karaf.
+
+You can use the Karaf `ProxyService` programmatically, or via the
corresponding shell commands and MBeans.
+
===== Commands
====== `http:list`
@@ -287,6 +294,36 @@ For instance, to start the Apache Karaf manual web
application:
karaf@root()> web:start 111
----
+====== `http:proxies`
+
+The `http:proxies` command list the configured HTTP proxies:
+
+----
+karaf@root()> http:proxies
+karaf@root()> http:proxies
+URL │ ProxyTo
+────────────┼─────────────────────────────────────
+/webconsole │ http://localhost:8181/system/console
+----
+
+===== `http:proxy-add`
+
+The `http:proxy-add` registers a new HTTP proxy. For instance, you can proxy
the Karaf WebConsole on another URL of your choice using:
+
+----
+karaf@root()> http:proxy-add /webconsole http://localhost:8181/system/console
+----
+
+Karaf HTTP Proxy can proxy any URL, like a backend running on Docker or a
remote URL.
+
+===== `http:proxy-remove`
+
+The `http:proxy-remove` removes an existing HTTP proxy:
+
+----
+karaf@root()> http:proxy-remove /webconsole
+----
+
===== JMX HttpMBean
On the JMX layer, you have a MBean dedicated to the manipulation of the
Servlets: the HttpMBean.
@@ -303,6 +340,17 @@ The `Servlets` attribute provides a tabular data providing
the list of deployed
* `State` is the current Servlet state (`Deployed` or `Undeployed`).
* `URL` is the URL of the Servlet (the Servlet context path).
+The `Proxies` attribute provides a tabular data providing the list of HTTP
proxies including:
+
+* `URL` is the proxy URL.
+* `proxyTo` is the proxy target.
+* `prefix` is optional proxy prefix.
+
+====== Operations
+
+* `addProxy(url, proxyTo, prefix)` registers a new HTTP proxy.
+* `removeProxy(url)` removes an existing HTTP proxy.
+
===== JMX WebMBean
On the JMX layer, you have a MBean dedicated to the manipulation of the Web
Applications: the WebMBean.
diff --git a/webconsole/http/pom.xml b/webconsole/http/pom.xml
index e1d6eaeec0..41077ff43c 100644
--- a/webconsole/http/pom.xml
+++ b/webconsole/http/pom.xml
@@ -73,6 +73,11 @@
<artifactId>pax-web-spi</artifactId>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.karaf.http</groupId>
+ <artifactId>org.apache.karaf.http.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
<dependency>
<groupId>org.apache.karaf.features</groupId>
<artifactId>org.apache.karaf.features.core</artifactId>
diff --git
a/webconsole/http/src/main/java/org/apache/karaf/webconsole/http/Activator.java
b/webconsole/http/src/main/java/org/apache/karaf/webconsole/http/Activator.java
index 74744d074a..e6a115e9dc 100644
---
a/webconsole/http/src/main/java/org/apache/karaf/webconsole/http/Activator.java
+++
b/webconsole/http/src/main/java/org/apache/karaf/webconsole/http/Activator.java
@@ -20,12 +20,14 @@
import java.util.Dictionary;
import java.util.Hashtable;
+import org.apache.karaf.http.core.ProxyService;
import org.apache.karaf.util.tracker.BaseActivator;
+import org.apache.karaf.util.tracker.annotation.RequireService;
import org.apache.karaf.util.tracker.annotation.Services;
import org.ops4j.pax.web.service.spi.ServletListener;
import org.ops4j.pax.web.service.spi.WebListener;
-@Services
+@Services(requires = @RequireService(ProxyService.class))
public class Activator extends BaseActivator {
private HttpPlugin httpPlugin;
@@ -34,6 +36,11 @@
@Override
protected void doStart() throws Exception {
+ ProxyService proxyService = getTrackedService(ProxyService.class);
+ if (proxyService == null) {
+ return;
+ }
+
eaHandler = new ServletEventHandler();
eaHandler.setBundleContext(bundleContext);
eaHandler.init();
@@ -48,6 +55,7 @@ protected void doStart() throws Exception {
httpPlugin.setBundleContext(bundleContext);
httpPlugin.setServletEventHandler(eaHandler);
httpPlugin.setWebEventHandler(webEaHandler);
+ httpPlugin.setProxyService(proxyService);
httpPlugin.start();
Dictionary<String, String> props = new Hashtable<>();
diff --git
a/webconsole/http/src/main/java/org/apache/karaf/webconsole/http/HttpPlugin.java
b/webconsole/http/src/main/java/org/apache/karaf/webconsole/http/HttpPlugin.java
index 86921578e6..433cbade74 100644
---
a/webconsole/http/src/main/java/org/apache/karaf/webconsole/http/HttpPlugin.java
+++
b/webconsole/http/src/main/java/org/apache/karaf/webconsole/http/HttpPlugin.java
@@ -34,6 +34,7 @@
import org.apache.felix.utils.json.JSONWriter;
import org.apache.felix.webconsole.AbstractWebConsolePlugin;
import org.apache.felix.webconsole.WebConsoleConstants;
+import org.apache.karaf.http.core.ProxyService;
import org.ops4j.pax.web.service.spi.ServletEvent;
import org.ops4j.pax.web.service.spi.WebEvent;
import org.osgi.framework.Bundle;
@@ -56,6 +57,7 @@
private ServletEventHandler servletEventHandler;
private WebEventHandler webEventHandler;
private BundleContext bundleContext;
+ private ProxyService proxyService;
@Override
protected boolean isHtmlRequest(HttpServletRequest request) {
@@ -145,6 +147,7 @@ private void writeJSON(final PrintWriter pw) throws
IOException {
final List<ServletDetails> servlets = this.getServletDetails();
final List<WebDetail> web = this.getWebDetails();
+ final Map<String, String> proxies = proxyService.getProxies();
final String statusLine = this.getStatusLine(servlets, web);
final JSONWriter jw = new JSONWriter(pw);
@@ -193,6 +196,18 @@ private void writeJSON(final PrintWriter pw) throws
IOException {
}
jw.endArray();
+ jw.key("proxy");
+ jw.array();
+ for (String proxy : proxies.keySet()) {
+ jw.object();
+ jw.key("url");
+ jw.value(proxy);
+ jw.key("proxyTo");
+ jw.value(proxies.get(proxy));
+ jw.endObject();
+ }
+ jw.endArray();
+
jw.endObject();
}
@@ -312,4 +327,8 @@ public void setBundleContext(BundleContext bundleContext) {
this.bundleContext = bundleContext;
}
+ public void setProxyService(ProxyService proxyService) {
+ this.proxyService = proxyService;
+ }
+
}
diff --git a/webconsole/http/src/main/resources/res/ui/http-contexts.js
b/webconsole/http/src/main/resources/res/ui/http-contexts.js
index 79d6220709..d1eae05b86 100644
--- a/webconsole/http/src/main/resources/res/ui/http-contexts.js
+++ b/webconsole/http/src/main/resources/res/ui/http-contexts.js
@@ -26,6 +26,7 @@ function renderView() {
renderStatusLine();
renderTable( "HTTP Contexts", "context_table", ["ID", "Servlet", "Name",
"State", "Alias", "urls"] );
renderTable( "Web Contexts", "webctxt_table", ["ID", "BundleState", "Web
Context", "State"] );
+ renderTable(" HTTP Proxies", "proxy_table", ["URL", "ProxyTo"]);
renderStatusLine();
}
----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
[email protected]
> Add HttpRedirect/Proxy service with http:redirect/proxy command & MBean
> -----------------------------------------------------------------------
>
> Key: KARAF-5614
> URL: https://issues.apache.org/jira/browse/KARAF-5614
> Project: Karaf
> Issue Type: New Feature
> Components: karaf-webcontainer
> Reporter: Jean-Baptiste Onofré
> Assignee: Jean-Baptiste Onofré
> Priority: Major
> Fix For: 4.2.0
>
>
> In Decanter 2.0.0, for the Kibana 6.x integration, I registered a
> JettyProxyServlet to "façade" and proxy HTTP requests from Karaf {{/kibana}}
> to the actual Kibana location {{http://localhost:5061/app/kibana}}.
> This approach could be interesting generally speaking for end users. Further,
> with Cellar or Karaf Docker, it would be even more useful.
--
This message was sent by Atlassian JIRA
(v7.6.3#76005)