View Javadoc
1   /*
2    * Copyright 2014 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty5.example.http.websocketx.client;
17  
18  import io.netty5.bootstrap.Bootstrap;
19  import io.netty5.channel.Channel;
20  import io.netty5.channel.ChannelInitializer;
21  import io.netty5.channel.ChannelPipeline;
22  import io.netty5.channel.EventLoopGroup;
23  import io.netty5.channel.MultithreadEventLoopGroup;
24  import io.netty5.channel.nio.NioHandler;
25  import io.netty5.channel.socket.SocketChannel;
26  import io.netty5.channel.socket.nio.NioSocketChannel;
27  import io.netty5.handler.codec.http.DefaultHttpContent;
28  import io.netty5.handler.codec.http.DefaultHttpHeaders;
29  import io.netty5.handler.codec.http.HttpClientCodec;
30  import io.netty5.handler.codec.http.HttpObjectAggregator;
31  import io.netty5.handler.codec.http.websocketx.CloseWebSocketFrame;
32  import io.netty5.handler.codec.http.websocketx.PingWebSocketFrame;
33  import io.netty5.handler.codec.http.websocketx.TextWebSocketFrame;
34  import io.netty5.handler.codec.http.websocketx.WebSocketClientHandshakerFactory;
35  import io.netty5.handler.codec.http.websocketx.WebSocketFrame;
36  import io.netty5.handler.codec.http.websocketx.WebSocketVersion;
37  import io.netty5.handler.codec.http.websocketx.extensions.compression.WebSocketClientCompressionHandler;
38  import io.netty5.handler.ssl.SslContext;
39  import io.netty5.handler.ssl.SslContextBuilder;
40  import io.netty5.handler.ssl.util.InsecureTrustManagerFactory;
41  
42  import java.io.BufferedReader;
43  import java.io.InputStreamReader;
44  import java.net.URI;
45  
46  /**
47   * This is an example of a WebSocket client.
48   * <p>
49   * In order to run this example you need a compatible WebSocket server.
50   * Therefore you can either start the WebSocket server from the examples
51   * by running {@link io.netty5.example.http.websocketx.server.WebSocketServer}
52   * or connect to an existing WebSocket server such as
53   * <a href="https://www.websocket.org/echo.html">ws://echo.websocket.org</a>.
54   * <p>
55   * The client will attempt to connect to the URI passed to it as the first argument.
56   * You don't have to specify any arguments if you want to connect to the example WebSocket server,
57   * as this is the default.
58   */
59  public final class WebSocketClient {
60  
61      static final String URL = System.getProperty("url", "ws://127.0.0.1:8080/websocket");
62  
63      public static void main(String[] args) throws Exception {
64          URI uri = new URI(URL);
65          String scheme = uri.getScheme() == null? "ws" : uri.getScheme();
66          final String host = uri.getHost() == null? "127.0.0.1" : uri.getHost();
67          final int port;
68          if (uri.getPort() == -1) {
69              if ("ws".equalsIgnoreCase(scheme)) {
70                  port = 80;
71              } else if ("wss".equalsIgnoreCase(scheme)) {
72                  port = 443;
73              } else {
74                  port = -1;
75              }
76          } else {
77              port = uri.getPort();
78          }
79  
80          if (!"ws".equalsIgnoreCase(scheme) && !"wss".equalsIgnoreCase(scheme)) {
81              System.err.println("Only WS(S) is supported.");
82              return;
83          }
84  
85          final boolean ssl = "wss".equalsIgnoreCase(scheme);
86          final SslContext sslCtx;
87          if (ssl) {
88              sslCtx = SslContextBuilder.forClient()
89                  .trustManager(InsecureTrustManagerFactory.INSTANCE).build();
90          } else {
91              sslCtx = null;
92          }
93  
94          EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory());
95          try {
96              // Connect with V13 (RFC 6455 aka HyBi-17). You can change it to V08 or V00.
97              // If you change it to V00, ping is not supported and remember to change
98              // HttpResponseDecoder to WebSocketHttpResponseDecoder in the pipeline.
99              final WebSocketClientHandler handler =
100                     new WebSocketClientHandler(
101                             WebSocketClientHandshakerFactory.newHandshaker(
102                                     uri, WebSocketVersion.V13, null, true, new DefaultHttpHeaders()));
103 
104             Bootstrap b = new Bootstrap();
105             b.group(group)
106              .channel(NioSocketChannel.class)
107              .handler(new ChannelInitializer<SocketChannel>() {
108                  @Override
109                  protected void initChannel(SocketChannel ch) {
110                      ChannelPipeline p = ch.pipeline();
111                      if (sslCtx != null) {
112                          p.addLast(sslCtx.newHandler(ch.bufferAllocator(), host, port));
113                      }
114                      p.addLast(
115                              new HttpClientCodec(),
116                              new HttpObjectAggregator<DefaultHttpContent>(8192),
117                              WebSocketClientCompressionHandler.INSTANCE,
118                              handler);
119                  }
120              });
121 
122             Channel ch = b.connect(uri.getHost(), port).asStage().get();
123             handler.handshakeFuture().asStage().sync();
124 
125             BufferedReader console = new BufferedReader(new InputStreamReader(System.in));
126             while (true) {
127                 String msg = console.readLine();
128                 if (msg == null) {
129                     break;
130                 } else if ("bye".equalsIgnoreCase(msg)) {
131                     ch.writeAndFlush(new CloseWebSocketFrame(true, 0, ch.bufferAllocator().allocate(0)));
132                     ch.closeFuture().asStage().sync();
133                     break;
134                 } else if ("ping".equalsIgnoreCase(msg)) {
135                     WebSocketFrame frame =
136                             new PingWebSocketFrame(ch.bufferAllocator().copyOf(new byte[] { 8, 1, 8, 1 }));
137                     ch.writeAndFlush(frame);
138                 } else {
139                     WebSocketFrame frame = new TextWebSocketFrame(ch.bufferAllocator(), msg);
140                     ch.writeAndFlush(frame);
141                 }
142             }
143         } finally {
144             group.shutdownGracefully();
145         }
146     }
147 }