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