You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by br...@apache.org on 2014/02/03 20:11:43 UTC
[1/2] git commit: [TS-2541] Add WebSocket Support
Updated Branches:
refs/heads/master f0f9cb9c3 -> 60ec95053
[TS-2541] Add WebSocket Support
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) {
Re: [1/2] git commit: [TS-2541] Add WebSocket Support
Posted by James Peach <jp...@apache.org>.
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) {
>
Re: [1/2] git commit: [TS-2541] Add WebSocket Support
Posted by James Peach <jp...@apache.org>.
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) {
>
[2/2] git commit: [TS-2541] Add WebSocket Support
Posted by br...@apache.org.
[TS-2541] Add WebSocket Support
Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo
Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/60ec9505
Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/60ec9505
Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/60ec9505
Branch: refs/heads/master
Commit: 60ec950539ab9f5b138c41bfddb2008128773787
Parents: cca82f1 f0f9cb9
Author: Brian Geffon <br...@apache.org>
Authored: Mon Feb 3 11:11:26 2014 -0800
Committer: Brian Geffon <br...@apache.org>
Committed: Mon Feb 3 11:11:26 2014 -0800
----------------------------------------------------------------------
----------------------------------------------------------------------