<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[199590] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/199590">199590</a></dd>
<dt>Author</dt> <dd>commit-queue@webkit.org</dd>
<dt>Date</dt> <dd>2016-04-15 09:39:00 -0700 (Fri, 15 Apr 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>Refactor WebSockets handshake to use StringView instead of String for header validation.
https://bugs.webkit.org/show_bug.cgi?id=155602

Patch by John Wilander &lt;wilander@apple.com&gt; on 2016-04-15
Reviewed by Darin Adler.

Source/WebCore:

No new tests. Existing test have been augmented.

* Modules/websockets/WebSocketHandshake.cpp:
(WebCore::WebSocketHandshake::readServerHandshake):
    Made sure failure reason was set consistently with makeString().
(WebCore::headerHasValidHTTPVersion):
    Now operates on the HTTP status line with StringView.
(WebCore::WebSocketHandshake::readStatusLine):
    Now operates on the HTTP status line with StringView.
(WebCore::WebSocketHandshake::readHTTPHeaders):
    Now operates on header names with StringView.
    Made sure failure reason was set consistently with makeString() and ASCIILiteral().
(WebCore::WebSocketHandshake::checkResponseHeaders):
    Made sure failure reason was set consistently with ASCIILiteral().
* platform/network/HTTPParsers.cpp:
(WebCore::parseHTTPRequestLine):
    Made sure failure reason was set consistently with ASCIILiteral().
(WebCore::isValidHeaderNameCharacter):
    Inlined function to check if a character is allowed in an HTTP header name according to RFC 7230.
    https://tools.ietf.org/html/rfc7230 (June 2014)
(WebCore::parseHTTPHeader):
* platform/network/HTTPParsers.h:
    Now receives the HTTP header name as a StringView.
    Checks that header names only contain valid characters according to RFC 7230 (see above).
* platform/network/ResourceRequestBase.cpp:
(WebCore::ResourceRequestBase::addHTTPHeaderField):
* platform/network/ResourceRequestBase.h:
     Now has an overloaded function which receives the HTTP header name as an HTTPHeaderName enum value.
* platform/network/ResourceResponseBase.cpp:
(WebCore::ResourceResponseBase::addHTTPHeaderField):
* platform/network/ResourceResponseBase.h:
     Now has an overloaded function which receives the HTTP header name as an HTTPHeaderName enum value.

Source/WebKit2:

* UIProcess/InspectorServer/HTTPRequest.cpp:
(WebKit::HTTPRequest::parseHeaders):
    Now declares the HTTP header name as a StringView to match the change in WebCore::parseHTTPHeader.

LayoutTests:

* http/tests/websocket/tests/hybi/bad-handshake-crash-expected.txt:
    Fixed so that new error output is expected.
* http/tests/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1-expected.txt:
* http/tests/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1.html:
* http/tests/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1_wsh.py:
    Now tests HTTP versions that are higher than 1.1, are lower than 1.1, have bad characters, and are empty.
* http/tests/websocket/tests/hybi/long-invalid-header-expected.txt:
    Fixed so that slightly refined error output is expected.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestshttptestswebsockettestshybibadhandshakecrashexpectedtxt">trunk/LayoutTests/http/tests/websocket/tests/hybi/bad-handshake-crash-expected.txt</a></li>
<li><a href="#trunkLayoutTestshttptestswebsockettestshybihandshakeokwithhttpversionbeyond1_1expectedtxt">trunk/LayoutTests/http/tests/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1-expected.txt</a></li>
<li><a href="#trunkLayoutTestshttptestswebsockettestshybihandshakeokwithhttpversionbeyond1_1html">trunk/LayoutTests/http/tests/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1.html</a></li>
<li><a href="#trunkLayoutTestshttptestswebsockettestshybihandshakeokwithhttpversionbeyond1_1_wshpy">trunk/LayoutTests/http/tests/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1_wsh.py</a></li>
<li><a href="#trunkLayoutTestshttptestswebsockettestshybilonginvalidheaderexpectedtxt">trunk/LayoutTests/http/tests/websocket/tests/hybi/long-invalid-header-expected.txt</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreModuleswebsocketsWebSocketHandshakecpp">trunk/Source/WebCore/Modules/websockets/WebSocketHandshake.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformnetworkHTTPParserscpp">trunk/Source/WebCore/platform/network/HTTPParsers.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformnetworkHTTPParsersh">trunk/Source/WebCore/platform/network/HTTPParsers.h</a></li>
<li><a href="#trunkSourceWebCoreplatformnetworkResourceRequestBasecpp">trunk/Source/WebCore/platform/network/ResourceRequestBase.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformnetworkResourceRequestBaseh">trunk/Source/WebCore/platform/network/ResourceRequestBase.h</a></li>
<li><a href="#trunkSourceWebCoreplatformnetworkResourceResponseBasecpp">trunk/Source/WebCore/platform/network/ResourceResponseBase.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformnetworkResourceResponseBaseh">trunk/Source/WebCore/platform/network/ResourceResponseBase.h</a></li>
<li><a href="#trunkSourceWebKit2ChangeLog">trunk/Source/WebKit2/ChangeLog</a></li>
<li><a href="#trunkSourceWebKit2UIProcessInspectorServerHTTPRequestcpp">trunk/Source/WebKit2/UIProcess/InspectorServer/HTTPRequest.cpp</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (199589 => 199590)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2016-04-15 16:18:37 UTC (rev 199589)
+++ trunk/LayoutTests/ChangeLog        2016-04-15 16:39:00 UTC (rev 199590)
</span><span class="lines">@@ -1,3 +1,19 @@
</span><ins>+2016-04-15  John Wilander  &lt;wilander@apple.com&gt;
+
+        Refactor WebSockets handshake to use StringView instead of String for header validation.
+        https://bugs.webkit.org/show_bug.cgi?id=155602
+
+        Reviewed by Darin Adler.
+
+        * http/tests/websocket/tests/hybi/bad-handshake-crash-expected.txt:
+            Fixed so that new error output is expected.
+        * http/tests/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1-expected.txt:
+        * http/tests/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1.html:
+        * http/tests/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1_wsh.py:
+            Now tests HTTP versions that are higher than 1.1, are lower than 1.1, have bad characters, and are empty.
+        * http/tests/websocket/tests/hybi/long-invalid-header-expected.txt:
+            Fixed so that slightly refined error output is expected.
+
</ins><span class="cx"> 2016-04-15  Joanmarie Diggs  &lt;jdiggs@igalia.com&gt;
</span><span class="cx"> 
</span><span class="cx">         AX: Presentational role on SVG elements is trumped by child 'title' and 'desc' elements
</span></span></pre></div>
<a id="trunkLayoutTestshttptestswebsockettestshybibadhandshakecrashexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/http/tests/websocket/tests/hybi/bad-handshake-crash-expected.txt (199589 => 199590)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/websocket/tests/hybi/bad-handshake-crash-expected.txt        2016-04-15 16:18:37 UTC (rev 199589)
+++ trunk/LayoutTests/http/tests/websocket/tests/hybi/bad-handshake-crash-expected.txt        2016-04-15 16:39:00 UTC (rev 199590)
</span><span class="lines">@@ -1,4 +1,4 @@
</span><del>-CONSOLE MESSAGE: WebSocket connection to 'ws://127.0.0.1:8880/websocket/tests/hybi/bad-handshake-crash' failed: Invalid UTF-8 sequence in header name
</del><ins>+CONSOLE MESSAGE: WebSocket connection to 'ws://127.0.0.1:8880/websocket/tests/hybi/bad-handshake-crash' failed: Unexpected start character in header name
</ins><span class="cx"> Make sure WebSocket doesn't crash with bad handshake message.
</span><span class="cx"> 
</span><span class="cx"> On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
</span></span></pre></div>
<a id="trunkLayoutTestshttptestswebsockettestshybihandshakeokwithhttpversionbeyond1_1expectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/http/tests/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1-expected.txt (199589 => 199590)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1-expected.txt        2016-04-15 16:18:37 UTC (rev 199589)
+++ trunk/LayoutTests/http/tests/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1-expected.txt        2016-04-15 16:39:00 UTC (rev 199590)
</span><span class="lines">@@ -1,8 +1,16 @@
</span><del>-Test whether WebSocket handshake is OK with if server sends status line with http version beyond 1.1.
</del><ins>+CONSOLE MESSAGE: WebSocket connection to 'ws://localhost:8880/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1?01.00' failed: Invalid HTTP version string: HTTP/01.00
+CONSOLE MESSAGE: WebSocket connection to 'ws://localhost:8880/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1?000.99' failed: Invalid HTTP version string: HTTP/000.99
+CONSOLE MESSAGE: WebSocket connection to 'ws://localhost:8880/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1?0.00' failed: Invalid HTTP version string: HTTP/0.00
+CONSOLE MESSAGE: WebSocket connection to 'ws://localhost:8880/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1?-11.9' failed: Invalid HTTP version string: HTTP/-11.9
+CONSOLE MESSAGE: WebSocket connection to 'ws://localhost:8880/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1?0x1.0x00' failed: Invalid HTTP version string: HTTP/0x1.0x00
+CONSOLE MESSAGE: WebSocket connection to 'ws://localhost:8880/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1?%EF%A3%BF.1' failed: Invalid HTTP version string: HTTP/%EF%A3%BF.1
+CONSOLE MESSAGE: WebSocket connection to 'ws://localhost:8880/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1?' failed: Invalid HTTP version string: HTTP/
+CONSOLE MESSAGE: WebSocket connection to 'ws://localhost:8880/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1?1.1%EF%A3%BF' failed: Invalid HTTP version string: HTTP/1.1%EF%A3%BF
+Test http version parsing and validation. HTTP version 1.1 and above should be accepted for WebSockets.
</ins><span class="cx"> 
</span><span class="cx"> On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
</span><span class="cx"> 
</span><del>-PASS handshake_success is true
</del><ins>+PASS for all URLs.
</ins><span class="cx"> PASS successfullyParsed is true
</span><span class="cx"> 
</span><span class="cx"> TEST COMPLETE
</span></span></pre></div>
<a id="trunkLayoutTestshttptestswebsockettestshybihandshakeokwithhttpversionbeyond1_1html"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/http/tests/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1.html (199589 => 199590)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1.html        2016-04-15 16:18:37 UTC (rev 199589)
+++ trunk/LayoutTests/http/tests/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1.html        2016-04-15 16:39:00 UTC (rev 199590)
</span><span class="lines">@@ -7,31 +7,64 @@
</span><span class="cx"> &lt;div id=&quot;description&quot;&gt;&lt;/div&gt;
</span><span class="cx"> &lt;div id=&quot;console&quot;&gt;&lt;/div&gt;
</span><span class="cx"> &lt;script&gt;
</span><del>-    description(&quot;Test whether WebSocket handshake is OK with if server sends status line with http version beyond 1.1.&quot;);
</del><ins>+    description(&quot;Test http version parsing and validation. HTTP version 1.1 and above should be accepted for WebSockets.&quot;);
</ins><span class="cx"> 
</span><span class="cx">     window.jsTestIsAsync = true;
</span><span class="cx"> 
</span><del>-    var url = &quot;ws://localhost:8880/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1&quot;;
-    var handshake_success = false;
-    var ws = new WebSocket(url);
-    var closeEvent;
</del><ins>+    var testCases = [
+        { url : &quot;ws://localhost:8880/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1?1.11&quot;, shouldSucceed : true }
+        ,{ url : &quot;ws://localhost:8880/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1?11.0&quot;, shouldSucceed : true }
+        ,{ url : &quot;ws://localhost:8880/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1?001.01&quot;, shouldSucceed : true }
</ins><span class="cx"> 
</span><del>-    function endTest() {
-        clearTimeout(timeoutID);
-        shouldBeTrue(&quot;handshake_success&quot;);
-        finishJSTest();
</del><ins>+        ,{ url : &quot;ws://localhost:8880/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1?01.00&quot;, shouldSucceed : false }
+        ,{ url : &quot;ws://localhost:8880/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1?000.99&quot;, shouldSucceed : false }
+        ,{ url : &quot;ws://localhost:8880/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1?0.00&quot;, shouldSucceed : false }
+        ,{ url : &quot;ws://localhost:8880/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1?-11.9&quot;, shouldSucceed : false }
+        ,{ url : &quot;ws://localhost:8880/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1?0x1.0x00&quot;, shouldSucceed : false }
+        ,{ url : &quot;ws://localhost:8880/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1?.1&quot;, shouldSucceed : false }
+        ,{ url : &quot;ws://localhost:8880/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1?&quot;, shouldSucceed : false }
+        ,{ url : &quot;ws://localhost:8880/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1?1.1&quot;, shouldSucceed : false }
+    ];
+
+    var currentTest = 0;
+    var allTestsPassed = true;
+
+    function runATest () {
+        if (currentTest === testCases.length) {
+            if (!allTestsPassed) {
+                testFailed(&quot;At least one test failed.&quot;);
+            } else {
+                debug(&quot;PASS for all URLs.&quot;);
+            }
+            finishJSTest();
+        } else {
+            var testCase = testCases[currentTest++];
+            var ws = new WebSocket(testCase.url);
+
+            ws.onopen = (function(shouldSucceed, url) {
+                    return function () {
+                            if (!shouldSucceed) {
+                                debug(&quot;FAIL The following URL should have caused an error: &quot; + url);
+                                allTestsPassed = false;
+                            }
+                            runATest();
+                        };
+                })(testCase.shouldSucceed, testCase.url);
+
+            ws.onerror = (function(shouldSucceed, url) {
+                    return function () {
+                            if (shouldSucceed) {
+                                debug(&quot;FAIL The following URL should have been allowed to be opened: &quot; + url);
+                                allTestsPassed = false;
+                            }
+                            runATest();
+                        };
+                })(testCase.shouldSucceed, testCase.url);
+        }
</ins><span class="cx">     }
</span><del>-    ws.onopen = function() {
-        handshake_success = true;
-        ws.close();
-    };
</del><span class="cx"> 
</span><del>-    ws.onclose = function () {
-        endTest();
-    };
-    
-    var timeoutID = setTimeout(&quot;endTest()&quot;, 2000);
</del><ins>+    runATest();
</ins><span class="cx"> &lt;/script&gt;
</span><span class="cx"> &lt;script src=&quot;../../../../js-test-resources/js-test-post.js&quot;&gt;&lt;/script&gt;
</span><span class="cx"> &lt;/body&gt;
</span><del>-&lt;/html&gt;
</del><span class="cx">\ No newline at end of file
</span><ins>+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestshttptestswebsockettestshybihandshakeokwithhttpversionbeyond1_1_wshpy"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/http/tests/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1_wsh.py (199589 => 199590)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1_wsh.py        2016-04-15 16:18:37 UTC (rev 199589)
+++ trunk/LayoutTests/http/tests/websocket/tests/hybi/handshake-ok-with-http-version-beyond-1_1_wsh.py        2016-04-15 16:39:00 UTC (rev 199590)
</span><span class="lines">@@ -4,7 +4,14 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> def web_socket_do_extra_handshake(request):
</span><del>-    message = 'HTTP/3.0 101 Switching Protocols\r\n'
</del><ins>+
+    resources = request.ws_resource.split('?', 1)
+    parameter = None
+    if len(resources) == 2:
+        parameter = resources[1]
+        message = 'HTTP/'
+        message += parameter
+        message += ' 101 Switching Protocols\r\n'
</ins><span class="cx">     message += 'Upgrade: websocket\r\n'
</span><span class="cx">     message += 'Connection: Upgrade\r\n'
</span><span class="cx">     message += 'Sec-WebSocket-Accept: %s\r\n' % compute_accept(request.headers_in['Sec-WebSocket-Key'])[0]
</span></span></pre></div>
<a id="trunkLayoutTestshttptestswebsockettestshybilonginvalidheaderexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/http/tests/websocket/tests/hybi/long-invalid-header-expected.txt (199589 => 199590)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/websocket/tests/hybi/long-invalid-header-expected.txt        2016-04-15 16:18:37 UTC (rev 199589)
+++ trunk/LayoutTests/http/tests/websocket/tests/hybi/long-invalid-header-expected.txt        2016-04-15 16:39:00 UTC (rev 199590)
</span><span class="lines">@@ -1,4 +1,4 @@
</span><del>-CONSOLE MESSAGE: WebSocket connection to 'ws://127.0.0.1:8880/websocket/tests/hybi/long-invalid-header' failed: Unexpected CR in name at pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp…
</del><ins>+CONSOLE MESSAGE: WebSocket connection to 'ws://127.0.0.1:8880/websocket/tests/hybi/long-invalid-header' failed: Unexpected CR in header name at pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp…
</ins><span class="cx"> Make sure WebSocket gives errors on long invalid upgrade header.
</span><span class="cx"> 
</span><span class="cx"> On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (199589 => 199590)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2016-04-15 16:18:37 UTC (rev 199589)
+++ trunk/Source/WebCore/ChangeLog        2016-04-15 16:39:00 UTC (rev 199590)
</span><span class="lines">@@ -1,3 +1,43 @@
</span><ins>+2016-04-15  John Wilander  &lt;wilander@apple.com&gt;
+
+        Refactor WebSockets handshake to use StringView instead of String for header validation.
+        https://bugs.webkit.org/show_bug.cgi?id=155602
+
+        Reviewed by Darin Adler.
+
+        No new tests. Existing test have been augmented.
+
+        * Modules/websockets/WebSocketHandshake.cpp:
+        (WebCore::WebSocketHandshake::readServerHandshake):
+            Made sure failure reason was set consistently with makeString().
+        (WebCore::headerHasValidHTTPVersion):
+            Now operates on the HTTP status line with StringView.
+        (WebCore::WebSocketHandshake::readStatusLine):
+            Now operates on the HTTP status line with StringView.
+        (WebCore::WebSocketHandshake::readHTTPHeaders):
+            Now operates on header names with StringView.
+            Made sure failure reason was set consistently with makeString() and ASCIILiteral().
+        (WebCore::WebSocketHandshake::checkResponseHeaders):
+            Made sure failure reason was set consistently with ASCIILiteral().
+        * platform/network/HTTPParsers.cpp:
+        (WebCore::parseHTTPRequestLine):
+            Made sure failure reason was set consistently with ASCIILiteral().
+        (WebCore::isValidHeaderNameCharacter):
+            Inlined function to check if a character is allowed in an HTTP header name according to RFC 7230.
+            https://tools.ietf.org/html/rfc7230 (June 2014)
+        (WebCore::parseHTTPHeader):
+        * platform/network/HTTPParsers.h:
+            Now receives the HTTP header name as a StringView.
+            Checks that header names only contain valid characters according to RFC 7230 (see above).
+        * platform/network/ResourceRequestBase.cpp:
+        (WebCore::ResourceRequestBase::addHTTPHeaderField):
+        * platform/network/ResourceRequestBase.h:
+             Now has an overloaded function which receives the HTTP header name as an HTTPHeaderName enum value.
+        * platform/network/ResourceResponseBase.cpp:
+        (WebCore::ResourceResponseBase::addHTTPHeaderField):
+        * platform/network/ResourceResponseBase.h:
+             Now has an overloaded function which receives the HTTP header name as an HTTPHeaderName enum value.
+
</ins><span class="cx"> 2016-04-15  Joanmarie Diggs  &lt;jdiggs@igalia.com&gt;
</span><span class="cx"> 
</span><span class="cx">         AX: Presentational role on SVG elements is trumped by child 'title' and 'desc' elements
</span></span></pre></div>
<a id="trunkSourceWebCoreModuleswebsocketsWebSocketHandshakecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/websockets/WebSocketHandshake.cpp (199589 => 199590)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/websockets/WebSocketHandshake.cpp        2016-04-15 16:18:37 UTC (rev 199589)
+++ trunk/Source/WebCore/Modules/websockets/WebSocketHandshake.cpp        2016-04-15 16:39:00 UTC (rev 199590)
</span><span class="lines">@@ -50,6 +50,7 @@
</span><span class="cx"> #include &lt;wtf/ASCIICType.h&gt;
</span><span class="cx"> #include &lt;wtf/CryptographicallyRandomNumber.h&gt;
</span><span class="cx"> #include &lt;wtf/MD5.h&gt;
</span><ins>+#include &lt;wtf/NeverDestroyed.h&gt;
</ins><span class="cx"> #include &lt;wtf/SHA1.h&gt;
</span><span class="cx"> #include &lt;wtf/StdLibExtras.h&gt;
</span><span class="cx"> #include &lt;wtf/StringExtras.h&gt;
</span><span class="lines">@@ -304,7 +305,7 @@
</span><span class="cx"> 
</span><span class="cx">     if (statusCode != 101) {
</span><span class="cx">         m_mode = Failed;
</span><del>-        m_failureReason = &quot;Unexpected response code: &quot; + String::number(statusCode);
</del><ins>+        m_failureReason = makeString(&quot;Unexpected response code: &quot;, String::number(statusCode));
</ins><span class="cx">         return len;
</span><span class="cx">     }
</span><span class="cx">     m_mode = Normal;
</span><span class="lines">@@ -394,27 +395,39 @@
</span><span class="cx"> 
</span><span class="cx"> // https://tools.ietf.org/html/rfc6455#section-4.1
</span><span class="cx"> // &quot;The HTTP version MUST be at least 1.1.&quot;
</span><del>-static inline bool headerHasValidHTTPVersion(const String&amp; httpVersionString, const size_t headerLength)
</del><ins>+static inline bool headerHasValidHTTPVersion(StringView httpStatusLine)
</ins><span class="cx"> {
</span><del>-    const size_t posOfHttp = httpVersionString.find(&quot;HTTP/&quot;);
-    if (posOfHttp == notFound)
</del><ins>+    const char* httpVersionStaticPreambleLiteral = &quot;HTTP/&quot;;
+    StringView httpVersionStaticPreamble(reinterpret_cast&lt;const LChar*&gt;(httpVersionStaticPreambleLiteral), strlen(httpVersionStaticPreambleLiteral));
+    if (!httpStatusLine.startsWith(httpVersionStaticPreamble))
</ins><span class="cx">         return false;
</span><span class="cx"> 
</span><del>-    // Check that there is a version number which should be three characters after &quot;HTTP/&quot;
-    const size_t posOfHttpVersionNumberString = posOfHttp + 5;
-    if (posOfHttpVersionNumberString + 3 &gt;= headerLength)
</del><ins>+    // Check that there is a version number which should be at least three characters after &quot;HTTP/&quot;
+    unsigned preambleLength = httpVersionStaticPreamble.length();
+    if (httpStatusLine.length() &lt; preambleLength + 3)
</ins><span class="cx">         return false;
</span><span class="cx"> 
</span><del>-    // Check that the version number is 1.1 or above
-    bool versionNumberIsOneDotOneOrAbove = false;
-    if (httpVersionString.characterAt(posOfHttpVersionNumberString + 1) == '.') {
-        UChar majorVersion = httpVersionString.characterAt(posOfHttpVersionNumberString);
-        UChar minorVersion = httpVersionString.characterAt(posOfHttpVersionNumberString + 2);
-        if ((majorVersion == '1' &amp;&amp; minorVersion &gt;= '1' &amp;&amp; minorVersion &lt;= '9')
-            || (majorVersion &gt; '1' &amp;&amp; majorVersion &lt;= '9' &amp;&amp; minorVersion &gt;= '0' &amp;&amp; minorVersion &lt;= '9'))
-            versionNumberIsOneDotOneOrAbove = true;
</del><ins>+    auto dotPosition = httpStatusLine.find('.', preambleLength);
+    if (dotPosition == notFound)
+        return false;
+
+    StringView majorVersionView = httpStatusLine.substring(preambleLength, dotPosition - preambleLength);
+    bool isValid;
+    int majorVersion = majorVersionView.toIntStrict(isValid);
+    if (!isValid)
+        return false;
+
+    unsigned minorVersionLength;
+    unsigned charactersLeftAfterDotPosition = httpStatusLine.length() - dotPosition;
+    for (minorVersionLength = 1; minorVersionLength &lt; charactersLeftAfterDotPosition; minorVersionLength++) {
+        if (!isASCIIDigit(httpStatusLine[dotPosition + minorVersionLength]))
+            break;
</ins><span class="cx">     }
</span><del>-    return versionNumberIsOneDotOneOrAbove;
</del><ins>+    int minorVersion = (httpStatusLine.substring(dotPosition + 1, minorVersionLength)).toIntStrict(isValid);
+    if (!isValid)
+        return false;
+
+    return (majorVersion &gt;= 1 &amp;&amp; minorVersion &gt;= 1) || majorVersion &gt;= 2;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> // Returns the header length (including &quot;\r\n&quot;), or -1 if we have not received enough data yet.
</span><span class="lines">@@ -444,10 +457,10 @@
</span><span class="cx">             // The caller isn't prepared to deal with null bytes in status
</span><span class="cx">             // line. WebSockets specification doesn't prohibit this, but HTTP
</span><span class="cx">             // does, so we'll just treat this as an error.
</span><del>-            m_failureReason = &quot;Status line contains embedded null&quot;;
</del><ins>+            m_failureReason = ASCIILiteral(&quot;Status line contains embedded null&quot;);
</ins><span class="cx">             return p + 1 - header;
</span><span class="cx">         } else if (!isASCII(*p)) {
</span><del>-            m_failureReason = &quot;Status line contains non-ASCII character&quot;;
</del><ins>+            m_failureReason = ASCIILiteral(&quot;Status line contains non-ASCII character&quot;);
</ins><span class="cx">             return p + 1 - header;
</span><span class="cx">         } else if (*p == '\n')
</span><span class="cx">             break;
</span><span class="lines">@@ -458,38 +471,38 @@
</span><span class="cx">     const char* end = p + 1;
</span><span class="cx">     int lineLength = end - header;
</span><span class="cx">     if (lineLength &gt; maximumLength) {
</span><del>-        m_failureReason = &quot;Status line is too long&quot;;
</del><ins>+        m_failureReason = ASCIILiteral(&quot;Status line is too long&quot;);
</ins><span class="cx">         return maximumLength;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     // The line must end with &quot;\r\n&quot;.
</span><span class="cx">     if (lineLength &lt; 2 || *(end - 2) != '\r') {
</span><del>-        m_failureReason = &quot;Status line does not end with CRLF&quot;;
</del><ins>+        m_failureReason = ASCIILiteral(&quot;Status line does not end with CRLF&quot;);
</ins><span class="cx">         return lineLength;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     if (!space1 || !space2) {
</span><del>-        m_failureReason = &quot;No response code found: &quot; + trimInputSample(header, lineLength - 2);
</del><ins>+        m_failureReason = makeString(&quot;No response code found: &quot;, trimInputSample(header, lineLength - 2));
</ins><span class="cx">         return lineLength;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    String httpVersionString(header, space1 - header);
-    if (!headerHasValidHTTPVersion(httpVersionString, headerLength)) {
-        m_failureReason = &quot;Invalid HTTP version string: &quot; + httpVersionString;
</del><ins>+    StringView httpStatusLine(reinterpret_cast&lt;const LChar*&gt;(header), space1 - header);
+    if (!headerHasValidHTTPVersion(httpStatusLine)) {
+        m_failureReason = makeString(&quot;Invalid HTTP version string: &quot;, httpStatusLine);
</ins><span class="cx">         return lineLength;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    String statusCodeString(space1 + 1, space2 - space1 - 1);
</del><ins>+    StringView statusCodeString(reinterpret_cast&lt;const LChar*&gt;(space1 + 1), space2 - space1 - 1);
</ins><span class="cx">     if (statusCodeString.length() != 3) // Status code must consist of three digits.
</span><span class="cx">         return lineLength;
</span><span class="cx">     for (int i = 0; i &lt; 3; ++i)
</span><span class="cx">         if (statusCodeString[i] &lt; '0' || statusCodeString[i] &gt; '9') {
</span><del>-            m_failureReason = &quot;Invalid status code: &quot; + statusCodeString;
</del><ins>+            m_failureReason = makeString(&quot;Invalid status code: &quot;, statusCodeString);
</ins><span class="cx">             return lineLength;
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">     bool ok = false;
</span><del>-    statusCode = statusCodeString.toInt(&amp;ok);
</del><ins>+    statusCode = statusCodeString.toIntStrict(ok);
</ins><span class="cx">     ASSERT(ok);
</span><span class="cx"> 
</span><span class="cx">     statusText = String(space2 + 1, end - space2 - 3); // Exclude &quot;\r\n&quot;.
</span><span class="lines">@@ -498,7 +511,7 @@
</span><span class="cx"> 
</span><span class="cx"> const char* WebSocketHandshake::readHTTPHeaders(const char* start, const char* end)
</span><span class="cx"> {
</span><del>-    String name;
</del><ins>+    StringView name;
</ins><span class="cx">     String value;
</span><span class="cx">     bool sawSecWebSocketExtensionsHeaderField = false;
</span><span class="cx">     bool sawSecWebSocketAcceptHeaderField = false;
</span><span class="lines">@@ -514,19 +527,25 @@
</span><span class="cx">         if (name.isEmpty())
</span><span class="cx">             break;
</span><span class="cx"> 
</span><ins>+        HTTPHeaderName headerName;
+        if (!findHTTPHeaderName(name, headerName)) {
+            m_failureReason = makeString(&quot;Unknown header name &quot;, name, &quot; in HTTP response&quot;);
+            return nullptr;
+        }
+
</ins><span class="cx">         // https://tools.ietf.org/html/rfc7230#section-3.2.4
</span><span class="cx">         // &quot;Newly defined header fields SHOULD limit their field values to US-ASCII octets.&quot;
</span><del>-        if ((equalLettersIgnoringASCIICase(name, &quot;sec-websocket-extensions&quot;)
-            || equalLettersIgnoringASCIICase(name, &quot;sec-websocket-accept&quot;)
-            || equalLettersIgnoringASCIICase(name, &quot;sec-websocket-protocol&quot;))
</del><ins>+        if ((headerName == HTTPHeaderName::SecWebSocketExtensions
+            || headerName == HTTPHeaderName::SecWebSocketAccept
+            || headerName == HTTPHeaderName::SecWebSocketProtocol)
</ins><span class="cx">             &amp;&amp; !value.containsOnlyASCII()) {
</span><del>-            m_failureReason = name + &quot; header value should only contain ASCII characters&quot;;
</del><ins>+            m_failureReason = makeString(name, &quot; header value should only contain ASCII characters&quot;);
</ins><span class="cx">             return nullptr;
</span><span class="cx">         }
</span><span class="cx">         
</span><del>-        if (equalLettersIgnoringASCIICase(name, &quot;sec-websocket-extensions&quot;)) {
</del><ins>+        if (headerName == HTTPHeaderName::SecWebSocketExtensions) {
</ins><span class="cx">             if (sawSecWebSocketExtensionsHeaderField) {
</span><del>-                m_failureReason = &quot;The Sec-WebSocket-Extensions header must not appear more than once in an HTTP response&quot;;
</del><ins>+                m_failureReason = ASCIILiteral(&quot;The Sec-WebSocket-Extensions header must not appear more than once in an HTTP response&quot;);
</ins><span class="cx">                 return nullptr;
</span><span class="cx">             }
</span><span class="cx">             if (!m_extensionDispatcher.processHeaderValue(value)) {
</span><span class="lines">@@ -534,22 +553,23 @@
</span><span class="cx">                 return nullptr;
</span><span class="cx">             }
</span><span class="cx">             sawSecWebSocketExtensionsHeaderField = true;
</span><del>-        } else if (equalLettersIgnoringASCIICase(name, &quot;sec-websocket-accept&quot;)) {
-            if (sawSecWebSocketAcceptHeaderField) {
-                m_failureReason = &quot;The Sec-WebSocket-Accept header must not appear more than once in an HTTP response&quot;;
-                return nullptr;
</del><ins>+        } else {
+            if (headerName == HTTPHeaderName::SecWebSocketAccept) {
+                if (sawSecWebSocketAcceptHeaderField) {
+                    m_failureReason = ASCIILiteral(&quot;The Sec-WebSocket-Accept header must not appear more than once in an HTTP response&quot;);
+                    return nullptr;
+                }
+                sawSecWebSocketAcceptHeaderField = true;
+            } else if (headerName == HTTPHeaderName::SecWebSocketProtocol) {
+                if (sawSecWebSocketProtocolHeaderField) {
+                    m_failureReason = ASCIILiteral(&quot;The Sec-WebSocket-Protocol header must not appear more than once in an HTTP response&quot;);
+                    return nullptr;
+                }
+                sawSecWebSocketProtocolHeaderField = true;
</ins><span class="cx">             }
</span><del>-            m_serverHandshakeResponse.addHTTPHeaderField(name, value);
-            sawSecWebSocketAcceptHeaderField = true;
-        } else if (equalLettersIgnoringASCIICase(name, &quot;sec-websocket-protocol&quot;)) {
-            if (sawSecWebSocketProtocolHeaderField) {
-                m_failureReason = &quot;The Sec-WebSocket-Protocol header must not appear more than once in an HTTP response&quot;;
-                return nullptr;
-            }
-            m_serverHandshakeResponse.addHTTPHeaderField(name, value);
-            sawSecWebSocketProtocolHeaderField = true;
-        } else
-            m_serverHandshakeResponse.addHTTPHeaderField(name, value);
</del><ins>+
+            m_serverHandshakeResponse.addHTTPHeaderField(headerName, value);
+        }
</ins><span class="cx">     }
</span><span class="cx">     return p;
</span><span class="cx"> }
</span><span class="lines">@@ -562,40 +582,40 @@
</span><span class="cx">     const String&amp; serverWebSocketAccept = this-&gt;serverWebSocketAccept();
</span><span class="cx"> 
</span><span class="cx">     if (serverUpgrade.isNull()) {
</span><del>-        m_failureReason = &quot;Error during WebSocket handshake: 'Upgrade' header is missing&quot;;
</del><ins>+        m_failureReason = ASCIILiteral(&quot;Error during WebSocket handshake: 'Upgrade' header is missing&quot;);
</ins><span class="cx">         return false;
</span><span class="cx">     }
</span><span class="cx">     if (serverConnection.isNull()) {
</span><del>-        m_failureReason = &quot;Error during WebSocket handshake: 'Connection' header is missing&quot;;
</del><ins>+        m_failureReason = ASCIILiteral(&quot;Error during WebSocket handshake: 'Connection' header is missing&quot;);
</ins><span class="cx">         return false;
</span><span class="cx">     }
</span><span class="cx">     if (serverWebSocketAccept.isNull()) {
</span><del>-        m_failureReason = &quot;Error during WebSocket handshake: 'Sec-WebSocket-Accept' header is missing&quot;;
</del><ins>+        m_failureReason = ASCIILiteral(&quot;Error during WebSocket handshake: 'Sec-WebSocket-Accept' header is missing&quot;);
</ins><span class="cx">         return false;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     if (!equalLettersIgnoringASCIICase(serverUpgrade, &quot;websocket&quot;)) {
</span><del>-        m_failureReason = &quot;Error during WebSocket handshake: 'Upgrade' header value is not 'WebSocket'&quot;;
</del><ins>+        m_failureReason = ASCIILiteral(&quot;Error during WebSocket handshake: 'Upgrade' header value is not 'WebSocket'&quot;);
</ins><span class="cx">         return false;
</span><span class="cx">     }
</span><span class="cx">     if (!equalLettersIgnoringASCIICase(serverConnection, &quot;upgrade&quot;)) {
</span><del>-        m_failureReason = &quot;Error during WebSocket handshake: 'Connection' header value is not 'Upgrade'&quot;;
</del><ins>+        m_failureReason = ASCIILiteral(&quot;Error during WebSocket handshake: 'Connection' header value is not 'Upgrade'&quot;);
</ins><span class="cx">         return false;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     if (serverWebSocketAccept != m_expectedAccept) {
</span><del>-        m_failureReason = &quot;Error during WebSocket handshake: Sec-WebSocket-Accept mismatch&quot;;
</del><ins>+        m_failureReason = ASCIILiteral(&quot;Error during WebSocket handshake: Sec-WebSocket-Accept mismatch&quot;);
</ins><span class="cx">         return false;
</span><span class="cx">     }
</span><span class="cx">     if (!serverWebSocketProtocol.isNull()) {
</span><span class="cx">         if (m_clientProtocol.isEmpty()) {
</span><del>-            m_failureReason = &quot;Error during WebSocket handshake: Sec-WebSocket-Protocol mismatch&quot;;
</del><ins>+            m_failureReason = ASCIILiteral(&quot;Error during WebSocket handshake: Sec-WebSocket-Protocol mismatch&quot;);
</ins><span class="cx">             return false;
</span><span class="cx">         }
</span><span class="cx">         Vector&lt;String&gt; result;
</span><span class="cx">         m_clientProtocol.split(String(WebSocket::subProtocolSeperator()), result);
</span><span class="cx">         if (!result.contains(serverWebSocketProtocol)) {
</span><del>-            m_failureReason = &quot;Error during WebSocket handshake: Sec-WebSocket-Protocol mismatch&quot;;
</del><ins>+            m_failureReason = ASCIILiteral(&quot;Error during WebSocket handshake: Sec-WebSocket-Protocol mismatch&quot;);
</ins><span class="cx">             return false;
</span><span class="cx">         }
</span><span class="cx">     }
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformnetworkHTTPParserscpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/network/HTTPParsers.cpp (199589 => 199590)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/network/HTTPParsers.cpp        2016-04-15 16:18:37 UTC (rev 199589)
+++ trunk/Source/WebCore/platform/network/HTTPParsers.cpp        2016-04-15 16:39:00 UTC (rev 199590)
</span><span class="lines">@@ -582,20 +582,20 @@
</span><span class="cx"> 
</span><span class="cx">     // Haven't finished header line.
</span><span class="cx">     if (consumedLength == length) {
</span><del>-        failureReason = &quot;Incomplete Request Line&quot;;
</del><ins>+        failureReason = ASCIILiteral(&quot;Incomplete Request Line&quot;);
</ins><span class="cx">         return 0;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     // RequestLine does not contain 3 parts.
</span><span class="cx">     if (!space1 || !space2) {
</span><del>-        failureReason = &quot;Request Line does not appear to contain: &lt;Method&gt; &lt;Url&gt; &lt;HTTPVersion&gt;.&quot;;
</del><ins>+        failureReason = ASCIILiteral(&quot;Request Line does not appear to contain: &lt;Method&gt; &lt;Url&gt; &lt;HTTPVersion&gt;.&quot;);
</ins><span class="cx">         return 0;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     // The line must end with &quot;\r\n&quot;.
</span><span class="cx">     const char* end = p + 1;
</span><span class="cx">     if (*(end - 2) != '\r') {
</span><del>-        failureReason = &quot;Request line does not end with CRLF&quot;;
</del><ins>+        failureReason = ASCIILiteral(&quot;Request line does not end with CRLF&quot;);
</ins><span class="cx">         return 0;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -619,14 +619,48 @@
</span><span class="cx">     return end - data;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-size_t parseHTTPHeader(const char* start, size_t length, String&amp; failureReason, String&amp; nameStr, String&amp; valueStr, bool strict)
</del><ins>+static inline bool isValidHeaderNameCharacter(const char* character)
</ins><span class="cx"> {
</span><ins>+    // https://tools.ietf.org/html/rfc7230#section-3.2
+    // A header name should only contain one or more of
+    // alphanumeric or ! # $ % &amp; ' * + - . ^ _ ` | ~
+    if (isASCIIAlphanumeric(*character))
+        return true;
+    switch (*character) {
+    case '!':
+    case '#':
+    case '$':
+    case '%':
+    case '&amp;':
+    case '\'':
+    case '*':
+    case '+':
+    case '-':
+    case '.':
+    case '^':
+    case '_':
+    case '`':
+    case '|':
+    case '~':
+        return true;
+    default:
+        return false;
+    }
+}
+
+size_t parseHTTPHeader(const char* start, size_t length, String&amp; failureReason, StringView&amp; nameStr, String&amp; valueStr, bool strict)
+{
</ins><span class="cx">     const char* p = start;
</span><span class="cx">     const char* end = start + length;
</span><span class="cx"> 
</span><span class="cx">     Vector&lt;char&gt; name;
</span><span class="cx">     Vector&lt;char&gt; value;
</span><del>-    nameStr = String();
</del><ins>+
+    bool foundFirstNameChar = false;
+    const char* namePtr;
+    size_t nameSize = 0;
+
+    nameStr = StringView();
</ins><span class="cx">     valueStr = String();
</span><span class="cx"> 
</span><span class="cx">     for (; p &lt; end; p++) {
</span><span class="lines">@@ -635,18 +669,29 @@
</span><span class="cx">             if (name.isEmpty()) {
</span><span class="cx">                 if (p + 1 &lt; end &amp;&amp; *(p + 1) == '\n')
</span><span class="cx">                     return (p + 2) - start;
</span><del>-                failureReason = &quot;CR doesn't follow LF at &quot; + trimInputSample(p, end - p);
</del><ins>+                failureReason = makeString(&quot;CR doesn't follow LF in header name at &quot;, trimInputSample(p, end - p));
</ins><span class="cx">                 return 0;
</span><span class="cx">             }
</span><del>-            failureReason = &quot;Unexpected CR in name at &quot; + trimInputSample(name.data(), name.size());
</del><ins>+            failureReason = makeString(&quot;Unexpected CR in header name at &quot;, trimInputSample(name.data(), name.size()));
</ins><span class="cx">             return 0;
</span><span class="cx">         case '\n':
</span><del>-            failureReason = &quot;Unexpected LF in name at &quot; + trimInputSample(name.data(), name.size());
</del><ins>+            failureReason = makeString(&quot;Unexpected LF in header name at &quot;, trimInputSample(name.data(), name.size()));
</ins><span class="cx">             return 0;
</span><span class="cx">         case ':':
</span><span class="cx">             break;
</span><span class="cx">         default:
</span><ins>+            if (!isValidHeaderNameCharacter(p)) {
+                if (name.size() &lt; 1)
+                    failureReason = &quot;Unexpected start character in header name&quot;;
+                else
+                    failureReason = makeString(&quot;Unexpected character in header name at &quot;, trimInputSample(name.data(), name.size()));
+                return 0;
+            }
</ins><span class="cx">             name.append(*p);
</span><ins>+            if (!foundFirstNameChar) {
+                namePtr = p;
+                foundFirstNameChar = true;
+            }
</ins><span class="cx">             continue;
</span><span class="cx">         }
</span><span class="cx">         if (*p == ':') {
</span><span class="lines">@@ -655,6 +700,9 @@
</span><span class="cx">         }
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    nameSize = name.size();
+    nameStr = StringView(reinterpret_cast&lt;const LChar*&gt;(namePtr), nameSize);
+
</ins><span class="cx">     for (; p &lt; end &amp;&amp; *p == 0x20; p++) { }
</span><span class="cx"> 
</span><span class="cx">     for (; p &lt; end; p++) {
</span><span class="lines">@@ -663,7 +711,7 @@
</span><span class="cx">             break;
</span><span class="cx">         case '\n':
</span><span class="cx">             if (strict) {
</span><del>-                failureReason = &quot;Unexpected LF in value at &quot; + trimInputSample(value.data(), value.size());
</del><ins>+                failureReason = makeString(&quot;Unexpected LF in header value at &quot;, trimInputSample(value.data(), value.size()));
</ins><span class="cx">                 return 0;
</span><span class="cx">             }
</span><span class="cx">             break;
</span><span class="lines">@@ -676,17 +724,12 @@
</span><span class="cx">         }
</span><span class="cx">     }
</span><span class="cx">     if (p &gt;= end || (strict &amp;&amp; *p != '\n')) {
</span><del>-        failureReason = &quot;CR doesn't follow LF after value at &quot; + trimInputSample(p, end - p);
</del><ins>+        failureReason = makeString(&quot;CR doesn't follow LF after header value at &quot;, trimInputSample(p, end - p));
</ins><span class="cx">         return 0;
</span><span class="cx">     }
</span><del>-    nameStr = String::fromUTF8(name.data(), name.size());
</del><span class="cx">     valueStr = String::fromUTF8(value.data(), value.size());
</span><del>-    if (nameStr.isNull()) {
-        failureReason = &quot;Invalid UTF-8 sequence in header name&quot;;
-        return 0;
-    }
</del><span class="cx">     if (valueStr.isNull()) {
</span><del>-        failureReason = &quot;Invalid UTF-8 sequence in header value&quot;;
</del><ins>+        failureReason = ASCIILiteral(&quot;Invalid UTF-8 sequence in header value&quot;);
</ins><span class="cx">         return 0;
</span><span class="cx">     }
</span><span class="cx">     return p - start;
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformnetworkHTTPParsersh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/network/HTTPParsers.h (199589 => 199590)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/network/HTTPParsers.h        2016-04-15 16:18:37 UTC (rev 199589)
+++ trunk/Source/WebCore/platform/network/HTTPParsers.h        2016-04-15 16:39:00 UTC (rev 199590)
</span><span class="lines">@@ -91,7 +91,7 @@
</span><span class="cx"> // Parsing Complete HTTP Messages.
</span><span class="cx"> enum HTTPVersion { Unknown, HTTP_1_0, HTTP_1_1 };
</span><span class="cx"> size_t parseHTTPRequestLine(const char* data, size_t length, String&amp; failureReason, String&amp; method, String&amp; url, HTTPVersion&amp;);
</span><del>-size_t parseHTTPHeader(const char* data, size_t length, String&amp; failureReason, String&amp; nameStr, String&amp; valueStr, bool strict = true);
</del><ins>+size_t parseHTTPHeader(const char* data, size_t length, String&amp; failureReason, StringView&amp; nameStr, String&amp; valueStr, bool strict = true);
</ins><span class="cx"> size_t parseHTTPRequestBody(const char* data, size_t length, Vector&lt;unsigned char&gt;&amp; body);
</span><span class="cx"> 
</span><span class="cx"> inline bool isHTTPSpace(UChar character)
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformnetworkResourceRequestBasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/network/ResourceRequestBase.cpp (199589 => 199590)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/network/ResourceRequestBase.cpp        2016-04-15 16:18:37 UTC (rev 199589)
+++ trunk/Source/WebCore/platform/network/ResourceRequestBase.cpp        2016-04-15 16:39:00 UTC (rev 199590)
</span><span class="lines">@@ -465,7 +465,7 @@
</span><span class="cx">         m_platformRequestUpdated = false;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void ResourceRequestBase::addHTTPHeaderField(const String&amp; name, const String&amp; value) 
</del><ins>+void ResourceRequestBase::addHTTPHeaderField(HTTPHeaderName name, const String&amp; value)
</ins><span class="cx"> {
</span><span class="cx">     updateResourceRequest();
</span><span class="cx"> 
</span><span class="lines">@@ -475,6 +475,16 @@
</span><span class="cx">         m_platformRequestUpdated = false;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void ResourceRequestBase::addHTTPHeaderField(const String&amp; name, const String&amp; value)
+{
+    updateResourceRequest();
+    
+    m_httpHeaderFields.add(name, value);
+    
+    if (url().protocolIsInHTTPFamily())
+        m_platformRequestUpdated = false;
+}
+    
</ins><span class="cx"> void ResourceRequestBase::setHTTPHeaderFields(HTTPHeaderMap headerFields)
</span><span class="cx"> {
</span><span class="cx">     updateResourceRequest();
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformnetworkResourceRequestBaseh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/network/ResourceRequestBase.h (199589 => 199590)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/network/ResourceRequestBase.h        2016-04-15 16:18:37 UTC (rev 199589)
+++ trunk/Source/WebCore/platform/network/ResourceRequestBase.h        2016-04-15 16:39:00 UTC (rev 199590)
</span><span class="lines">@@ -86,6 +86,7 @@
</span><span class="cx">         WEBCORE_EXPORT String httpHeaderField(HTTPHeaderName) const;
</span><span class="cx">         WEBCORE_EXPORT void setHTTPHeaderField(const String&amp; name, const String&amp; value);
</span><span class="cx">         WEBCORE_EXPORT void setHTTPHeaderField(HTTPHeaderName, const String&amp; value);
</span><ins>+        void addHTTPHeaderField(HTTPHeaderName, const String&amp; value);
</ins><span class="cx">         void addHTTPHeaderField(const String&amp; name, const String&amp; value);
</span><span class="cx"> 
</span><span class="cx">         // Instead of passing a string literal to any of these functions, just use a HTTPHeaderName instead.
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformnetworkResourceResponseBasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/network/ResourceResponseBase.cpp (199589 => 199590)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/network/ResourceResponseBase.cpp        2016-04-15 16:18:37 UTC (rev 199589)
+++ trunk/Source/WebCore/platform/network/ResourceResponseBase.cpp        2016-04-15 16:39:00 UTC (rev 199590)
</span><span class="lines">@@ -334,15 +334,22 @@
</span><span class="cx">     // FIXME: Should invalidate or update platform response if present.
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void ResourceResponseBase::addHTTPHeaderField(const String&amp; name, const String&amp; value)
</del><ins>+void ResourceResponseBase::addHTTPHeaderField(HTTPHeaderName name, const String&amp; value)
</ins><span class="cx"> {
</span><span class="cx">     lazyInit(AllFields);
</span><ins>+    updateHeaderParsedState(name);
+    m_httpHeaderFields.add(name, value);
+}
</ins><span class="cx"> 
</span><ins>+void ResourceResponseBase::addHTTPHeaderField(const String&amp; name, const String&amp; value)
+{
</ins><span class="cx">     HTTPHeaderName headerName;
</span><span class="cx">     if (findHTTPHeaderName(name, headerName))
</span><del>-        updateHeaderParsedState(headerName);
-
-    m_httpHeaderFields.add(name, value);
</del><ins>+        addHTTPHeaderField(headerName, value);
+    else {
+        lazyInit(AllFields);
+        m_httpHeaderFields.add(name, value);
+    }
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> const HTTPHeaderMap&amp; ResourceResponseBase::httpHeaderFields() const
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformnetworkResourceResponseBaseh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/network/ResourceResponseBase.h (199589 => 199590)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/network/ResourceResponseBase.h        2016-04-15 16:18:37 UTC (rev 199589)
+++ trunk/Source/WebCore/platform/network/ResourceResponseBase.h        2016-04-15 16:39:00 UTC (rev 199590)
</span><span class="lines">@@ -84,6 +84,7 @@
</span><span class="cx">     WEBCORE_EXPORT void setHTTPHeaderField(const String&amp; name, const String&amp; value);
</span><span class="cx">     void setHTTPHeaderField(HTTPHeaderName, const String&amp; value);
</span><span class="cx"> 
</span><ins>+    void addHTTPHeaderField(HTTPHeaderName, const String&amp; value);
</ins><span class="cx">     void addHTTPHeaderField(const String&amp; name, const String&amp; value);
</span><span class="cx"> 
</span><span class="cx">     // Instead of passing a string literal to any of these functions, just use a HTTPHeaderName instead.
</span></span></pre></div>
<a id="trunkSourceWebKit2ChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/ChangeLog (199589 => 199590)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/ChangeLog        2016-04-15 16:18:37 UTC (rev 199589)
+++ trunk/Source/WebKit2/ChangeLog        2016-04-15 16:39:00 UTC (rev 199590)
</span><span class="lines">@@ -1,3 +1,14 @@
</span><ins>+2016-04-15  John Wilander  &lt;wilander@apple.com&gt;
+
+        Refactor WebSockets handshake to use StringView instead of String for header validation.
+        https://bugs.webkit.org/show_bug.cgi?id=155602
+
+        Reviewed by Darin Adler.
+
+        * UIProcess/InspectorServer/HTTPRequest.cpp:
+        (WebKit::HTTPRequest::parseHeaders):
+            Now declares the HTTP header name as a StringView to match the change in WebCore::parseHTTPHeader.
+
</ins><span class="cx"> 2016-04-15  Alex Christensen  &lt;achristensen@webkit.org&gt;
</span><span class="cx"> 
</span><span class="cx">         Don't copy entire NSURLSessionConfiguration just to test for credentials
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessInspectorServerHTTPRequestcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/InspectorServer/HTTPRequest.cpp (199589 => 199590)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/InspectorServer/HTTPRequest.cpp        2016-04-15 16:18:37 UTC (rev 199589)
+++ trunk/Source/WebKit2/UIProcess/InspectorServer/HTTPRequest.cpp        2016-04-15 16:39:00 UTC (rev 199590)
</span><span class="lines">@@ -27,6 +27,7 @@
</span><span class="cx"> #include &quot;HTTPRequest.h&quot;
</span><span class="cx"> 
</span><span class="cx"> #include &lt;wtf/text/CString.h&gt;
</span><ins>+#include &lt;wtf/text/StringView.h&gt;
</ins><span class="cx"> 
</span><span class="cx"> using namespace WebCore;
</span><span class="cx"> 
</span><span class="lines">@@ -82,7 +83,7 @@
</span><span class="cx"> {
</span><span class="cx">     const char* p = data;
</span><span class="cx">     const char* end = data + length;
</span><del>-    String name;
</del><ins>+    StringView name;
</ins><span class="cx">     String value;
</span><span class="cx">     for (; p &lt; data + length; p++) {
</span><span class="cx">         size_t consumedLength = parseHTTPHeader(p, end - p, failureReason, name, value);
</span><span class="lines">@@ -91,7 +92,7 @@
</span><span class="cx">         p += consumedLength;
</span><span class="cx">         if (name.isEmpty())
</span><span class="cx">             break;
</span><del>-        m_headerFields.add(name, value);
</del><ins>+        m_headerFields.add(name.toString(), value);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     // If we got here and &quot;name&quot; is empty, it means the headers are valid and ended with a
</span></span></pre>
</div>
</div>

</body>
</html>