You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@trafficserver.apache.org by James Peach <jp...@apache.org> on 2014/02/04 02:30:47 UTC

Re: [1/2] git commit: [TS-2541] Add WebSocket Support

On Feb 3, 2014, at 11:11 AM, briang@apache.org wrote:

> Updated Branches:
>  refs/heads/master f0f9cb9c3 -> 60ec95053
> 
> 
> [TS-2541] Add WebSocket Support

Nice; were you planning to update the docs?

> 
> 
> Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo
> Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/cca82f1b
> Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/cca82f1b
> Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/cca82f1b
> 
> Branch: refs/heads/master
> Commit: cca82f1b910dfea6903e05602b06df53e4979b9b
> Parents: 70f8e10
> Author: Brian Geffon <br...@apache.org>
> Authored: Mon Feb 3 11:10:44 2014 -0800
> Committer: Brian Geffon <br...@apache.org>
> Committed: Mon Feb 3 11:10:44 2014 -0800
> 
> ----------------------------------------------------------------------
> CHANGES                           |   2 +
> proxy/config/remap.config.default |   8 +
> proxy/hdrs/HdrToken.cc            |  18 +++
> proxy/hdrs/HdrToken.h             |   8 +-
> proxy/hdrs/MIME.cc                |  20 ++-
> proxy/hdrs/MIME.h                 |   8 +
> proxy/hdrs/URL.cc                 |  14 ++
> proxy/hdrs/URL.h                  |   6 +
> proxy/http/HttpClientSession.cc   |   1 +
> proxy/http/HttpConfig.cc          |   5 +
> proxy/http/HttpConfig.h           |   1 +
> proxy/http/HttpDebugNames.cc      |   3 +-
> proxy/http/HttpSM.cc              |  23 ++-
> proxy/http/HttpTransact.cc        | 284 ++++++++++++++++++++++++++++-----
> proxy/http/HttpTransact.h         |  33 +++-
> proxy/http/HttpTransactHeaders.cc |   7 +-
> proxy/http/remap/RemapConfig.cc   |  17 +-
> 17 files changed, 406 insertions(+), 52 deletions(-)
> ----------------------------------------------------------------------
> 
> 
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/cca82f1b/CHANGES
> ----------------------------------------------------------------------
> diff --git a/CHANGES b/CHANGES
> index be006e8..fab65c0 100644
> --- a/CHANGES
> +++ b/CHANGES
> @@ -1,6 +1,8 @@
>                                                          -*- coding: utf-8 -*-
> Changes with Apache Traffic Server 4.2.0
> 
> +  *) [TS-2541] Add WebSocket support
> +
>   *) [TS-2550] Add inline configuration overrised to the conf_remap plugin.
> 
>   *) [TS-2546] Move xptr and _xstrdup to ink_memory.{h,cc}.
> 
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/cca82f1b/proxy/config/remap.config.default
> ----------------------------------------------------------------------
> diff --git a/proxy/config/remap.config.default b/proxy/config/remap.config.default
> index e0cd89a..2198ee4 100644
> --- a/proxy/config/remap.config.default
> +++ b/proxy/config/remap.config.default
> @@ -149,6 +149,14 @@
> #  Example:
> #    map http://foo.cow.com/ http://bar.cow.com @src_ip=10.72.118.51-10.72.118.62 @method=GET @method=DELETE @src_ip=192.168.0.1-192.168.0.254 @action=allow @method=PUT
> #
> +#  Traffic Server supports WebSockets but it must be enabled via remap. WebSocket upgrades are automatically
> +#  detected when there exists a remap rule containing a ws:// scheme.
> +#
> +#  Example:
> +#     map ws://bar.com/ ws://foo.com/
> +#
> +#  Explaination: When a request comes in with the appropriate upgrade headers, Traffic Server will use this
> +#   remap rule in an attempt to establish and maintain a websocket connection.
> #
> #  Named filters can be created and applied to blocks of mappings
> #  using the .definefilter, .activatefilter, and .deactivatefilter
> 
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/cca82f1b/proxy/hdrs/HdrToken.cc
> ----------------------------------------------------------------------
> diff --git a/proxy/hdrs/HdrToken.cc b/proxy/hdrs/HdrToken.cc
> index 72bbbe1..7977b67 100644
> --- a/proxy/hdrs/HdrToken.cc
> +++ b/proxy/hdrs/HdrToken.cc
> @@ -145,6 +145,11 @@ static const char *_hdrtoken_strs[] = {
>   "chunked",
>   "close",
> 
> +  // WS
> +  "websocket",
> +  "Sec-WebSocket-Key",
> +  "Sec-WebSocket-Version",
> +
>   // URL schemes
>   "file",
>   "ftp",
> @@ -164,6 +169,8 @@ static const char *_hdrtoken_strs[] = {
>   "mmsu",
>   "mmst",
>   "mms",
> +  "wss",
> +  "ws",
> 
>   // HTTP methods
>   "CONNECT",
> @@ -203,6 +210,8 @@ static HdrTokenTypeBinding _hdrtoken_strs_type_initializers[] = {
>   {"mms", HDRTOKEN_TYPE_SCHEME},
>   {"mmsu", HDRTOKEN_TYPE_SCHEME},
>   {"mmst", HDRTOKEN_TYPE_SCHEME},
> +  {"wss", HDRTOKEN_TYPE_SCHEME},
> +  {"ws", HDRTOKEN_TYPE_SCHEME},
> 
>   {"CONNECT", HDRTOKEN_TYPE_METHOD},
>   {"DELETE", HDRTOKEN_TYPE_METHOD},
> @@ -309,6 +318,8 @@ static HdrTokenFieldInfo _hdrtoken_strs_field_initializers[] = {
>   {"@DataInfo", MIME_SLOTID_NONE, MIME_PRESENCE_INT_DATA_INFO, HTIF_NONE},
>   {"X-ID", MIME_SLOTID_NONE, MIME_PRESENCE_NONE, (HTIF_COMMAS | HTIF_MULTVALS | HTIF_HOPBYHOP)},
>   {"X-Forwarded-For", MIME_SLOTID_NONE, MIME_PRESENCE_NONE, (HTIF_COMMAS | HTIF_MULTVALS)},
> +  {"Sec-WebSocket-Key", MIME_SLOTID_NONE, MIME_PRESENCE_NONE, HTIF_NONE},
> +  {"Sec-WebSocket-Version", MIME_SLOTID_NONE, MIME_PRESENCE_NONE, HTIF_NONE},
>   {NULL, 0, 0, 0}
> };
> 
> @@ -473,6 +484,11 @@ static const char *_hdrtoken_commonly_tokenized_strs[] = {
>   "chunked",
>   "close",
> 
> +  // WS
> +  "websocket",
> +  "Sec-WebSocket-Key",
> +  "Sec-WebSocket-Version",
> +
>   // URL schemes
>   "file",
>   "ftp",
> @@ -492,6 +508,8 @@ static const char *_hdrtoken_commonly_tokenized_strs[] = {
>   "mmsu",
>   "mmst",
>   "mms",
> +  "wss",
> +  "ws",
> 
>   // HTTP methods
>   "CONNECT",
> 
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/cca82f1b/proxy/hdrs/HdrToken.h
> ----------------------------------------------------------------------
> diff --git a/proxy/hdrs/HdrToken.h b/proxy/hdrs/HdrToken.h
> index 052753a..95dc95c 100644
> --- a/proxy/hdrs/HdrToken.h
> +++ b/proxy/hdrs/HdrToken.h
> @@ -373,11 +373,11 @@ hdrtoken_wks_to_flags(const char *wks)
> 
> // bits 56-60 were used for a benchmark hack, but are now free to be used
> // for something else
> -#define MIME_PRESENCE_UNUSED_1			(TOK_64_CONST(1) << 56)
> -#define MIME_PRESENCE_UNUSED_2	 		(TOK_64_CONST(1) << 57)
> +#define MIME_PRESENCE_UNUSED_1      (TOK_64_CONST(1) << 56)
> +#define MIME_PRESENCE_UNUSED_2			(TOK_64_CONST(1) << 57)
> #define MIME_PRESENCE_UNUSED_3			(TOK_64_CONST(1) << 58)
> -#define MIME_PRESENCE_UNUSED_4			(TOK_64_CONST(1) << 59)
> -#define MIME_PRESENCE_UNUSED_5			(TOK_64_CONST(1) << 60)
> +#define MIME_PRESENCE_UNUSED_4      (TOK_64_CONST(1) << 59)
> +#define MIME_PRESENCE_UNUSED_5      (TOK_64_CONST(1) << 60)
> 
> #define MIME_PRESENCE_XREF			(TOK_64_CONST(1) << 61)
> #define MIME_PRESENCE_INT_DATA_INFO		(TOK_64_CONST(1) << 62)
> 
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/cca82f1b/proxy/hdrs/MIME.cc
> ----------------------------------------------------------------------
> diff --git a/proxy/hdrs/MIME.cc b/proxy/hdrs/MIME.cc
> index 0313314..0de0db2 100644
> --- a/proxy/hdrs/MIME.cc
> +++ b/proxy/hdrs/MIME.cc
> @@ -156,6 +156,8 @@ const char *MIME_FIELD_XREF;
> const char *MIME_FIELD_INT_DATA_INFO;
> const char *MIME_FIELD_X_ID;
> const char *MIME_FIELD_X_FORWARDED_FOR;
> +const char *MIME_FIELD_SEC_WEBSOCKET_KEY;
> +const char *MIME_FIELD_SEC_WEBSOCKET_VERSION;
> 
> const char *MIME_VALUE_BYTES;
> const char *MIME_VALUE_CHUNKED;
> @@ -179,6 +181,8 @@ const char *MIME_VALUE_PROXY_REVALIDATE;
> const char *MIME_VALUE_PUBLIC;
> const char *MIME_VALUE_S_MAXAGE;
> const char *MIME_VALUE_NEED_REVALIDATE_ONCE;
> +const char *MIME_VALUE_WEBSOCKET;
> +
> // Cache-control: extension "need-revalidate-once" is used internally by T.S.
> // to invalidate a document, and it is not returned/forwarded.
> // If a cached document has this extension set (ie, is invalidated),
> @@ -265,6 +269,8 @@ int MIME_LEN_XREF;
> int MIME_LEN_INT_DATA_INFO;
> int MIME_LEN_X_ID;
> int MIME_LEN_X_FORWARDED_FOR;
> +int MIME_LEN_SEC_WEBSOCKET_KEY;
> +int MIME_LEN_SEC_WEBSOCKET_VERSION;
> 
> int MIME_WKSIDX_ACCEPT;
> int MIME_WKSIDX_ACCEPT_CHARSET;
> @@ -340,7 +346,8 @@ int MIME_WKSIDX_XREF;
> int MIME_WKSIDX_INT_DATA_INFO;
> int MIME_WKSIDX_X_ID;
> int MIME_WKSIDX_X_FORWARDED_FOR;
> -
> +int MIME_WKSIDX_SEC_WEBSOCKET_KEY;
> +int MIME_WKSIDX_SEC_WEBSOCKET_VERSION;
> 
> /***********************************************************************
>  *                                                                     *
> @@ -684,6 +691,9 @@ mime_init()
>     MIME_FIELD_X_ID = hdrtoken_string_to_wks("X-ID");
>     MIME_FIELD_X_FORWARDED_FOR = hdrtoken_string_to_wks("X-Forwarded-For");
> 
> +    MIME_FIELD_SEC_WEBSOCKET_KEY = hdrtoken_string_to_wks("Sec-WebSocket-Key");
> +    MIME_FIELD_SEC_WEBSOCKET_VERSION = hdrtoken_string_to_wks("Sec-WebSocket-Version");
> +
> 
>     MIME_LEN_ACCEPT = hdrtoken_wks_to_length(MIME_FIELD_ACCEPT);
>     MIME_LEN_ACCEPT_CHARSET = hdrtoken_wks_to_length(MIME_FIELD_ACCEPT_CHARSET);
> @@ -760,6 +770,10 @@ mime_init()
>     MIME_LEN_X_ID = hdrtoken_wks_to_length(MIME_FIELD_X_ID);
>     MIME_LEN_X_FORWARDED_FOR = hdrtoken_wks_to_length(MIME_FIELD_X_FORWARDED_FOR);
> 
> +    MIME_LEN_SEC_WEBSOCKET_KEY = hdrtoken_wks_to_length(MIME_FIELD_SEC_WEBSOCKET_KEY);
> +    MIME_LEN_SEC_WEBSOCKET_VERSION = hdrtoken_wks_to_length(MIME_FIELD_SEC_WEBSOCKET_VERSION);
> +
> +
>     MIME_WKSIDX_ACCEPT = hdrtoken_wks_to_index(MIME_FIELD_ACCEPT);
>     MIME_WKSIDX_ACCEPT_CHARSET = hdrtoken_wks_to_index(MIME_FIELD_ACCEPT_CHARSET);
>     MIME_WKSIDX_ACCEPT_ENCODING = hdrtoken_wks_to_index(MIME_FIELD_ACCEPT_ENCODING);
> @@ -833,6 +847,8 @@ mime_init()
>     MIME_WKSIDX_XREF = hdrtoken_wks_to_index(MIME_FIELD_XREF);
>     MIME_WKSIDX_X_ID = hdrtoken_wks_to_index(MIME_FIELD_X_ID);
>     MIME_WKSIDX_X_FORWARDED_FOR = hdrtoken_wks_to_index(MIME_FIELD_X_FORWARDED_FOR);
> +    MIME_WKSIDX_SEC_WEBSOCKET_KEY = hdrtoken_wks_to_index(MIME_FIELD_SEC_WEBSOCKET_KEY);
> +    MIME_WKSIDX_SEC_WEBSOCKET_VERSION = hdrtoken_wks_to_index(MIME_FIELD_SEC_WEBSOCKET_VERSION);
> 
>     MIME_VALUE_BYTES = hdrtoken_string_to_wks("bytes");
>     MIME_VALUE_CHUNKED = hdrtoken_string_to_wks("chunked");
> @@ -856,6 +872,8 @@ mime_init()
>     MIME_VALUE_PUBLIC = hdrtoken_string_to_wks("public");
>     MIME_VALUE_S_MAXAGE = hdrtoken_string_to_wks("s-maxage");
>     MIME_VALUE_NEED_REVALIDATE_ONCE = hdrtoken_string_to_wks("need-revalidate-once");
> +    MIME_VALUE_WEBSOCKET = hdrtoken_string_to_wks("websocket");
> +
> 
>     mime_init_date_format_table();
>     mime_init_cache_control_cooking_masks();
> 
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/cca82f1b/proxy/hdrs/MIME.h
> ----------------------------------------------------------------------
> diff --git a/proxy/hdrs/MIME.h b/proxy/hdrs/MIME.h
> index a75e56a..9c7b721 100644
> --- a/proxy/hdrs/MIME.h
> +++ b/proxy/hdrs/MIME.h
> @@ -367,6 +367,8 @@ extern const char *MIME_FIELD_XREF;
> extern const char *MIME_FIELD_INT_DATA_INFO;
> extern const char *MIME_FIELD_X_ID;
> extern const char *MIME_FIELD_X_FORWARDED_FOR;
> +extern const char *MIME_FIELD_SEC_WEBSOCKET_KEY;
> +extern const char *MIME_FIELD_SEC_WEBSOCKET_VERSION;
> 
> extern const char *MIME_VALUE_BYTES;
> extern const char *MIME_VALUE_CHUNKED;
> @@ -390,6 +392,7 @@ extern const char *MIME_VALUE_PROXY_REVALIDATE;
> extern const char *MIME_VALUE_PUBLIC;
> extern const char *MIME_VALUE_S_MAXAGE;
> extern const char *MIME_VALUE_NEED_REVALIDATE_ONCE;
> +extern const char *MIME_VALUE_WEBSOCKET;
> 
> extern int MIME_LEN_ACCEPT;
> extern int MIME_LEN_ACCEPT_CHARSET;
> @@ -489,6 +492,9 @@ extern int MIME_LEN_PUBLIC;
> extern int MIME_LEN_S_MAXAGE;
> extern int MIME_LEN_NEED_REVALIDATE_ONCE;
> 
> +extern int MIME_LEN_SEC_WEBSOCKET_KEY;
> +extern int MIME_LEN_SEC_WEBSOCKET_VERSION;
> +
> extern int MIME_WKSIDX_ACCEPT;
> extern int MIME_WKSIDX_ACCEPT_CHARSET;
> extern int MIME_WKSIDX_ACCEPT_ENCODING;
> @@ -562,6 +568,8 @@ extern int MIME_WKSIDX_WWW_AUTHENTICATE;
> extern int MIME_WKSIDX_XREF;
> extern int MIME_WKSIDX_INT_DATA_INFO;
> extern int MIME_WKSIDX_X_ID;
> +extern int MIME_WKSIDX_SEC_WEBSOCKET_KEY;
> +extern int MIME_WKSIDX_SEC_WEBSOCKET_VERSION;
> 
> /***********************************************************************
>  *                                                                     *
> 
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/cca82f1b/proxy/hdrs/URL.cc
> ----------------------------------------------------------------------
> diff --git a/proxy/hdrs/URL.cc b/proxy/hdrs/URL.cc
> index 976a9c4..2dea36f 100644
> --- a/proxy/hdrs/URL.cc
> +++ b/proxy/hdrs/URL.cc
> @@ -33,6 +33,8 @@ const char *URL_SCHEME_FTP;
> const char *URL_SCHEME_GOPHER;
> const char *URL_SCHEME_HTTP;
> const char *URL_SCHEME_HTTPS;
> +const char *URL_SCHEME_WSS;
> +const char *URL_SCHEME_WS;
> const char *URL_SCHEME_MAILTO;
> const char *URL_SCHEME_NEWS;
> const char *URL_SCHEME_NNTP;
> @@ -52,6 +54,8 @@ int URL_WKSIDX_FTP;
> int URL_WKSIDX_GOPHER;
> int URL_WKSIDX_HTTP;
> int URL_WKSIDX_HTTPS;
> +int URL_WKSIDX_WS;
> +int URL_WKSIDX_WSS;
> int URL_WKSIDX_MAILTO;
> int URL_WKSIDX_NEWS;
> int URL_WKSIDX_NNTP;
> @@ -71,6 +75,8 @@ int URL_LEN_FTP;
> int URL_LEN_GOPHER;
> int URL_LEN_HTTP;
> int URL_LEN_HTTPS;
> +int URL_LEN_WS;
> +int URL_LEN_WSS;
> int URL_LEN_MAILTO;
> int URL_LEN_NEWS;
> int URL_LEN_NNTP;
> @@ -106,6 +112,8 @@ url_init()
>     URL_SCHEME_GOPHER = hdrtoken_string_to_wks("gopher");
>     URL_SCHEME_HTTP = hdrtoken_string_to_wks("http");
>     URL_SCHEME_HTTPS = hdrtoken_string_to_wks("https");
> +    URL_SCHEME_WSS = hdrtoken_string_to_wks("wss");
> +    URL_SCHEME_WS = hdrtoken_string_to_wks("ws");
>     URL_SCHEME_MAILTO = hdrtoken_string_to_wks("mailto");
>     URL_SCHEME_NEWS = hdrtoken_string_to_wks("news");
>     URL_SCHEME_NNTP = hdrtoken_string_to_wks("nntp");
> @@ -125,6 +133,8 @@ url_init()
>       URL_SCHEME_GOPHER && 
>       URL_SCHEME_HTTP && 
>       URL_SCHEME_HTTPS && 
> +      URL_SCHEME_WS &&
> +      URL_SCHEME_WSS &&
>       URL_SCHEME_MAILTO && 
>       URL_SCHEME_NEWS && 
>       URL_SCHEME_NNTP && 
> @@ -145,6 +155,8 @@ url_init()
>     URL_WKSIDX_GOPHER = hdrtoken_wks_to_index(URL_SCHEME_GOPHER);
>     URL_WKSIDX_HTTP = hdrtoken_wks_to_index(URL_SCHEME_HTTP);
>     URL_WKSIDX_HTTPS = hdrtoken_wks_to_index(URL_SCHEME_HTTPS);
> +    URL_WKSIDX_WS = hdrtoken_wks_to_index(URL_SCHEME_WS);
> +    URL_WKSIDX_WSS = hdrtoken_wks_to_index(URL_SCHEME_WSS);
>     URL_WKSIDX_MAILTO = hdrtoken_wks_to_index(URL_SCHEME_MAILTO);
>     URL_WKSIDX_NEWS = hdrtoken_wks_to_index(URL_SCHEME_NEWS);
>     URL_WKSIDX_NNTP = hdrtoken_wks_to_index(URL_SCHEME_NNTP);
> @@ -164,6 +176,8 @@ url_init()
>     URL_LEN_GOPHER = hdrtoken_wks_to_length(URL_SCHEME_GOPHER);
>     URL_LEN_HTTP = hdrtoken_wks_to_length(URL_SCHEME_HTTP);
>     URL_LEN_HTTPS = hdrtoken_wks_to_length(URL_SCHEME_HTTPS);
> +    URL_LEN_WS = hdrtoken_wks_to_length(URL_SCHEME_WS);
> +    URL_LEN_WSS = hdrtoken_wks_to_length(URL_SCHEME_WSS);
>     URL_LEN_MAILTO = hdrtoken_wks_to_length(URL_SCHEME_MAILTO);
>     URL_LEN_NEWS = hdrtoken_wks_to_length(URL_SCHEME_NEWS);
>     URL_LEN_NNTP = hdrtoken_wks_to_length(URL_SCHEME_NNTP);
> 
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/cca82f1b/proxy/hdrs/URL.h
> ----------------------------------------------------------------------
> diff --git a/proxy/hdrs/URL.h b/proxy/hdrs/URL.h
> index 30f1711..c60d1c2 100644
> --- a/proxy/hdrs/URL.h
> +++ b/proxy/hdrs/URL.h
> @@ -91,6 +91,8 @@ extern const char *URL_SCHEME_FTP;
> extern const char *URL_SCHEME_GOPHER;
> extern const char *URL_SCHEME_HTTP;
> extern const char *URL_SCHEME_HTTPS;
> +extern const char *URL_SCHEME_WS;
> +extern const char *URL_SCHEME_WSS;
> extern const char *URL_SCHEME_MAILTO;
> extern const char *URL_SCHEME_NEWS;
> extern const char *URL_SCHEME_NNTP;
> @@ -110,6 +112,8 @@ extern int URL_WKSIDX_FTP;
> extern int URL_WKSIDX_GOPHER;
> extern int URL_WKSIDX_HTTP;
> extern int URL_WKSIDX_HTTPS;
> +extern int URL_WKSIDX_WS;
> +extern int URL_WKSIDX_WSS;
> extern int URL_WKSIDX_MAILTO;
> extern int URL_WKSIDX_NEWS;
> extern int URL_WKSIDX_NNTP;
> @@ -129,6 +133,8 @@ extern int URL_LEN_FTP;
> extern int URL_LEN_GOPHER;
> extern int URL_LEN_HTTP;
> extern int URL_LEN_HTTPS;
> +extern int URL_LEN_WS;
> +extern int URL_LEN_WSS;
> extern int URL_LEN_MAILTO;
> extern int URL_LEN_NEWS;
> extern int URL_LEN_NNTP;
> 
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/cca82f1b/proxy/http/HttpClientSession.cc
> ----------------------------------------------------------------------
> diff --git a/proxy/http/HttpClientSession.cc b/proxy/http/HttpClientSession.cc
> index 5db6275..c518044 100644
> --- a/proxy/http/HttpClientSession.cc
> +++ b/proxy/http/HttpClientSession.cc
> @@ -285,6 +285,7 @@ HttpClientSession::do_io_close(int alerrno)
>       HTTP_DECREMENT_DYN_STAT(http_current_active_client_connections_stat);
>     }
>   }
> +
>   // Prevent double closing
>   ink_release_assert(read_state != HCS_CLOSED);
> 
> 
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/cca82f1b/proxy/http/HttpConfig.cc
> ----------------------------------------------------------------------
> diff --git a/proxy/http/HttpConfig.cc b/proxy/http/HttpConfig.cc
> index 61ba66b..419de41 100644
> --- a/proxy/http/HttpConfig.cc
> +++ b/proxy/http/HttpConfig.cc
> @@ -140,6 +140,11 @@ register_stat_callbacks()
>                      RECD_INT, RECP_NON_PERSISTENT,
>                      (int) http_current_active_client_connections_stat, RecRawStatSyncSum);
>   HTTP_CLEAR_DYN_STAT(http_current_active_client_connections_stat);
> +  RecRegisterRawStat(http_rsb, RECT_PROCESS,
> +                     "proxy.process.http.websocket.current_active_client_connections",
> +                     RECD_INT, RECP_NON_PERSISTENT,
> +                     (int) http_websocket_current_active_client_connections_stat, RecRawStatSyncSum);
> +  HTTP_CLEAR_DYN_STAT(http_websocket_current_active_client_connections_stat);
>   // Current Transaction Stats
>   RecRegisterRawStat(http_rsb, RECT_PROCESS,
>                      "proxy.process.http.current_client_transactions",
> 
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/cca82f1b/proxy/http/HttpConfig.h
> ----------------------------------------------------------------------
> diff --git a/proxy/http/HttpConfig.h b/proxy/http/HttpConfig.h
> index e4790a9..54cf273 100644
> --- a/proxy/http/HttpConfig.h
> +++ b/proxy/http/HttpConfig.h
> @@ -66,6 +66,7 @@ enum
>   http_background_fill_current_count_stat,
>   http_current_client_connections_stat,
>   http_current_active_client_connections_stat,
> +  http_websocket_current_active_client_connections_stat,
>   http_current_client_transactions_stat,
>   http_total_incoming_connections_stat,
>   http_current_parent_proxy_transactions_stat,
> 
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/cca82f1b/proxy/http/HttpDebugNames.cc
> ----------------------------------------------------------------------
> diff --git a/proxy/http/HttpDebugNames.cc b/proxy/http/HttpDebugNames.cc
> index c5a3728..0211294 100644
> --- a/proxy/http/HttpDebugNames.cc
> +++ b/proxy/http/HttpDebugNames.cc
> @@ -396,7 +396,8 @@ HttpDebugNames::get_action_name(HttpTransact::StateMachineAction_t e)
>     return ("HTTP_API_POST_REMAP");
>   case HttpTransact::HTTP_POST_REMAP_SKIP:
>     return ("HTTP_POST_REMAP_SKIP");
> -
> +  case HttpTransact::HTTP_POST_REMAP_UPGRADE:
> +    return ("HTTP_POST_REMAP_UPGRADE");
>   }
> 
>   return ("unknown state name");
> 
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/cca82f1b/proxy/http/HttpSM.cc
> ----------------------------------------------------------------------
> diff --git a/proxy/http/HttpSM.cc b/proxy/http/HttpSM.cc
> index 41f8cf1..8618728 100644
> --- a/proxy/http/HttpSM.cc
> +++ b/proxy/http/HttpSM.cc
> @@ -1583,9 +1583,19 @@ HttpSM::handle_api_return()
>     }
>   case HttpTransact::SERVER_READ:
>     {
> -      setup_server_transfer();
> -      perform_cache_write_action();
> -      tunnel.tunnel_run();
> +      if (unlikely(t_state.did_upgrade_succeed)) {
> +       // We've sucessfully handled the upgrade, let's now setup
> +       // a blind tunnel.
> +       if(t_state.is_websocket) {
> +         HTTP_INCREMENT_DYN_STAT(http_websocket_current_active_client_connections_stat);
> +       }
> +
> +       setup_blind_tunnel(true);
> +      } else {
> +       setup_server_transfer();
> +       perform_cache_write_action();
> +       tunnel.tunnel_run();
> +      }
>       break;
>     }
>   case HttpTransact::SERVE_FROM_CACHE:
> @@ -2739,6 +2749,11 @@ HttpSM::tunnel_handler(int event, void *data)
>   ink_assert(data == &tunnel);
>   // The tunnel calls this when it is done
>   terminate_sm = true;
> +
> +  if (unlikely(t_state.is_websocket)) {
> +    HTTP_DECREMENT_DYN_STAT(http_websocket_current_active_client_connections_stat);
> +  }
> +
>   return 0;
> }
> 
> @@ -6136,6 +6151,7 @@ HttpSM::setup_server_transfer_to_cache_only()
> void
> HttpSM::setup_server_transfer()
> {
> +  DebugSM("http", "Setup Server Transfer");
>   int64_t alloc_index, hdr_size;
>   int64_t nbytes;
> 
> @@ -6806,6 +6822,7 @@ HttpSM::set_next_state()
>       break;
>     }
> 
> +  case HttpTransact::HTTP_POST_REMAP_UPGRADE:
>   case HttpTransact::HTTP_POST_REMAP_SKIP:
>     {
>       call_transact_and_set_next_state(NULL);
> 
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/cca82f1b/proxy/http/HttpTransact.cc
> ----------------------------------------------------------------------
> diff --git a/proxy/http/HttpTransact.cc b/proxy/http/HttpTransact.cc
> index a3187b2..9692d99 100644
> --- a/proxy/http/HttpTransact.cc
> +++ b/proxy/http/HttpTransact.cc
> @@ -757,6 +757,11 @@ HttpTransact::StartRemapRequest(State* s)
> 
>   if (s->api_skip_all_remapping) {
>     Debug ("http_trans", "API request to skip remapping");
> +
> +    if (s->is_upgrade_request && s->post_remap_upgrade_return_point) {
> +      TRANSACT_RETURN(HTTP_POST_REMAP_SKIP, s->post_remap_upgrade_return_point);
> +    }
> +
>     TRANSACT_RETURN(HTTP_POST_REMAP_SKIP, HttpTransact::HandleRequest);
>   }
> 
> @@ -965,12 +970,150 @@ done:
>   } else {
>     s->hdr_info.client_response.clear(); //anything previously set is invalid from this point forward
>     DebugTxn("http_trans", "END HttpTransact::EndRemapRequest");
> +
> +    if (s->is_upgrade_request && s->post_remap_upgrade_return_point) {
> +      TRANSACT_RETURN(HTTP_API_POST_REMAP, s->post_remap_upgrade_return_point);
> +    }
> +
>     TRANSACT_RETURN(HTTP_API_POST_REMAP, HttpTransact::HandleRequest);
>   }
> 
>   ink_assert(!"not reached");
> }
> 
> +bool HttpTransact::handle_upgrade_request(State *s) {
> +  // Quickest way to determine that this is defintely not an upgrade.
> +  /* RFC 6455 The method of the request MUST be GET, and the HTTP version MUST
> +        be at least 1.1. */
> +  if (!s->hdr_info.client_request.presence(MIME_PRESENCE_UPGRADE) ||
> +      !s->hdr_info.client_request.presence(MIME_PRESENCE_CONNECTION) ||
> +      s->method != HTTP_WKSIDX_GET ||
> +      s->hdr_info.client_request.version_get() < HTTPVersion(1, 1)) {
> +    return false;
> +  }
> +
> +  MIMEField *upgrade_hdr = s->hdr_info.client_request.field_find(MIME_FIELD_UPGRADE, MIME_LEN_UPGRADE);
> +  MIMEField *connection_hdr = s->hdr_info.client_request.field_find(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION);
> +
> +  StrList connection_hdr_vals;
> +  const char *upgrade_hdr_val = NULL;
> +  int upgrade_hdr_val_len = 0;
> +
> +  if ( !upgrade_hdr ||
> +       !connection_hdr ||
> +       connection_hdr->value_get_comma_list(&connection_hdr_vals) == 0 ||
> +       (upgrade_hdr_val = upgrade_hdr->value_get(&upgrade_hdr_val_len)) == NULL) {
> +      DebugTxn("http_trans_upgrade", "Transaction wasn't a valid upgrade request, proceeding as a normal HTTP request.");
> +      return false;
> +  }
> +
> +  /*
> +   * In order for this request to be treated as a normal upgrade request we must have a Connection: Upgrade header
> +   * and a Upgrade: header, with a non-empty value, otherwise we just assume it's not an Upgrade Request, after
> +   * we've verified that, we will try to match this upgrade to a known upgrade type such as Websockets.
> +   */
> +  bool connection_contains_upgrade = false;
> +  // Next, let's validate that the Connection header contains an Upgrade key
> +  for(int i = 0; i < connection_hdr_vals.count; ++i) {
> +    Str *val = connection_hdr_vals.get_idx(i);
> +    if (ptr_len_casecmp(val->str, val->len, MIME_FIELD_UPGRADE, MIME_LEN_UPGRADE) == 0) {
> +      connection_contains_upgrade = true;
> +      break;
> +    }
> +  }
> +
> +  if (!connection_contains_upgrade) {
> +    DebugTxn("http_trans_upgrade", "Transaction wasn't a valid upgrade request, proceeding as a normal HTTP request, missing Connection upgrade header.");
> +    return false;
> +  }
> +
> +
> +  // Mark this request as an upgrade request.
> +  s->is_upgrade_request = true;
> +
> +  /*
> +     RFC 6455
> +     The request MUST contain an |Upgrade| header field whose value
> +        MUST include the "websocket" keyword.
> +     The request MUST contain a |Connection| header field whose value
> +        MUST include the "Upgrade" token. // Checked Above
> +     The request MUST include a header field with the name
> +        |Sec-WebSocket-Key|.
> +     The request MUST include a header field with the name
> +        |Sec-WebSocket-Version|.  The value of this header field MUST be
> +        13.
> +   */
> +  if (hdrtoken_tokenize(upgrade_hdr_val, upgrade_hdr_val_len, &s->upgrade_token_wks) >= 0) {
> +    if (s->upgrade_token_wks == MIME_VALUE_WEBSOCKET) {
> +      MIMEField *sec_websocket_key = s->hdr_info.client_request.field_find(MIME_FIELD_SEC_WEBSOCKET_KEY, MIME_LEN_SEC_WEBSOCKET_KEY);
> +      MIMEField *sec_websocket_ver = s->hdr_info.client_request.field_find(MIME_FIELD_SEC_WEBSOCKET_VERSION, MIME_LEN_SEC_WEBSOCKET_VERSION);
> +
> +      if (sec_websocket_key &&
> +          sec_websocket_ver &&
> +          sec_websocket_ver->value_get_int() == 13) {
> +        DebugTxn("http_trans_upgrade", "Transaction wants upgrade to websockets");
> +        handle_websocket_upgrade_pre_remap(s);
> +        return true;
> +      } else {
> +        DebugTxn("http_trans_upgrade", "Unable to upgrade connection to websockets, invalid headers (RFC 6455).");
> +      }
> +    }
> +  } else {
> +    DebugTxn("http_trans_upgrade", "Transaction requested upgrade for unknown protocol: %s", upgrade_hdr_val);
> +  }
> +
> +  build_error_response(s, HTTP_STATUS_BAD_REQUEST, "Invalid Upgrade Request", "request#syntax_error",
> +                       "Invalid Upgrade Request");
> +
> +  // we want our modify_request method to just return while we fail out from here.
> +  // this seems like the preferred option because the user wanted to do an upgrade but sent a bad protocol.
> +  TRANSACT_RETURN_VAL(PROXY_SEND_ERROR_CACHE_NOOP, NULL, true);
> +}
> +
> +void
> +HttpTransact::handle_websocket_upgrade_pre_remap(State *s) {
> +  DebugTxn("http_trans_websocket_upgrade_pre_remap", "Prepping transaction before remap.");
> +
> +  /*
> +   * We will use this opportunity to set everything up so that during the remap stage we can deal with
> +   * ws:// and wss:// remap rules, and then we will take over again post remap.
> +   */
> +  s->is_websocket = true;
> +  s->post_remap_upgrade_return_point = HttpTransact::handle_websocket_upgrade_post_remap;
> +
> +  /* let's modify the url scheme to be wss or ws, so remapping will happen as expected */
> +  URL *url = s->hdr_info.client_request.url_get();
> +  if (url->scheme_get_wksidx() == URL_WKSIDX_HTTP) {
> +    DebugTxn("http_trans_websocket_upgrade_pre_remap", "Changing scheme to WS for remapping.");
> +    url->scheme_set(URL_SCHEME_WS, URL_LEN_WS);
> +  } else if (url->scheme_get_wksidx() == URL_WKSIDX_HTTPS) {
> +    DebugTxn("http_trans_websocket_upgrade_pre_remap", "Changing scheme to WSS for remapping.");
> +    url->scheme_set(URL_SCHEME_WSS, URL_LEN_WSS);
> +  } else {
> +    DebugTxn("http_trans_websocket_upgrade_pre_remap", "Invalid scheme for websocket upgrade");
> +    build_error_response(s, HTTP_STATUS_BAD_REQUEST, "Invalid Upgrade Request", "request#syntax_error",
> +                          "Invalid Upgrade Request");
> +    TRANSACT_RETURN(PROXY_SEND_ERROR_CACHE_NOOP, NULL);
> +  }
> +
> +  TRANSACT_RETURN(HTTP_API_READ_REQUEST_HDR, HttpTransact::StartRemapRequest);
> +}
> +
> +void
> +HttpTransact::handle_websocket_upgrade_post_remap(State *s) {
> +  DebugTxn("http_trans_websocket_upgrade_post_remap", "Remap is complete, start websocket upgrade");
> +
> +  TRANSACT_RETURN(HTTP_API_POST_REMAP, HttpTransact::handle_websocket_connection);
> +}
> +
> +void
> +HttpTransact::handle_websocket_connection(State *s) {
> +  DebugTxn("http_trans_websocket", "START handle_websocket_connection");
> +
> +  HandleRequest(s);
> +}
> +
> +
> void
> HttpTransact::ModifyRequest(State* s)
> {
> @@ -1078,6 +1221,13 @@ HttpTransact::ModifyRequest(State* s)
> 
>   DebugTxn("http_trans", "END HttpTransact::ModifyRequest");
> 
> +  DebugTxn("http_trans", "Checking if transaction wants to upgrade");
> +  if(handle_upgrade_request(s)) {
> +    // everything should be handled by the upgrade handler.
> +    DebugTxn("http_trans", "Transaction will be upgraded by the appropriate upgrade handler.");
> +    return;
> +  }
> +
>   TRANSACT_RETURN(HTTP_API_READ_REQUEST_HDR, HttpTransact::StartRemapRequest);
> }
> 
> @@ -1129,7 +1279,6 @@ HttpTransact::handleIfRedirect(State *s)
>   return false;
> }
> 
> -
> void
> HttpTransact::HandleRequest(State* s)
> {
> @@ -1158,6 +1307,7 @@ HttpTransact::HandleRequest(State* s)
>   if (is_debug_tag_set("http_chdr_describe")) {
>     obj_describe(s->hdr_info.client_request.m_http, 1);
>   }
> +
>   // at this point we are guaranteed that the request is good and acceptable.
>   // initialize some state variables from the request (client version,
>   // client keep-alive, cache action, etc.
> @@ -1251,6 +1401,7 @@ HttpTransact::HandleRequest(State* s)
>       TRANSACT_RETURN(HttpTransact::PROXY_SEND_ERROR_CACHE_NOOP, NULL);
>     }
>   }
> +
>   // Added to skip the dns if the document is in the cache.
>   // DNS is requested before cache lookup only if there are rules in cache.config , parent.config or
>   // if the newly added varible doc_in_cache_skip_dns is not enabled
> @@ -5142,7 +5293,8 @@ HttpTransact::RequestError_t HttpTransact::check_request_validity(State* s, HTTP
> 
>   if (!((scheme == URL_WKSIDX_HTTP) && (method == HTTP_WKSIDX_GET))) {
>     if (scheme != URL_WKSIDX_HTTP && scheme != URL_WKSIDX_HTTPS &&
> -        method != HTTP_WKSIDX_CONNECT) {
> +        method != HTTP_WKSIDX_CONNECT &&
> +        ((scheme == URL_WKSIDX_WS || scheme == URL_WKSIDX_WSS) && !s->is_websocket)) {
>       if (scheme < 0) {
>         return NO_REQUEST_SCHEME;
>       } else {
> @@ -5471,6 +5623,31 @@ HttpTransact::initialize_state_variables_from_request(State* s, HTTPHdr* obsolet
>   }
> 
>   s->next_hop_scheme = s->scheme = incoming_request->url_get()->scheme_get_wksidx();
> +
> +  // With websockets we need to make an outgoing request
> +  // as http or https.
> +  // We switch back to HTTP or HTTPS for the next hop
> +  // I think this is required to properly establish outbound WSS connections,
> +  // you'll need to force the next hop to be https.
> +  if (s->is_websocket) {
> +    if (s->next_hop_scheme == URL_WKSIDX_WS) {
> +      DebugTxn("http_trans", "Switching WS next hop scheme to http.");
> +      s->next_hop_scheme = URL_WKSIDX_HTTP;
> +      s->scheme = URL_WKSIDX_HTTP;
> +      //s->request_data.hdr->url_get()->scheme_set(URL_SCHEME_HTTP, URL_LEN_HTTP);
> +    } else if (s->next_hop_scheme == URL_WKSIDX_WSS) {
> +      DebugTxn("http_trans", "Switching WSS next hop scheme to https.");
> +      s->next_hop_scheme = URL_WKSIDX_HTTPS;
> +      s->scheme = URL_WKSIDX_HTTPS;
> +      //s->request_data.hdr->url_get()->scheme_set(URL_SCHEME_HTTPS, URL_LEN_HTTPS);
> +    } else {
> +      Error("Scheme doesn't match websocket...!");
> +    }
> +
> +    s->current.mode = GENERIC_PROXY;
> +    s->cache_info.action = CACHE_DO_NO_ACTION;
> +  }
> +
>   s->method = incoming_request->method_get_wksidx();
> 
>   if (s->method == HTTP_WKSIDX_GET) {
> @@ -5520,6 +5697,7 @@ HttpTransact::initialize_state_variables_from_request(State* s, HTTPHdr* obsolet
>     s->hdr_info.request_content_length = HTTP_UNDEFINED_CL;
>   }
>   s->request_data.hdr = &s->hdr_info.client_request;
> +
>   s->request_data.hostname_str = s->arena.str_store(host_name, host_len);
>   ats_ip_copy(&s->request_data.src_ip, &s->client_info.addr);
>   memset(&s->request_data.dest_ip, 0, sizeof(s->request_data.dest_ip));
> @@ -5570,6 +5748,11 @@ HttpTransact::initialize_state_variables_from_response(State* s, HTTPHdr* incomi
>   s->current.server->keep_alive = is_header_keep_alive(s->hdr_info.server_response.version_get(),
>                                                        s->hdr_info.server_request.version_get(), c_hdr);
> 
> +  // Don't allow an upgrade request to Keep Alive
> +  if (s->is_upgrade_request) {
> +    s->current.server->keep_alive = HTTP_NO_KEEPALIVE;
> +  }
> +
>   if (s->current.server->keep_alive == HTTP_KEEPALIVE) {
>     if (!s->cop_test_page)
>       DebugTxn("http_hdrs", "[initialize_state_variables_from_response]" "Server is keep-alive.");
> @@ -6668,44 +6851,53 @@ HttpTransact::handle_request_keep_alive_headers(State* s, HTTPVersion ver, HTTPH
> 
>   // Since connection headers are hop-to-hop, strip the
>   //  the ones we received from the user-agent
> -  heads->field_delete(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION);
>   heads->field_delete(MIME_FIELD_PROXY_CONNECTION, MIME_LEN_PROXY_CONNECTION);
> +  heads->field_delete(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION);
> 
> -
> -  // Insert K-A headers as necessary
> -  switch (ka_action) {
> -  case KA_CONNECTION:
> -    ink_assert(s->current.server->keep_alive != HTTP_NO_KEEPALIVE);
> -    if (ver == HTTPVersion(1, 0)) {
> -      if (s->current.request_to == PARENT_PROXY ||
> -          s->current.request_to == ICP_SUGGESTED_HOST) {
> -        heads->value_set(MIME_FIELD_PROXY_CONNECTION, MIME_LEN_PROXY_CONNECTION, "keep-alive", 10);
> -      } else {
> -        heads->value_set(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION, "keep-alive", 10);
> +  if (!s->is_upgrade_request) {
> +    // Insert K-A headers as necessary
> +    switch (ka_action) {
> +    case KA_CONNECTION:
> +      ink_assert(s->current.server->keep_alive != HTTP_NO_KEEPALIVE);
> +      if (ver == HTTPVersion(1, 0)) {
> +        if (s->current.request_to == PARENT_PROXY ||
> +            s->current.request_to == ICP_SUGGESTED_HOST) {
> +          heads->value_set(MIME_FIELD_PROXY_CONNECTION, MIME_LEN_PROXY_CONNECTION, "keep-alive", 10);
> +        } else {
> +          heads->value_set(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION, "keep-alive", 10);
> +        }
>       }
> -    }
> -    // NOTE: if the version is 1.1 we don't need to do
> -    //  anything since keep-alive is assumed
> -    break;
> -  case KA_DISABLED:
> -  case KA_CLOSE:
> -    if (s->current.server->keep_alive != HTTP_NO_KEEPALIVE || (ver == HTTPVersion(1, 1))) {
> -      /* Had keep-alive */
> -      s->current.server->keep_alive = HTTP_NO_KEEPALIVE;
> -      if (s->current.request_to == PARENT_PROXY ||
> -          s->current.request_to == ICP_SUGGESTED_HOST) {
> -        heads->value_set(MIME_FIELD_PROXY_CONNECTION, MIME_LEN_PROXY_CONNECTION, "close", 5);
> -      } else {
> -        heads->value_set(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION, "close", 5);
> +      // NOTE: if the version is 1.1 we don't need to do
> +      //  anything since keep-alive is assumed
> +      break;
> +    case KA_DISABLED:
> +    case KA_CLOSE:
> +      if (s->current.server->keep_alive != HTTP_NO_KEEPALIVE || (ver == HTTPVersion(1, 1))) {
> +        /* Had keep-alive */
> +        s->current.server->keep_alive = HTTP_NO_KEEPALIVE;
> +        if (s->current.request_to == PARENT_PROXY ||
> +            s->current.request_to == ICP_SUGGESTED_HOST) {
> +          heads->value_set(MIME_FIELD_PROXY_CONNECTION, MIME_LEN_PROXY_CONNECTION, "close", 5);
> +        } else {
> +          heads->value_set(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION, "close", 5);
> +        }
>       }
> +      // Note: if we are 1.1, we always need to send the close
> +      //  header since persistant connnections are the default
> +      break;
> +    case KA_UNKNOWN:
> +    default:
> +      ink_assert(0);
> +      break;
> +    }
> +  } else { /* websocket connection */
> +    s->current.server->keep_alive = HTTP_NO_KEEPALIVE;
> +    s->client_info.keep_alive = HTTP_NO_KEEPALIVE;
> +    heads->value_set(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION, MIME_FIELD_UPGRADE, MIME_LEN_UPGRADE);
> +
> +    if (s->is_websocket) {
> +      heads->value_set(MIME_FIELD_UPGRADE, MIME_LEN_UPGRADE, "websocket", 9);
>     }
> -    // Note: if we are 1.1, we always need to send the close
> -    //  header since persistant connnections are the default
> -    break;
> -  case KA_UNKNOWN:
> -  default:
> -    ink_assert(0);
> -    break;
>   }
> }                               /* End HttpTransact::handle_request_keep_alive_headers */
> 
> @@ -6736,6 +6928,24 @@ HttpTransact::handle_response_keep_alive_headers(State* s, HTTPVersion ver, HTTP
>   heads->field_delete(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION);
>   heads->field_delete(MIME_FIELD_PROXY_CONNECTION, MIME_LEN_PROXY_CONNECTION);
> 
> +  // Handle the upgrade cases
> +  if (s->is_upgrade_request  &&
> +      heads->status_get() == HTTP_STATUS_SWITCHING_PROTOCOL &&
> +      s->source == SOURCE_HTTP_ORIGIN_SERVER) {
> +    s->client_info.keep_alive = HTTP_NO_KEEPALIVE;
> +    if (s->is_websocket) {
> +      DebugTxn("http_trans", "transaction successfully upgraded to websockets.");
> +      //s->transparent_passthrough = true;
> +      heads->value_set(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION, MIME_FIELD_UPGRADE, MIME_LEN_UPGRADE);
> +      heads->value_set(MIME_FIELD_UPGRADE, MIME_LEN_UPGRADE, "websocket", 9);
> +    }
> +
> +    // We set this state so that we can jump to our blind forwarding state once
> +    // the response is sent to the client.
> +    s->did_upgrade_succeed = true;
> +    return;
> +  }
> +
>   int c_hdr_field_len;
>   const char *c_hdr_field_str;
>   if (s->client_info.proxy_connect_hdr) {
> @@ -7585,7 +7795,9 @@ HttpTransact::build_request(State* s, HTTPHdr* base_request, HTTPHdr* outgoing_r
>   }
> 
>   if (s->current.server == &s->server_info &&
> -      (s->next_hop_scheme == URL_WKSIDX_HTTP || s->next_hop_scheme == URL_WKSIDX_HTTPS)) {
> +      (s->next_hop_scheme == URL_WKSIDX_HTTP || s->next_hop_scheme == URL_WKSIDX_HTTPS ||
> +       s->next_hop_scheme == URL_WKSIDX_WS || s->next_hop_scheme == URL_WKSIDX_WSS)) {
> +    DebugTxn("http_trans", "[build_request] removing host name from url");
>     HttpTransactHeaders::remove_host_name_from_url(outgoing_request);
>   }
> 
> 
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/cca82f1b/proxy/http/HttpTransact.h
> ----------------------------------------------------------------------
> diff --git a/proxy/http/HttpTransact.h b/proxy/http/HttpTransact.h
> index ad3152b..21d8a40 100644
> --- a/proxy/http/HttpTransact.h
> +++ b/proxy/http/HttpTransact.h
> @@ -79,12 +79,20 @@
> }
> 
> 
> -#define TRANSACT_RETURN(n, r)  \
> +#define TRANSACT_SETUP_RETURN(n, r) \
> s->next_action = n; \
> s->transact_return_point = r; \
> DebugSpecific((s->state_machine && s->state_machine->debug_on), "http_trans", "Next action %s; %s", #n, #r); \
> +
> +#define TRANSACT_RETURN(n, r)  \
> +TRANSACT_SETUP_RETURN(n, r) \
> return; \
> 
> +#define TRANSACT_RETURN_VAL(n, r, v) \
> +TRANSACT_SETUP_RETURN(n, r) \
> +return v; \
> +
> +
> #define SET_UNPREPARE_CACHE_ACTION(C) \
> { \
>     if (C.action == HttpTransact::CACHE_PREPARE_TO_DELETE) { \
> @@ -499,7 +507,8 @@ public:
>     HTTP_REMAP_REQUEST,
>     HTTP_API_POST_REMAP,
>     HTTP_POST_REMAP_SKIP,
> -    
> +    HTTP_POST_REMAP_UPGRADE,
> +
>     HTTP_API_OS_DNS,
>     HTTP_API_SEND_REQUEST_HDR,
>     HTTP_API_READ_CACHE_HDR,
> @@ -894,6 +903,16 @@ public:
>     StateMachineAction_t next_action;   // out
>     StateMachineAction_t api_next_action;       // out
>     void (*transact_return_point) (HttpTransact::State* s);    // out
> +
> +    // We keep this so we can jump back to the upgrade handler after remap is complete
> +    bool is_upgrade_request;
> +    void (*post_remap_upgrade_return_point) (HttpTransact::State* s);    // out
> +    const char *upgrade_token_wks;
> +
> +    // Some WebSocket state
> +    bool is_websocket;
> +    bool did_upgrade_succeed;
> +
>     char *internal_msg_buffer;  // out
>     char *internal_msg_buffer_type;     // out
>     int64_t internal_msg_buffer_size;       // out
> @@ -1031,6 +1050,11 @@ public:
>         next_action(STATE_MACHINE_ACTION_UNDEFINED),
>         api_next_action(STATE_MACHINE_ACTION_UNDEFINED),
>         transact_return_point(NULL),
> +        is_upgrade_request(false),
> +        post_remap_upgrade_return_point(NULL),
> +        upgrade_token_wks(NULL),
> +        is_websocket(false),
> +        did_upgrade_succeed(false),
>         internal_msg_buffer(0),
>         internal_msg_buffer_type(0),
>         internal_msg_buffer_size(0),
> @@ -1259,6 +1283,10 @@ public:
>   static void merge_warning_header(HTTPHdr* cached_header, HTTPHdr* response_header);
>   static void SetCacheFreshnessLimit(State* s);
>   static void HandleApiErrorJump(State *);
> +  static void handle_websocket_upgrade_pre_remap(State *s);
> +  static void handle_websocket_upgrade_post_remap(State *s);
> +  static bool handle_upgrade_request(State *s);
> +  static void handle_websocket_connection(State *s);
> 
>   static void HandleCacheOpenReadPush(State* s, bool read_successful);
>   static void HandlePushResponseHdr(State* s);
> @@ -1298,6 +1326,7 @@ public:
>   static bool is_request_cache_lookupable(State* s);
>   static bool is_request_valid(State* s, HTTPHdr* incoming_request);
>   static bool is_request_retryable(State* s);
> +
>   static bool is_response_cacheable(State* s, HTTPHdr* request, HTTPHdr* response);
>   static bool is_response_valid(State* s, HTTPHdr* incoming_response);
> 
> 
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/cca82f1b/proxy/http/HttpTransactHeaders.cc
> ----------------------------------------------------------------------
> diff --git a/proxy/http/HttpTransactHeaders.cc b/proxy/http/HttpTransactHeaders.cc
> index b5ab0fe..0cebb0f 100644
> --- a/proxy/http/HttpTransactHeaders.cc
> +++ b/proxy/http/HttpTransactHeaders.cc
> @@ -68,9 +68,12 @@ HttpTransactHeaders::is_this_method_supported(int the_scheme, int the_method)
> {
>   if (the_method == HTTP_WKSIDX_CONNECT) {
>     return true;
> -  } else if (the_scheme == URL_WKSIDX_HTTP || the_scheme == URL_WKSIDX_HTTPS)
> +  } else if (the_scheme == URL_WKSIDX_HTTP || the_scheme == URL_WKSIDX_HTTPS) {
>     return is_this_http_method_supported(the_method);
> -  else
> +  } else if ((the_scheme == URL_WKSIDX_WS || the_scheme == URL_WKSIDX_WSS) &&
> +            the_method == HTTP_WKSIDX_GET) {
> +    return true;
> +  } else
>     return false;
> }
> 
> 
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/cca82f1b/proxy/http/remap/RemapConfig.cc
> ----------------------------------------------------------------------
> diff --git a/proxy/http/remap/RemapConfig.cc b/proxy/http/remap/RemapConfig.cc
> index 787fbb0..c138557 100644
> --- a/proxy/http/remap/RemapConfig.cc
> +++ b/proxy/http/remap/RemapConfig.cc
> @@ -1082,12 +1082,23 @@ remap_parse_config_bti(const char * path, BUILD_TABLE_INFO * bti)
>     // includes support for FILE scheme
>     if ((fromScheme != URL_SCHEME_HTTP && fromScheme != URL_SCHEME_HTTPS &&
>          fromScheme != URL_SCHEME_FILE &&
> -         fromScheme != URL_SCHEME_TUNNEL) ||
> +         fromScheme != URL_SCHEME_TUNNEL &&
> +         fromScheme != URL_SCHEME_WS &&
> +         fromScheme != URL_SCHEME_WSS) ||
>         (toScheme != URL_SCHEME_HTTP && toScheme != URL_SCHEME_HTTPS &&
> -         toScheme != URL_SCHEME_TUNNEL)) {
> -      errStr = "Only http, https, and tunnel remappings are supported";
> +         toScheme != URL_SCHEME_TUNNEL && toScheme != URL_SCHEME_WS &&
> +         toScheme != URL_SCHEME_WSS)) {
> +      errStr = "Only http, https, ws, wss, and tunnel remappings are supported";
>       goto MAP_ERROR;
>     }
> +
> +    // If mapping from WS or WSS we must map out to WS or WSS
> +    if ( (fromScheme == URL_SCHEME_WSS || fromScheme == URL_SCHEME_WS) &&
> +         (toScheme != URL_SCHEME_WSS && toScheme != URL_SCHEME_WS)) {
> +      errStr = "WS or WSS can only be mapped out to WS or WSS.";
> +      goto MAP_ERROR;
> +    }
> +
>     // Check if a tag is specified.
>     if (bti->paramv[3] != NULL) {
>       if (maptype == FORWARD_MAP_REFERER) {
>