diff options
Diffstat (limited to 'plugins')
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; } |