org.chromium.sdk/src/org/chromium/sdk/internal/transport/Handshaker.java
changeset 2 e4420d2515f1
child 52 f577ea64429e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/transport/Handshaker.java	Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,142 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.transport;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.RunnableFuture;
+
+import org.chromium.sdk.internal.transport.Message.MalformedMessageException;
+
+/**
+ * Handshaker handles "handshake" part of communication. It may write and read whatever it needs
+ * before regular message-based communication has started.
+ */
+public interface Handshaker {
+  /**
+   * Performs handshake. This method is blocking. After it has successfully finished, input/output
+   * should be ready for normal message-based communication. In case method fails with IOException,
+   * input and output are returned in undefined state.
+   * @throws IOException if handshake process failed physically (input or output has unexpectedly
+   * closed) or logically (if unexpected message came from remote).
+   */
+  void perform(BufferedReader input, Writer output) throws IOException;
+
+  /**
+   * Implementation of handshake from Google Chrome Developer Tools Protocol. Used when we
+   * connect to browser.
+   */
+  Handshaker CHROMIUM = new Handshaker() {
+    public void perform(BufferedReader input, Writer output) throws IOException {
+      output.write(OUTGOING_MESSAGE);
+      output.flush();
+
+      // TODO(prybin): expose this as a parameter or get rid of this option if we don't need it.
+      final boolean ignoreUnexpectedResponses = false;
+
+      while (true) {
+        if (Thread.interrupted()) {
+          throw new IOException("Interrupted");
+        }
+        String line = input.readLine();
+        if (line == null) {
+          throw new IOException("Connection closed");
+        }
+        if (INCOMING_TEXT.equals(line)) {
+          break;
+        }
+        if (!ignoreUnexpectedResponses) {
+          throw new IOException("Unexpected handshake: " + line);
+        }
+      }
+    }
+
+    /**
+     * A handshake string to be sent by a browser on the connection start,
+     * specified by the protocol design doc (without trailing cr/lf).
+     */
+    private static final String INCOMING_TEXT = "ChromeDevToolsHandshake";
+
+    /**
+     * A handshake string that we send to a browser on the connection start,
+     * specified by the protocol design doc (including trailing cr/lf).
+     */
+    private static final String OUTGOING_MESSAGE = "ChromeDevToolsHandshake\r\n";
+  };
+
+  /**
+   * Stateful handshaker implementation for Standalone V8 protocol.
+   */
+  class StandaloneV8 implements Handshaker {
+    public interface RemoteInfo {
+      String getProtocolVersion();
+      String getV8VmVersion();
+      String getEmbeddingHostName();
+    }
+
+    public Future<RemoteInfo> getRemoteInfo() {
+      return runnableFuture;
+    }
+
+    private final RunnableFuture<RemoteInfo> runnableFuture =
+        new FutureTask<RemoteInfo>(new HandshakeTaks());
+
+    private BufferedReader input = null;
+
+    public void perform(BufferedReader input, Writer output) throws IOException {
+      this.input = input;
+      runnableFuture.run();
+
+      // Check for possible exceptions
+      try {
+        runnableFuture.get();
+      } catch (InterruptedException e) {
+        throw new RuntimeException(e);
+      } catch (ExecutionException e) {
+        throw new IOException("Failed to perform handshake", e);
+      }
+
+    }
+
+    class HandshakeTaks implements Callable<RemoteInfo> {
+      public RemoteInfo call() throws IOException {
+        final Message message;
+        try {
+          message = Message.fromBufferedReader(input);
+        } catch (MalformedMessageException e) {
+          throw new IOException("Unrecognized handshake message from remote", e);
+        }
+        if (message == null) {
+          throw new IOException("End of stream");
+        }
+        final String protocolVersion = message.getHeader("Protocol-Version", null);
+        if (protocolVersion == null) {
+          throw new IOException("Absent protocol version");
+        }
+        final String vmVersion = message.getHeader("V8-Version", null);
+        if (vmVersion == null) {
+          throw new IOException("Absent V8 VM version");
+        }
+        RemoteInfo remoteInfo = new RemoteInfo() {
+          public String getProtocolVersion() {
+            return protocolVersion;
+          }
+          public String getV8VmVersion() {
+            return vmVersion;
+          }
+          public String getEmbeddingHostName() {
+            return message.getHeader("Embedding-Host", null);
+          }
+        };
+        return remoteInfo;
+      }
+    }
+  }
+}