aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEugene Tarassov2018-08-22 15:24:58 -0400
committerEugene Tarassov2018-08-22 15:51:11 -0400
commit3ed69ee3c25ba7b0fcdd6331916f620d3cc7df29 (patch)
tree3f69e5e3c5e81c21b717797aab1a769d51a78bce
parent618694e83ee107c878b5a244b2c6225dcf563860 (diff)
downloadorg.eclipse.tcf-3ed69ee3c25ba7b0fcdd6331916f620d3cc7df29.tar.gz
org.eclipse.tcf-3ed69ee3c25ba7b0fcdd6331916f620d3cc7df29.tar.xz
org.eclipse.tcf-3ed69ee3c25ba7b0fcdd6331916f620d3cc7df29.zip
TCF Core: added support for TCF over HTTP
-rw-r--r--plugins/org.eclipse.tcf.core/META-INF/MANIFEST.MF3
-rw-r--r--plugins/org.eclipse.tcf.core/pom.xml2
-rw-r--r--plugins/org.eclipse.tcf.core/src/org/eclipse/tcf/core/AbstractChannel.java8
-rw-r--r--plugins/org.eclipse.tcf.core/src/org/eclipse/tcf/core/ChannelHTTP.java308
-rw-r--r--plugins/org.eclipse.tcf.core/src/org/eclipse/tcf/internal/core/TransportManager.java19
-rw-r--r--plugins/org.eclipse.tcf.core/src/org/eclipse/tcf/protocol/JSON.java3
-rw-r--r--plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestEcho.java10
-rw-r--r--plugins/org.eclipse.tcf/src/org/eclipse/tcf/ssl/TCFSecurityManager.java4
8 files changed, 346 insertions, 11 deletions
diff --git a/plugins/org.eclipse.tcf.core/META-INF/MANIFEST.MF b/plugins/org.eclipse.tcf.core/META-INF/MANIFEST.MF
index 206a11018..e8346daa0 100644
--- a/plugins/org.eclipse.tcf.core/META-INF/MANIFEST.MF
+++ b/plugins/org.eclipse.tcf.core/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2
Bundle-Name: %pluginName
Bundle-Vendor: %providerName
Bundle-SymbolicName: org.eclipse.tcf.core;singleton:=true
-Bundle-Version: 1.5.0.qualifier
+Bundle-Version: 1.6.0.qualifier
Bundle-Localization: plugin
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Bundle-ActivationPolicy: lazy
@@ -20,3 +20,4 @@ Export-Package: org.eclipse.tcf.core;version="1.5.0",
org.eclipse.tcf.protocol;version="1.5.0",
org.eclipse.tcf.services;version="1.5.0",
org.eclipse.tcf.util;version="1.5.0"
+Automatic-Module-Name: org.eclipse.tcf.core
diff --git a/plugins/org.eclipse.tcf.core/pom.xml b/plugins/org.eclipse.tcf.core/pom.xml
index a5600d1c4..79c6949aa 100644
--- a/plugins/org.eclipse.tcf.core/pom.xml
+++ b/plugins/org.eclipse.tcf.core/pom.xml
@@ -11,7 +11,7 @@
<relativePath>../../admin/pom-build.xml</relativePath>
</parent>
- <version>1.5.0-SNAPSHOT</version>
+ <version>1.6.0-SNAPSHOT</version>
<artifactId>org.eclipse.tcf.core</artifactId>
<packaging>eclipse-plugin</packaging>
</project>
diff --git a/plugins/org.eclipse.tcf.core/src/org/eclipse/tcf/core/AbstractChannel.java b/plugins/org.eclipse.tcf.core/src/org/eclipse/tcf/core/AbstractChannel.java
index d54a9d0c8..8ee8eafff 100644
--- a/plugins/org.eclipse.tcf.core/src/org/eclipse/tcf/core/AbstractChannel.java
+++ b/plugins/org.eclipse.tcf.core/src/org/eclipse/tcf/core/AbstractChannel.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2007, 2015 Wind River Systems, Inc. and others.
+ * Copyright (c) 2007-2018 Wind River Systems, Inc. and others.
* 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
@@ -65,8 +65,9 @@ public abstract class AbstractChannel implements IChannel {
/**
* Represents a message sent through a channel between peers
+ * @since 1.6
*/
- private static class Message {
+ protected static class Message {
/**
* Type of message.
* "C" for Commands.
@@ -1053,9 +1054,10 @@ public abstract class AbstractChannel implements IChannel {
/**
* Handles the message received from the channel
* @param msg
+ * @since 1.6
*/
@SuppressWarnings("unchecked")
- private void handleInput(Message msg) {
+ protected void handleInput(Message msg) {
assert Protocol.isDispatchThread();
if (state == STATE_CLOSED) {
return;
diff --git a/plugins/org.eclipse.tcf.core/src/org/eclipse/tcf/core/ChannelHTTP.java b/plugins/org.eclipse.tcf.core/src/org/eclipse/tcf/core/ChannelHTTP.java
new file mode 100644
index 000000000..cb2502bf9
--- /dev/null
+++ b/plugins/org.eclipse.tcf.core/src/org/eclipse/tcf/core/ChannelHTTP.java
@@ -0,0 +1,308 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Xilinx, Inc. and others.
+ * 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
+ *
+ * Contributors:
+ * Xilinx - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.core;
+
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Map;
+import java.util.UUID;
+
+import org.eclipse.tcf.internal.core.Token;
+import org.eclipse.tcf.protocol.IPeer;
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.protocol.JSON;
+import org.eclipse.tcf.protocol.Protocol;
+import org.eclipse.tcf.services.ILocator;
+
+/**
+ * ChannelHTTP implements TCF channel over HTTP protocol.
+ * @since 1.6
+ */
+public class ChannelHTTP extends AbstractChannel {
+
+ private static int id_cnt = 0;
+ private final String id = UUID.randomUUID().toString() +
+ "-" + Integer.toUnsignedString(id_cnt++, 16);
+
+ private final String host;
+ private final int port;
+
+ private boolean stopped;
+
+ private byte[] wr_buf;
+ private int wr_cnt;
+
+ public ChannelHTTP(IPeer remote_peer, String host, int port) {
+ super(remote_peer);
+ this.host = host;
+ this.port = port;
+ Protocol.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ start();
+ }
+ });
+ }
+
+ @Override
+ public boolean isZeroCopySupported() {
+ return false;
+ }
+
+ @Override
+ protected int read() throws IOException {
+ String nm = "http://" + host + ":" + port + "/tcf/sse";
+ URL url = new URL(nm);
+ while (!stopped) {
+ try {
+ HttpURLConnection con = (HttpURLConnection)url.openConnection();
+ con.setRequestProperty("Content-Type", "text/event-stream");
+ con.setRequestProperty("X-Session-ID", id);
+ con.setRequestMethod("GET");
+ BufferedReader inp = new BufferedReader(new InputStreamReader(con.getInputStream(), "UTF-8"));
+ while (!stopped) {
+ String s = inp.readLine();
+ if (s == null) break;
+ Protocol.invokeLater(new Runnable() {
+ IToken cmd;
+ @Override
+ public void run() {
+ if (cmd != null) return;
+ if (getState() != STATE_OPEN) return;
+ ILocator l = getRemoteService(ILocator.class);
+ cmd = l.sync(new ILocator.DoneSync() {
+ @Override
+ public void doneSync(IToken token) {
+ assert cmd == token;
+ cmd = null;
+ }
+ });
+ }
+ });
+ }
+ inp.close();
+ }
+ catch (Throwable x) {
+ if (x instanceof FileNotFoundException) throw new IOException("Page not found: " + x.getMessage());
+ if (x instanceof IOException) throw (IOException)x;
+ throw new IOException(x);
+ }
+ }
+ return -1;
+ }
+
+ @Override
+ protected void write(int n) throws IOException {
+ if (n < 0) {
+ if (n == EOM && wr_cnt > 0) {
+ try {
+ int i = 0;
+ char type = (char)wr_buf[i++];
+ while (i < wr_cnt && wr_buf[i] == 0) i++;
+ switch (type) {
+ case 'C':
+ sendCommand(i);
+ break;
+ case 'E':
+ sendEvent(i);
+ break;
+ }
+ }
+ catch (Throwable x) {
+ if (x instanceof FileNotFoundException) throw new IOException("Page not found: " + x.getMessage());
+ if (x instanceof IOException) throw (IOException)x;
+ throw new IOException(x);
+ }
+ finally {
+ wr_cnt = 0;
+ }
+ }
+ return;
+ }
+ if (wr_buf == null) {
+ wr_buf = new byte[0x1000];
+ }
+ else if (wr_cnt >= wr_buf.length) {
+ byte[] t = new byte[wr_cnt * 2];
+ System.arraycopy(wr_buf, 0, t, 0, wr_cnt);
+ wr_buf = t;
+ }
+ wr_buf[wr_cnt++] = (byte)n;
+ }
+
+ @Override
+ protected void flush() throws IOException {
+ }
+
+ @Override
+ protected void stop() throws IOException {
+ stopped = true;
+ }
+
+ private char toHexDigit(int n) {
+ if (n >= 0 && n <= 9) return (char)('0' + n);
+ if (n >= 10 && n <= 15) return (char)('A' + n - 10);
+ return ' ';
+ }
+
+ private String getArgs(int i) throws Exception {
+ StringBuffer args = new StringBuffer();
+ while (i < wr_cnt) {
+ if (args.length() > 0) args.append('&');
+ while (wr_buf[i] != 0) {
+ char ch = (char)(wr_buf[i++] & 0xff);
+ if (ch <= ' ' || ch == '%' || ch == '#' || ch == '&' || ch >= (char)127) {
+ args.append('%');
+ args.append(toHexDigit(((int)ch >> 4) & 0xf));
+ args.append(toHexDigit((int)ch & 0xf));
+ }
+ else {
+ args.append(ch);
+ }
+ }
+ while (i < wr_buf.length && wr_buf[i] == 0) i++;
+ }
+ return args.toString();
+ }
+
+ private void sendCommand(int i) throws Exception {
+ int p = i;
+ while (wr_buf[i] != 0) i++;
+ byte[] t = new byte[i - p];
+ System.arraycopy(wr_buf, p, t, 0, t.length);
+ Token token = new Token(t);
+ while (wr_buf[i] == 0) i++;
+
+ p = i;
+ while (wr_buf[i] != 0) i++;
+ String service = new String(wr_buf, p, i - p, "UTF-8");
+ while (wr_buf[i] == 0) i++;
+
+ p = i;
+ while (wr_buf[i] != 0) i++;
+ String command = new String(wr_buf, p, i - p, "UTF-8");
+ while (i < wr_buf.length && wr_buf[i] == 0) i++;
+
+ sendRequest(token, service, command, getArgs(i));
+ }
+
+ private void sendEvent(int i) throws Exception {
+ int p = i;
+ while (wr_buf[i] != 0) i++;
+ String service = new String(wr_buf, p, i - p, "UTF-8");
+ while (wr_buf[i] == 0) i++;
+
+ p = i;
+ while (wr_buf[i] != 0) i++;
+ String command = new String(wr_buf, p, i - p, "UTF-8");
+ while (i < wr_buf.length && wr_buf[i] == 0) i++;
+
+ sendRequest(null, service, command, getArgs(i));
+ }
+
+ @SuppressWarnings("unchecked")
+ private void sendRequest(final Token token, String service, String command, String args) throws Exception {
+ String nm = token != null ? "/tcf/c/" + token + "/" : "/tcf/e/";
+ nm = "http://" + host + ":" + port + nm + service + "/" + command;
+ if (args != null && args.length() > 0) nm += "?" + args;
+ URL url = new URL(nm);
+ HttpURLConnection con = (HttpURLConnection)url.openConnection();
+ con.setRequestProperty("Content-Type", "application/json");
+ con.setRequestProperty("X-Session-ID", id);
+ con.setRequestMethod("GET");
+ InputStream inp = con.getInputStream();
+ final byte[] buf = new byte[con.getHeaderFieldInt("Content-Length", 0)];
+ int pos = 0;
+ while (pos < buf.length) {
+ int rd = inp.read(buf, pos, buf.length - pos);
+ if (rd < 0) break;
+ pos += rd;
+ }
+ while (inp.read() > 0) {}
+ inp.close();
+ Protocol.invokeLater(new Runnable() {
+ public void run() {
+ try {
+ Object obj = JSON.parseOne(buf);
+ if (obj instanceof Collection) {
+ for (Object x : (Collection<Object>)obj) handleReply(x);
+ }
+ else {
+ throw new Exception("Invalid HTTP reply");
+ }
+ }
+ catch (Exception x) {
+ Protocol.log("Cannot execute HTTP request", x);
+ }
+ }
+ });
+ }
+
+ @SuppressWarnings("rawtypes")
+ private void handleReply(Object obj) throws Exception {
+ if (obj instanceof Map) {
+ Map m = (Map)obj;
+ String error = (String)m.get("Error");
+ if (error != null) throw new Exception(error);
+ String type = (String)m.get("Type");
+ final Message msg = new Message(type.charAt(0));
+ switch (msg.type) {
+ case 'C':
+ msg.token = new Token(((String)m.get("Token")).getBytes("UTF-8"));
+ msg.service = (String)m.get("Service");
+ msg.name = (String)m.get("Command");
+ msg.data = readArgs(m.get("Args"));
+ break;
+ case 'P':
+ case 'R':
+ case 'N':
+ msg.token = new Token(((String)m.get("Token")).getBytes("UTF-8"));
+ msg.data = readArgs(m.get("Args"));
+ break;
+ case 'E':
+ msg.service = (String)m.get("Service");
+ msg.name = (String)m.get("Event");
+ msg.data = readArgs(m.get("Args"));
+ if (msg.service.equals(ILocator.NAME) &&
+ msg.name.equals("Hello") &&
+ getState() != STATE_OPENING) return;
+ break;
+ case 'F':
+ msg.data = readArgs(m.get("Args"));
+ break;
+ default:
+ throw new Exception("Invalid HTTP reply");
+ }
+ handleInput(msg);
+ }
+ else {
+ throw new Exception("Invalid HTTP reply");
+ }
+ }
+
+ @SuppressWarnings("rawtypes")
+ private byte[] readArgs(Object obj) throws Exception {
+ byte[] res = null;
+ if (obj instanceof Collection) {
+ res = JSON.toJSONSequence(((Collection)obj).toArray());
+ }
+ else if (obj != null) {
+ throw new Exception("Invalid HTTP reply");
+ }
+ return res;
+ }
+}
diff --git a/plugins/org.eclipse.tcf.core/src/org/eclipse/tcf/internal/core/TransportManager.java b/plugins/org.eclipse.tcf.core/src/org/eclipse/tcf/internal/core/TransportManager.java
index 15d86db60..1a5e269cf 100644
--- a/plugins/org.eclipse.tcf.core/src/org/eclipse/tcf/internal/core/TransportManager.java
+++ b/plugins/org.eclipse.tcf.core/src/org/eclipse/tcf/internal/core/TransportManager.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2007, 2013 Wind River Systems, Inc. and others.
+ * Copyright (c) 2007-2018 Wind River Systems, Inc. and others.
* 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
@@ -18,6 +18,7 @@ import java.util.Map;
import java.util.Set;
import org.eclipse.tcf.core.AbstractChannel;
+import org.eclipse.tcf.core.ChannelHTTP;
import org.eclipse.tcf.core.ChannelPIPE;
import org.eclipse.tcf.core.ChannelTCP;
import org.eclipse.tcf.protocol.IChannel;
@@ -103,6 +104,22 @@ public class TransportManager {
addTransportProvider(new ITransportProvider() {
public String getName() {
+ return "HTTP";
+ }
+
+ public IChannel openChannel(IPeer peer) {
+ assert getName().equals(peer.getTransportName());
+ Map<String,String> attrs = peer.getAttributes();
+ String host = attrs.get(IPeer.ATTR_IP_HOST);
+ String port = attrs.get(IPeer.ATTR_IP_PORT);
+ if (host == null) throw new IllegalArgumentException("No host name");
+ return new ChannelHTTP(peer, host, parsePort(port));
+ }
+ });
+
+ addTransportProvider(new ITransportProvider() {
+
+ public String getName() {
return "Loop";
}
diff --git a/plugins/org.eclipse.tcf.core/src/org/eclipse/tcf/protocol/JSON.java b/plugins/org.eclipse.tcf.core/src/org/eclipse/tcf/protocol/JSON.java
index dbb19bbe8..434ad9a49 100644
--- a/plugins/org.eclipse.tcf.core/src/org/eclipse/tcf/protocol/JSON.java
+++ b/plugins/org.eclipse.tcf.core/src/org/eclipse/tcf/protocol/JSON.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2007, 2013 Wind River Systems, Inc. and others.
+ * Copyright (c) 2007-2018 Wind River Systems, Inc. and others.
* 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
@@ -761,6 +761,7 @@ public final class JSON {
err_buf_cnt = 0;
read();
Object o = readNestedObject();
+ skipWS();
if (cur_ch >= 0) error();
return o;
}
diff --git a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestEcho.java b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestEcho.java
index 2d88b09b3..4e873a7cf 100644
--- a/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestEcho.java
+++ b/plugins/org.eclipse.tcf.debug/src/org/eclipse/tcf/internal/debug/tests/TestEcho.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2008, 2014 Wind River Systems, Inc. and others.
+ * Copyright (c) 2008-2018 Wind River Systems, Inc. and others.
* 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
@@ -75,7 +75,13 @@ class TestEcho implements ITCFTest, IDiagnostics.DoneEcho {
test_suite.done(this, error);
}
else if (!s.equals(b)) {
- test_suite.done(this, new Exception("Echo test failed: " + s + " != " + b));
+ int i = 0;
+ while (i < s.length() && i < b.length() && s.charAt(i) == b.charAt(i)) i++;
+ int cx = i < s.length() ? s.charAt(i) : '\0';
+ int cy = i < b.length() ? b.charAt(i) : '\0';
+ test_suite.done(this, new Exception("Echo test failed, offset " + i +
+ ", 0x" + Integer.toHexString(cx) + " != 0x" + Integer.toHexString(cy) +
+ ":\n" + s + "\n" + b));
}
else if (count < 0x400) {
sendMessage();
diff --git a/plugins/org.eclipse.tcf/src/org/eclipse/tcf/ssl/TCFSecurityManager.java b/plugins/org.eclipse.tcf/src/org/eclipse/tcf/ssl/TCFSecurityManager.java
index c18bc4178..d98201c9a 100644
--- a/plugins/org.eclipse.tcf/src/org/eclipse/tcf/ssl/TCFSecurityManager.java
+++ b/plugins/org.eclipse.tcf/src/org/eclipse/tcf/ssl/TCFSecurityManager.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2009, 2012 Wind River Systems, Inc. and others.
+ * Copyright (c) 2009-2018 Wind River Systems, Inc. and others.
* 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
@@ -205,7 +205,7 @@ public class TCFSecurityManager {
}
public void checkServerTrusted(X509Certificate[] chain, String auth_type) throws CertificateException {
- if ("RSA".equals(auth_type) && chain != null && chain.length == 1) { //$NON-NLS-1$
+ if (chain != null && chain.length == 1) { //$NON-NLS-1$
for (X509Certificate cert : getAcceptedIssuers()) {
if (cert.equals(chain[0])) return;
}