Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/org.eclipse.tcf.core/src/org/eclipse/tcf/core/ChannelHTTP.java')
-rw-r--r--plugins/org.eclipse.tcf.core/src/org/eclipse/tcf/core/ChannelHTTP.java308
1 files changed, 308 insertions, 0 deletions
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;
+ }
+}

Back to the top