|
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
|
2 // Use of this source code is governed by a BSD-style license that can be |
|
3 // found in the LICENSE file. |
|
4 |
|
5 package org.chromium.sdk.internal.transport; |
|
6 |
|
7 import java.io.BufferedReader; |
|
8 import java.io.IOException; |
|
9 import java.io.Writer; |
|
10 import java.util.concurrent.Callable; |
|
11 import java.util.concurrent.ExecutionException; |
|
12 import java.util.concurrent.Future; |
|
13 import java.util.concurrent.FutureTask; |
|
14 import java.util.concurrent.RunnableFuture; |
|
15 |
|
16 import org.chromium.sdk.internal.transport.Message.MalformedMessageException; |
|
17 |
|
18 /** |
|
19 * Handshaker handles "handshake" part of communication. It may write and read whatever it needs |
|
20 * before regular message-based communication has started. |
|
21 */ |
|
22 public interface Handshaker { |
|
23 /** |
|
24 * Performs handshake. This method is blocking. After it has successfully finished, input/output |
|
25 * should be ready for normal message-based communication. In case method fails with IOException, |
|
26 * input and output are returned in undefined state. |
|
27 * @throws IOException if handshake process failed physically (input or output has unexpectedly |
|
28 * closed) or logically (if unexpected message came from remote). |
|
29 */ |
|
30 void perform(BufferedReader input, Writer output) throws IOException; |
|
31 |
|
32 /** |
|
33 * Implementation of handshake from Google Chrome Developer Tools Protocol. Used when we |
|
34 * connect to browser. |
|
35 */ |
|
36 Handshaker CHROMIUM = new Handshaker() { |
|
37 public void perform(BufferedReader input, Writer output) throws IOException { |
|
38 output.write(OUTGOING_MESSAGE); |
|
39 output.flush(); |
|
40 |
|
41 // TODO(prybin): expose this as a parameter or get rid of this option if we don't need it. |
|
42 final boolean ignoreUnexpectedResponses = false; |
|
43 |
|
44 while (true) { |
|
45 if (Thread.interrupted()) { |
|
46 throw new IOException("Interrupted"); |
|
47 } |
|
48 String line = input.readLine(); |
|
49 if (line == null) { |
|
50 throw new IOException("Connection closed"); |
|
51 } |
|
52 if (INCOMING_TEXT.equals(line)) { |
|
53 break; |
|
54 } |
|
55 if (!ignoreUnexpectedResponses) { |
|
56 throw new IOException("Unexpected handshake: " + line); |
|
57 } |
|
58 } |
|
59 } |
|
60 |
|
61 /** |
|
62 * A handshake string to be sent by a browser on the connection start, |
|
63 * specified by the protocol design doc (without trailing cr/lf). |
|
64 */ |
|
65 private static final String INCOMING_TEXT = "ChromeDevToolsHandshake"; |
|
66 |
|
67 /** |
|
68 * A handshake string that we send to a browser on the connection start, |
|
69 * specified by the protocol design doc (including trailing cr/lf). |
|
70 */ |
|
71 private static final String OUTGOING_MESSAGE = "ChromeDevToolsHandshake\r\n"; |
|
72 }; |
|
73 |
|
74 /** |
|
75 * Stateful handshaker implementation for Standalone V8 protocol. |
|
76 */ |
|
77 class StandaloneV8 implements Handshaker { |
|
78 public interface RemoteInfo { |
|
79 String getProtocolVersion(); |
|
80 String getV8VmVersion(); |
|
81 String getEmbeddingHostName(); |
|
82 } |
|
83 |
|
84 public Future<RemoteInfo> getRemoteInfo() { |
|
85 return runnableFuture; |
|
86 } |
|
87 |
|
88 private final RunnableFuture<RemoteInfo> runnableFuture = |
|
89 new FutureTask<RemoteInfo>(new HandshakeTaks()); |
|
90 |
|
91 private BufferedReader input = null; |
|
92 |
|
93 public void perform(BufferedReader input, Writer output) throws IOException { |
|
94 this.input = input; |
|
95 runnableFuture.run(); |
|
96 |
|
97 // Check for possible exceptions |
|
98 try { |
|
99 runnableFuture.get(); |
|
100 } catch (InterruptedException e) { |
|
101 throw new RuntimeException(e); |
|
102 } catch (ExecutionException e) { |
|
103 throw new IOException("Failed to perform handshake", e); |
|
104 } |
|
105 |
|
106 } |
|
107 |
|
108 class HandshakeTaks implements Callable<RemoteInfo> { |
|
109 public RemoteInfo call() throws IOException { |
|
110 final Message message; |
|
111 try { |
|
112 message = Message.fromBufferedReader(input); |
|
113 } catch (MalformedMessageException e) { |
|
114 throw new IOException("Unrecognized handshake message from remote", e); |
|
115 } |
|
116 if (message == null) { |
|
117 throw new IOException("End of stream"); |
|
118 } |
|
119 final String protocolVersion = message.getHeader("Protocol-Version", null); |
|
120 if (protocolVersion == null) { |
|
121 throw new IOException("Absent protocol version"); |
|
122 } |
|
123 final String vmVersion = message.getHeader("V8-Version", null); |
|
124 if (vmVersion == null) { |
|
125 throw new IOException("Absent V8 VM version"); |
|
126 } |
|
127 RemoteInfo remoteInfo = new RemoteInfo() { |
|
128 public String getProtocolVersion() { |
|
129 return protocolVersion; |
|
130 } |
|
131 public String getV8VmVersion() { |
|
132 return vmVersion; |
|
133 } |
|
134 public String getEmbeddingHostName() { |
|
135 return message.getHeader("Embedding-Host", null); |
|
136 } |
|
137 }; |
|
138 return remoteInfo; |
|
139 } |
|
140 } |
|
141 } |
|
142 } |