TEST(HTTPParsersTest, parseHTTPHeaderTwoLines) { const char data[] = "foo: bar\r\nhoge: fuga\r\nxxx"; String failureReason; AtomicString name, value; EXPECT_EQ(10u, parseHTTPHeader(data, failureReason, name, value)); EXPECT_TRUE(failureReason.isEmpty()); EXPECT_EQ("foo", name.string()); EXPECT_EQ("bar", value.string()); EXPECT_EQ(12u, parseHTTPHeader(data + 10, failureReason, name, value)); EXPECT_TRUE(failureReason.isEmpty()); EXPECT_EQ("hoge", name.string()); EXPECT_EQ("fuga", value.string()); }
TEST(HTTPParsersTest, parseHTTPHeaderUnexpectedLFinValue) { String failureReason; AtomicString name, value; EXPECT_EQ(0u, parseHTTPHeader("foo: bar\notherdata\n", failureReason, name, value)); EXPECT_EQ("Unexpected LF in value at bar", failureReason); }
TEST(HTTPParsersTest, parseHTTPHeaderNoLF) { String failureReason; AtomicString name, value; EXPECT_EQ(0u, parseHTTPHeader("foo: bar\rhoge\r\n", failureReason, name, value)); EXPECT_EQ("LF doesn't follow CR after value at hoge\r\n", failureReason); }
TEST(HTTPParsersTest, parseHTTPHeaderInvalidValue) { String failureReason; AtomicString name, value; EXPECT_EQ(0u, parseHTTPHeader("foo: \xfa\r\notherdata", failureReason, name, value)); EXPECT_EQ("Invalid UTF-8 sequence in header value", failureReason); }
TEST(HTTPParsersTest, parseHTTPHeaderUnexpectedCRinName) { String failureReason; AtomicString name, value; EXPECT_EQ(0u, parseHTTPHeader("foo\rotherdata\n", failureReason, name, value)); EXPECT_EQ("Unexpected CR in name at foo", failureReason); }
TEST(HTTPParsersTest, parseHTTPHeaderEmpty) { String failureReason; AtomicString name, value; EXPECT_EQ(0u, parseHTTPHeader("", failureReason, name, value)); EXPECT_EQ("Unterminated header name", failureReason); }
TEST(HTTPParsersTest, parseHTTPHeaderEmptyName) { String failureReason; AtomicString name, value; EXPECT_EQ(0u, parseHTTPHeader(": bar\r\notherdata", failureReason, name, value)); EXPECT_EQ("Header name is missing", failureReason); }
TEST(HTTPParsersTest, parseHTTPHeaderEmptyLine) { String failureReason; AtomicString name, value; EXPECT_EQ(2u, parseHTTPHeader("\r\notherdata", failureReason, name, value)); EXPECT_TRUE(failureReason.isEmpty()); EXPECT_TRUE(name.isNull()); EXPECT_TRUE(value.isNull()); }
TEST(HTTPParsersTest, parseHTTPHeaderEmptyValue) { String failureReason; AtomicString name, value; EXPECT_EQ(7u, parseHTTPHeader("foo: \r\notherdata", failureReason, name, value)); EXPECT_TRUE(failureReason.isEmpty()); EXPECT_EQ("foo", name.string()); EXPECT_TRUE(value.isEmpty()); }
const char* WebSocketHandshake::readHTTPHeaders(const char* start, const char* end) { m_response.clearHeaderFields(); AtomicString name; AtomicString value; bool sawSecWebSocketAcceptHeaderField = false; bool sawSecWebSocketProtocolHeaderField = false; const char* p = start; for (; p < end; p++) { size_t consumedLength = parseHTTPHeader(p, end - p, m_failureReason, name, value); if (!consumedLength) return 0; p += consumedLength; // Stop once we consumed an empty line. if (name.isEmpty()) break; // Sec-WebSocket-Extensions may be split. We parse and check the // header value every time the header appears. if (equalIgnoringCase("Sec-WebSocket-Extensions", name)) { if (!m_extensionDispatcher.processHeaderValue(value)) { m_failureReason = formatHandshakeFailureReason(m_extensionDispatcher.failureReason()); return 0; } } else if (equalIgnoringCase("Sec-WebSocket-Accept", name)) { if (sawSecWebSocketAcceptHeaderField) { m_failureReason = formatHandshakeFailureReason("'Sec-WebSocket-Accept' header must not appear more than once in a response"); return 0; } m_response.addHeaderField(name, value); sawSecWebSocketAcceptHeaderField = true; } else if (equalIgnoringCase("Sec-WebSocket-Protocol", name)) { if (sawSecWebSocketProtocolHeaderField) { m_failureReason = formatHandshakeFailureReason("'Sec-WebSocket-Protocol' header must not appear more than once in a response"); return 0; } m_response.addHeaderField(name, value); sawSecWebSocketProtocolHeaderField = true; } else { m_response.addHeaderField(name, value); } } String extensions = m_extensionDispatcher.acceptedExtensions(); if (!extensions.isEmpty()) m_response.addHeaderField("Sec-WebSocket-Extensions", AtomicString(extensions)); return p; }
const char* WebSocketHandshake::readHTTPHeaders(const char* start, const char* end) { AtomicString name; String value; bool sawSecWebSocketExtensionsHeaderField = false; bool sawSecWebSocketAcceptHeaderField = false; bool sawSecWebSocketProtocolHeaderField = false; const char* p = start; for (; p < end; p++) { size_t consumedLength = parseHTTPHeader(p, end - p, m_failureReason, name, value); if (!consumedLength) return 0; p += consumedLength; // Stop once we consumed an empty line. if (name.isEmpty()) break; if (equalIgnoringCase("sec-websocket-extensions", name)) { if (sawSecWebSocketExtensionsHeaderField) { m_failureReason = "The Sec-WebSocket-Extensions header MUST NOT appear more than once in an HTTP response"; return 0; } if (!m_extensionDispatcher.processHeaderValue(value)) { m_failureReason = m_extensionDispatcher.failureReason(); return 0; } sawSecWebSocketExtensionsHeaderField = true; } else if (equalIgnoringCase("Sec-WebSocket-Accept", name)) { if (sawSecWebSocketAcceptHeaderField) { m_failureReason = "The Sec-WebSocket-Accept header MUST NOT appear more than once in an HTTP response"; return 0; } m_serverHandshakeResponse.addHTTPHeaderField(name, value); sawSecWebSocketAcceptHeaderField = true; } else if (equalIgnoringCase("Sec-WebSocket-Protocol", name)) { if (sawSecWebSocketProtocolHeaderField) { m_failureReason = "The Sec-WebSocket-Protocol header MUST NOT appear more than once in an HTTP response"; return 0; } m_serverHandshakeResponse.addHTTPHeaderField(name, value); sawSecWebSocketProtocolHeaderField = true; } else m_serverHandshakeResponse.addHTTPHeaderField(name, value); } return p; }
size_t HTTPRequest::parseHeaders(const char* data, size_t length, String& failureReason) { const char* p = data; const char* end = data + length; AtomicString name; String value; for (; p < data + length; p++) { size_t consumedLength = parseHTTPHeader(p, end - p, failureReason, name, value); if (!consumedLength) return 0; p += consumedLength; if (name.isEmpty()) break; m_headerFields.add(name, value); } return p - data; }
bool MultipartHandle::parseHeadersIfPossible() { size_t contentLength = m_buffer.size(); if (!contentLength) return false; const char* content = m_buffer.data(); // Check if we have the header closing strings. if (!strnstr(content, "\r\n\r\n", contentLength)) { // Some servers closes the headers with only \n-s. if (!strnstr(content, "\n\n", contentLength)) { // Don't have the header closing string. Wait for more data. return false; } } // Parse the HTTP headers. String value; AtomicString name; char* p = const_cast<char*>(content); const char* end = content + contentLength; size_t totalConsumedLength = 0; for (; p < end; ++p) { String failureReason; size_t consumedLength = parseHTTPHeader(p, end - p, failureReason, name, value, false); if (!consumedLength) break; // No more header to parse. p += consumedLength; totalConsumedLength += consumedLength; // The name should not be empty, but the value could be empty. if (name.isEmpty()) break; m_headers.add(name, value); } m_buffer.remove(0, totalConsumedLength + 1); return true; }
const char* WebSocketHandshake::readHTTPHeaders(const char* start, const char* end) { StringView name; String value; bool sawSecWebSocketExtensionsHeaderField = false; bool sawSecWebSocketAcceptHeaderField = false; bool sawSecWebSocketProtocolHeaderField = false; const char* p = start; for (; p < end; p++) { size_t consumedLength = parseHTTPHeader(p, end - p, m_failureReason, name, value); if (!consumedLength) return nullptr; p += consumedLength; // Stop once we consumed an empty line. if (name.isEmpty()) break; HTTPHeaderName headerName; if (!findHTTPHeaderName(name, headerName)) { // Evidence in the wild shows that services make use of custom headers in the handshake m_serverHandshakeResponse.addHTTPHeaderField(name.toString(), value); continue; } // https://tools.ietf.org/html/rfc7230#section-3.2.4 // "Newly defined header fields SHOULD limit their field values to US-ASCII octets." if ((headerName == HTTPHeaderName::SecWebSocketExtensions || headerName == HTTPHeaderName::SecWebSocketAccept || headerName == HTTPHeaderName::SecWebSocketProtocol) && !value.containsOnlyASCII()) { m_failureReason = makeString(name, " header value should only contain ASCII characters"); return nullptr; } if (headerName == HTTPHeaderName::SecWebSocketExtensions) { if (sawSecWebSocketExtensionsHeaderField) { 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)) { m_failureReason = m_extensionDispatcher.failureReason(); return nullptr; } sawSecWebSocketExtensionsHeaderField = true; } 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(headerName, value); } } return p; }