View Javadoc

    1   /*
    2    * Copyright 2012 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    *   http://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 org.jboss.netty.handler.codec.http.websocketx;
    17
    18  import java.util.Collections;
    19  import java.util.LinkedHashSet;
    20  import java.util.Set;
    21
    22  import org.jboss.netty.channel.Channel;
    23  import org.jboss.netty.channel.ChannelFuture;
    24  import org.jboss.netty.channel.ChannelFutureListener;
    25  import org.jboss.netty.channel.Channels;
    26  import org.jboss.netty.handler.codec.http.HttpRequest;
    27
    28  /**
    29   * Base class for server side web socket opening and closing handshakes
    30   */
    31  public abstract class WebSocketServerHandshaker {
    32
    33      private final String webSocketUrl;
    34
    35      private final String[] subprotocols;
    36
    37      private final WebSocketVersion version;
    38
    39      private final long maxFramePayloadLength;
    40
    41      private String selectedSubprotocol;
    42
    43      /**
    44       * {@link ChannelFutureListener} which will call
    45       * {@link Channels#fireExceptionCaught(org.jboss.netty.channel.ChannelHandlerContext, Throwable)}
    46       * if the {@link ChannelFuture} was not successful.
    47       */
    48      public static final ChannelFutureListener HANDSHAKE_LISTENER = new ChannelFutureListener() {
    49          public void operationComplete(ChannelFuture future) throws Exception {
    50              if (!future.isSuccess()) {
    51                  Channels.fireExceptionCaught(future.getChannel(), future.getCause());
    52              }
    53          }
    54      };
    55
    56      /**
    57       * Constructor using default values
    58       *
    59       * @param version
    60       *            the protocol version
    61       * @param webSocketUrl
    62       *            URL for web socket communications. e.g
    63       *            "ws://myhost.com/mypath". Subsequent web socket frames will be
    64       *            sent to this URL.
    65       * @param subprotocols
    66       *            CSV of supported protocols. Null if sub protocols not
    67       *            supported.
    68       */
    69      protected WebSocketServerHandshaker(WebSocketVersion version, String webSocketUrl, String subprotocols) {
    70          this(version, webSocketUrl, subprotocols, Long.MAX_VALUE);
    71      }
    72
    73      /**
    74       * Constructor specifying the destination web socket location
    75       *
    76       * @param version
    77       *            the protocol version
    78       * @param webSocketUrl
    79       *            URL for web socket communications. e.g
    80       *            "ws://myhost.com/mypath". Subsequent web socket frames will be
    81       *            sent to this URL.
    82       * @param subprotocols
    83       *            CSV of supported protocols. Null if sub protocols not
    84       *            supported.
    85       * @param maxFramePayloadLength
    86       *            Maximum length of a frame's payload
    87       */
    88      protected WebSocketServerHandshaker(WebSocketVersion version, String webSocketUrl, String subprotocols,
    89              long maxFramePayloadLength) {
    90          this.version = version;
    91          this.webSocketUrl = webSocketUrl;
    92          if (subprotocols != null) {
    93              String[] subprotocolArray = subprotocols.split(",");
    94              for (int i = 0; i < subprotocolArray.length; i++) {
    95                  subprotocolArray[i] = subprotocolArray[i].trim();
    96              }
    97              this.subprotocols = subprotocolArray;
    98          } else {
    99              this.subprotocols = new String[0];
    100         }
    101         this.maxFramePayloadLength = maxFramePayloadLength;
    102     }
    103
    104     /**
    105      * Returns the URL of the web socket
    106      */
    107     public String getWebSocketUrl() {
    108         return webSocketUrl;
    109     }
    110
    111     /**
    112      * Returns the CSV of supported sub protocols
    113      */
    114     public Set<String> getSubprotocols() {
    115         Set<String> ret = new LinkedHashSet<String>();
    116         Collections.addAll(ret, subprotocols);
    117         return ret;
    118     }
    119
    120     /**
    121      * Returns the version of the specification being supported
    122      */
    123     public WebSocketVersion getVersion() {
    124         return version;
    125     }
    126
    127     /**
    128      * Returns the max length for any frame's payload
    129      */
    130     public long getMaxFramePayloadLength() {
    131         return maxFramePayloadLength;
    132     }
    133
    134     /**
    135      * Performs the opening handshake
    136      *
    137      * @param channel
    138      *            Channel
    139      * @param req
    140      *            HTTP Request
    141      */
    142     public abstract ChannelFuture handshake(Channel channel, HttpRequest req);
    143
    144     /**
    145      * Performs the closing handshake
    146      *
    147      * @param channel
    148      *            Channel
    149      * @param frame
    150      *            Closing Frame that was received
    151      */
    152     public abstract ChannelFuture close(Channel channel, CloseWebSocketFrame frame);
    153
    154     /**
    155      * Selects the first matching supported sub protocol
    156      *
    157      * @param requestedSubprotocols
    158      *            CSV of protocols to be supported. e.g. "chat, superchat"
    159      * @return First matching supported sub protocol. Null if not found.
    160      */
    161     protected String selectSubprotocol(String requestedSubprotocols) {
    162         if (requestedSubprotocols == null || subprotocols.length == 0) {
    163             return null;
    164         }
    165
    166         String[] requestedSubprotocolArray = requestedSubprotocols.split(",");
    167         for (String p : requestedSubprotocolArray) {
    168             String requestedSubprotocol = p.trim();
    169
    170             for (String supportedSubprotocol : subprotocols) {
    171                 if (requestedSubprotocol.equals(supportedSubprotocol)) {
    172                     return requestedSubprotocol;
    173                 }
    174             }
    175         }
    176
    177         // No match found
    178         return null;
    179     }
    180
    181     /**
    182      * Returns the selected subprotocol. Null if no subprotocol has been selected.
    183      * <p>
    184      * This is only available AFTER <tt>handshake()</tt> has been called.
    185      * </p>
    186      */
    187     public String getSelectedSubprotocol() {
    188         return selectedSubprotocol;
    189     }
    190
    191     protected void setSelectedSubprotocol(String value) {
    192         selectedSubprotocol = value;
    193     }
    194
    195 }