1 package org.eclipse.jetty.websocket;
2
3 import java.io.BufferedReader;
4 import java.io.BufferedWriter;
5 import java.io.IOException;
6 import java.io.InputStreamReader;
7 import java.io.OutputStreamWriter;
8 import java.net.Socket;
9 import java.security.SecureRandom;
10 import java.util.Arrays;
11 import java.util.Random;
12 import java.util.concurrent.BlockingQueue;
13 import java.util.concurrent.LinkedBlockingQueue;
14
15 import org.eclipse.jetty.io.Buffer;
16 import org.eclipse.jetty.io.bio.SocketEndPoint;
17 import org.eclipse.jetty.util.B64Code;
18 import org.eclipse.jetty.util.StringUtil;
19 import org.eclipse.jetty.util.TypeUtil;
20 import org.eclipse.jetty.util.log.Log;
21
22
23
24
25
26
27
28 public class TestClient
29 {
30 private final static Random __random = new SecureRandom();
31 private static boolean _verbose=false;
32 private final String _host;
33 private final int _port;
34 private final String _protocol;
35 private int _size=64;
36 private final Socket _socket;
37 private final BufferedWriter _output;
38 private final BufferedReader _input;
39 private final SocketEndPoint _endp;
40 private final WebSocketGeneratorD07 _generator;
41 private final WebSocketParserD07 _parser;
42 private int _framesSent;
43 private int _messagesSent;
44 private int _framesReceived;
45 private int _messagesReceived;
46 private long _totalTime;
47 private long _minDuration=Long.MAX_VALUE;
48 private long _maxDuration=Long.MIN_VALUE;
49 private long _start;
50 private BlockingQueue<Long> _starts = new LinkedBlockingQueue<Long>();
51 int _messageBytes;
52 int _frames;
53 byte _opcode=-1;
54 private final WebSocketParser.FrameHandler _handler = new WebSocketParser.FrameHandler()
55 {
56 public synchronized void onFrame(byte flags, byte opcode, Buffer buffer)
57 {
58 try
59 {
60 _framesReceived++;
61 _frames++;
62 if (opcode == WebSocketConnectionD07.OP_CLOSE)
63 {
64 byte[] data=buffer.asArray();
65
66 _generator.addFrame((byte)0x8,WebSocketConnectionD07.OP_CLOSE,data,0,data.length);
67 _generator.flush();
68 _socket.shutdownOutput();
69 _socket.close();
70 return;
71 }
72 else if (opcode == WebSocketConnectionD07.OP_PING)
73 {
74 _generator.addFrame((byte)0x8,WebSocketConnectionD07.OP_PONG,buffer.array(),buffer.getIndex(),buffer.length());
75 _generator.flush();
76 }
77
78 _messageBytes+=buffer.length();
79
80 if (_opcode==-1)
81 _opcode=opcode;
82
83
84 if (WebSocketConnectionD07.isLastFrame(flags))
85 {
86 _messagesReceived++;
87 Long start=_starts.take();
88
89 long duration = System.nanoTime()-start.longValue();
90 if (duration>_maxDuration)
91 _maxDuration=duration;
92 if (duration<_minDuration)
93 _minDuration=duration;
94 _totalTime+=duration;
95 System.out.printf("%d bytes from %s: frames=%d req=%d time=%.1fms opcode=0x%s\n",_messageBytes,_host,_frames,_messagesReceived,((double)duration/1000000.0),TypeUtil.toHexString(_opcode));
96 _frames=0;
97 _messageBytes=0;
98 _opcode=-1;
99 }
100
101 }
102 catch(Exception e)
103 {
104 e.printStackTrace();
105 }
106
107 }
108
109 public void close(int code,String message)
110 {
111 }
112 };
113
114
115 public TestClient(String host, int port,String protocol, int timeoutMS) throws IOException
116 {
117 _host=host;
118 _port=port;
119 _protocol=protocol;
120 _socket = new Socket(host, port);
121 _socket.setSoTimeout(timeoutMS);
122 _output = new BufferedWriter(new OutputStreamWriter(_socket.getOutputStream(), "ISO-8859-1"));
123 _input = new BufferedReader(new InputStreamReader(_socket.getInputStream(), "ISO-8859-1"));
124
125 _endp=new SocketEndPoint(_socket);
126 _generator = new WebSocketGeneratorD07(new WebSocketBuffers(32*1024),_endp,new WebSocketGeneratorD07.FixedMaskGen(new byte[4]));
127 _parser = new WebSocketParserD07(new WebSocketBuffers(32*1024),_endp,_handler,false);
128 }
129
130 public int getSize()
131 {
132 return _size;
133 }
134
135 public void setSize(int size)
136 {
137 _size = size;
138 }
139
140 private void open() throws IOException
141 {
142 System.out.println("Jetty WebSocket PING "+_host+":"+_port+
143 " ("+_socket.getRemoteSocketAddress()+") " +_size+" bytes of data.");
144 byte[] key = new byte[16];
145 __random.nextBytes(key);
146
147
148 _output.write("GET /chat HTTP/1.1\r\n"+
149 "Host: "+_host+":"+_port+"\r\n"+
150 "Upgrade: websocket\r\n"+
151 "Connection: Upgrade\r\n"+
152 "Sec-WebSocket-Key: "+new String(B64Code.encode(key))+"\r\n"+
153 "Sec-WebSocket-Origin: http://example.com\r\n"+
154 "Sec-WebSocket-Protocol: "+_protocol+"\r\n" +
155 "Sec-WebSocket-Version: 7\r\n"+
156 "\r\n");
157 _output.flush();
158
159 String responseLine = _input.readLine();
160 if(!responseLine.startsWith("HTTP/1.1 101 Switching Protocols"))
161 throw new IOException(responseLine);
162
163 String line;
164 boolean accepted=false;
165 String protocol="";
166 while ((line = _input.readLine()) != null)
167 {
168 if (line.length() == 0)
169 break;
170 if (line.startsWith("Sec-WebSocket-Accept:"))
171 {
172 String accept=line.substring(21).trim();
173 accepted=accept.equals(WebSocketConnectionD07.hashKey(new String(B64Code.encode(key))));
174 }
175 else if (line.startsWith("Sec-WebSocket-Protocol:"))
176 {
177 protocol=line.substring(24).trim();
178 }
179 }
180
181 if (!accepted)
182 throw new IOException("Bad Sec-WebSocket-Accept");
183 System.out.println("handshake OK for protocol '"+protocol+"'");
184
185 new Thread()
186 {
187 public void run()
188 {
189 while (_endp.isOpen())
190 {
191 _parser.parseNext();
192 }
193 }
194 }.start();
195 }
196
197 public void ping(int count,byte opcode,int fragment)
198 {
199 try
200 {
201 _start=System.currentTimeMillis();
202 for (int i=0;i<count && !_socket.isClosed();i++)
203 {
204 if (_socket.isClosed())
205 break;
206 byte data[]=null;
207
208 if (opcode==WebSocketConnectionD07.OP_TEXT)
209 {
210 StringBuilder b = new StringBuilder();
211 while (b.length()<_size)
212 b.append('A'+__random.nextInt(26));
213 data=b.toString().getBytes(StringUtil.__UTF8);
214 }
215 else
216 {
217 data= new byte[_size];
218 __random.nextBytes(data);
219 }
220 _starts.add(System.nanoTime());
221
222 int off=0;
223 int len=data.length;
224 if (fragment>0&& len>fragment)
225 len=fragment;
226 _messagesSent++;
227 while(off<data.length)
228 {
229 _framesSent++;
230 byte flags= (byte)(off+len==data.length?0x8:0);
231 byte op=(byte)(off==0?opcode:WebSocketConnectionD07.OP_CONTINUATION);
232
233 if (_verbose)
234 System.err.printf("%s#addFrame %s|%s %s\n",this.getClass().getSimpleName(),TypeUtil.toHexString(flags),TypeUtil.toHexString(op),TypeUtil.toHexString(data,off,len));
235 _generator.addFrame(flags,op,data,off,len);
236
237 off+=len;
238 if(data.length-off>len)
239 len=data.length-off;
240 if (fragment>0&& len>fragment)
241 len=fragment;
242 }
243
244 _generator.flush();
245
246 Thread.sleep(1000);
247 }
248 }
249 catch (Exception x)
250 {
251 throw new RuntimeException(x);
252 }
253 }
254
255 public void dump() throws Exception
256 {
257 for (int i=0;i<250;i++)
258 {
259 if (_messagesSent==_messagesReceived)
260 break;
261 _generator.flush();
262 Thread.sleep(100);
263 }
264
265 _socket.close();
266 long duration=System.currentTimeMillis()-_start;
267 System.out.println("--- "+_host+" websocket ping statistics using 1 connection ---");
268 System.out.println(_framesSent+" frames transmitted, "+_framesReceived+" received, "+
269 _messagesSent+" messages transmitted, "+_messagesReceived+" received, "+
270 "time "+duration+"ms");
271 System.out.printf("rtt min/ave/max = %.3f/%.3f/%.3f ms\n",_minDuration/1000000.0,_messagesReceived==0?0.0:(_totalTime/_messagesReceived/1000000.0),_maxDuration/1000000.0);
272 }
273
274
275 private static void usage(String[] args)
276 {
277 System.err.println("ERROR: "+Arrays.asList(args));
278 System.err.println("USAGE: java -cp CLASSPATH "+TestClient.class+" [ OPTIONS ]");
279 System.err.println(" -h|--host HOST (default localhost)");
280 System.err.println(" -p|--port PORT (default 8080)");
281 System.err.println(" -b|--binary");
282 System.err.println(" -v|--verbose");
283 System.err.println(" -c|--count n (default 10)");
284 System.err.println(" -s|--size n (default 64)");
285 System.err.println(" -f|--fragment n (default 4000) ");
286 System.err.println(" -P|--protocol echo|echo-assemble|echo-fragment|echo-broadcast");
287 System.exit(1);
288 }
289
290 public static void main(String[] args)
291 {
292 try
293 {
294 String host="localhost";
295 int port=8080;
296 String protocol=null;
297 int count=10;
298 int size=64;
299 int fragment=4000;
300 boolean binary=false;
301
302 for (int i=0;i<args.length;i++)
303 {
304 String a=args[i];
305 if ("-p".equals(a)||"--port".equals(a))
306 port=Integer.parseInt(args[++i]);
307 else if ("-h".equals(a)||"--host".equals(a))
308 port=Integer.parseInt(args[++i]);
309 else if ("-c".equals(a)||"--count".equals(a))
310 count=Integer.parseInt(args[++i]);
311 else if ("-s".equals(a)||"--size".equals(a))
312 size=Integer.parseInt(args[++i]);
313 else if ("-f".equals(a)||"--fragment".equals(a))
314 fragment=Integer.parseInt(args[++i]);
315 else if ("-P".equals(a)||"--protocol".equals(a))
316 protocol=args[++i];
317 else if ("-v".equals(a)||"--verbose".equals(a))
318 _verbose=true;
319 else if ("-b".equals(a)||"--binary".equals(a))
320 binary=true;
321 else if (a.startsWith("-"))
322 usage(args);
323 }
324
325
326 TestClient client = new TestClient(host,port,protocol==null?null:protocol,10000);
327 client.setSize(size);
328
329 try
330 {
331 client.open();
332 if (protocol!=null && protocol.startsWith("echo"))
333 client.ping(count,binary?WebSocketConnectionD07.OP_BINARY:WebSocketConnectionD07.OP_TEXT,fragment);
334 else
335 client.ping(count,WebSocketConnectionD07.OP_PING,-1);
336 }
337 finally
338 {
339 client.dump();
340 }
341
342 }
343 catch (Exception e)
344 {
345 Log.warn(e);
346 }
347 }
348
349
350
351 }