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;
}
예제 #2
0
bool WebSocketHandshake::checkResponseHeaders()
{
    const AtomicString& serverWebSocketProtocol = this->serverWebSocketProtocol();
    const AtomicString& serverUpgrade = this->serverUpgrade();
    const AtomicString& serverConnection = this->serverConnection();
    const AtomicString& serverWebSocketAccept = this->serverWebSocketAccept();

    if (serverUpgrade.isNull()) {
        m_failureReason = formatHandshakeFailureReason("'Upgrade' header is missing");
        return false;
    }
    if (serverConnection.isNull()) {
        m_failureReason = formatHandshakeFailureReason("'Connection' header is missing");
        return false;
    }
    if (serverWebSocketAccept.isNull()) {
        m_failureReason = formatHandshakeFailureReason("'Sec-WebSocket-Accept' header is missing");
        return false;
    }

    if (!equalIgnoringCase(serverUpgrade, "websocket")) {
        m_failureReason = formatHandshakeFailureReason("'Upgrade' header value is not 'WebSocket': " + serverUpgrade);
        return false;
    }
    if (!equalIgnoringCase(serverConnection, "upgrade")) {
        m_failureReason = formatHandshakeFailureReason("'Connection' header value is not 'Upgrade': " + serverConnection);
        return false;
    }

    if (serverWebSocketAccept != m_expectedAccept) {
        m_failureReason = formatHandshakeFailureReason("Incorrect 'Sec-WebSocket-Accept' header value");
        return false;
    }
    if (!serverWebSocketProtocol.isNull()) {
        if (m_clientProtocol.isEmpty()) {
            m_failureReason = formatHandshakeFailureReason("Response must not include 'Sec-WebSocket-Protocol' header if not present in request: " + serverWebSocketProtocol);
            return false;
        }
        Vector<String> result;
        m_clientProtocol.split(String(WebSocket::subprotocolSeperator()), result);
        if (!result.contains(serverWebSocketProtocol)) {
            m_failureReason = formatHandshakeFailureReason("'Sec-WebSocket-Protocol' header value '" + serverWebSocketProtocol + "' in response does not match any of sent values");
            return false;
        }
    } else if (!m_clientProtocol.isEmpty()) {
        m_failureReason = formatHandshakeFailureReason("Sent non-empty 'Sec-WebSocket-Protocol' header but no response was received");
        return false;
    }
    return true;
}
int WebSocketHandshake::readServerHandshake(const char* header, size_t len)
{
    m_mode = Incomplete;
    int statusCode;
    String statusText;
    int lineLength = readStatusLine(header, len, statusCode, statusText);
    if (lineLength == -1)
        return -1;
    if (statusCode == -1) {
        m_mode = Failed; // m_failureReason is set inside readStatusLine().
        return len;
    }
    WTF_LOG(Network, "WebSocketHandshake %p readServerHandshake() Status code is %d", this, statusCode);
    m_response.setStatusCode(statusCode);
    m_response.setStatusText(statusText);
    if (statusCode != 101) {
        m_mode = Failed;
        m_failureReason = formatHandshakeFailureReason("Unexpected response code: " + String::number(statusCode));
        return len;
    }
    m_mode = Normal;
    if (!strnstr(header, "\r\n\r\n", len)) {
        // Just hasn't been received fully yet.
        m_mode = Incomplete;
        return -1;
    }
    const char* p = readHTTPHeaders(header + lineLength, header + len);
    if (!p) {
        WTF_LOG(Network, "WebSocketHandshake %p readServerHandshake() readHTTPHeaders() failed", this);
        m_mode = Failed; // m_failureReason is set inside readHTTPHeaders().
        return len;
    }
    if (!checkResponseHeaders()) {
        WTF_LOG(Network, "WebSocketHandshake %p readServerHandshake() checkResponseHeaders() failed", this);
        m_mode = Failed;
        return p - header;
    }

    m_mode = Connected;
    return p - header;
}
bool WebSocketHandshake::checkResponseHeaders()
{
    const AtomicString& serverWebSocketProtocol = this->serverWebSocketProtocol();
    const AtomicString& serverUpgrade = this->serverUpgrade();
    const AtomicString& serverConnection = this->serverConnection();
    const AtomicString& serverWebSocketAccept = this->serverWebSocketAccept();

    if (serverUpgrade.isNull()) {
        m_failureReason = formatHandshakeFailureReason("'Upgrade' header is missing");
        return false;
    }
    if (serverConnection.isNull()) {
        m_failureReason = formatHandshakeFailureReason("'Connection' header is missing");
        return false;
    }
    if (serverWebSocketAccept.isNull()) {
        m_failureReason = formatHandshakeFailureReason("'Sec-WebSocket-Accept' header is missing");
        return false;
    }

    if (!equalIgnoringCase(serverUpgrade, "websocket")) {
        m_failureReason = formatHandshakeFailureReason("'Upgrade' header value is not 'WebSocket': " + serverUpgrade);
        return false;
    }
    if (!equalIgnoringCase(serverConnection, "upgrade")) {
        m_failureReason = formatHandshakeFailureReason("'Connection' header value is not 'Upgrade': " + serverConnection);
        return false;
    }

    if (serverWebSocketAccept != m_expectedAccept) {
        m_failureReason = formatHandshakeFailureReason("Incorrect 'Sec-WebSocket-Accept' header value");
        return false;
    }
    if (!serverWebSocketProtocol.isNull()) {
        if (m_clientProtocol.isEmpty()) {
            m_failureReason = formatHandshakeFailureReason("Response must not include 'Sec-WebSocket-Protocol' header if not present in request: " + serverWebSocketProtocol);
            return false;
        }
        Vector<String> result;
        m_clientProtocol.split(String(WebSocket::subProtocolSeperator()), result);
        if (!result.contains(serverWebSocketProtocol)) {
            m_failureReason = formatHandshakeFailureReason("'Sec-WebSocket-Protocol' header value '" + serverWebSocketProtocol + "' in response does not match any of sent values");
            return false;
        }
    } else if (!m_clientProtocol.isEmpty()) {
        // FIXME: Some servers are not accustomed to this failure, so we provide an adhoc white-list for it tentatively.
        Vector<String> protocols;
        m_clientProtocol.split(String(WebSocket::subProtocolSeperator()), protocols);
        bool match = false;
        for (size_t i = 0; i < protocols.size() && !match; ++i) {
            for (size_t j = 0; j < WTF_ARRAY_LENGTH(missingProtocolWhiteList) && !match; ++j) {
                if (protocols[i] == missingProtocolWhiteList[j])
                    match = true;
            }
        }
        if (!match) {
            m_failureReason = formatHandshakeFailureReason("Sent non-empty 'Sec-WebSocket-Protocol' header but no response was received");
            return false;
        }
    }
    return true;
}
// Returns the header length (including "\r\n"), or -1 if we have not received enough data yet.
// If the line is malformed or the status code is not a 3-digit number,
// statusCode and statusText will be set to -1 and a null string, respectively.
int WebSocketHandshake::readStatusLine(const char* header, size_t headerLength, int& statusCode, String& statusText)
{
    // Arbitrary size limit to prevent the server from sending an unbounded
    // amount of data with no newlines and forcing us to buffer it all.
    static const int maximumLength = 1024;

    statusCode = -1;
    statusText = String();

    const char* space1 = 0;
    const char* space2 = 0;
    const char* p;
    size_t consumedLength;

    for (p = header, consumedLength = 0; consumedLength < headerLength; p++, consumedLength++) {
        if (*p == ' ') {
            if (!space1)
                space1 = p;
            else if (!space2)
                space2 = p;
        } else if (*p == '\0') {
            // 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 = formatHandshakeFailureReason("Status line contains embedded null");
            return p + 1 - header;
        } else if (*p == '\n') {
            break;
        }
    }
    if (consumedLength == headerLength)
        return -1; // We have not received '\n' yet.

    const char* end = p + 1;
    int lineLength = end - header;
    if (lineLength > maximumLength) {
        m_failureReason = formatHandshakeFailureReason("Status line is too long");
        return maximumLength;
    }

    // The line must end with "\r\n".
    if (lineLength < 2 || *(end - 2) != '\r') {
        m_failureReason = formatHandshakeFailureReason("Status line does not end with CRLF");
        return lineLength;
    }

    if (!space1 || !space2) {
        m_failureReason = formatHandshakeFailureReason("No response code found in status line: " + trimInputSample(header, lineLength - 2));
        return lineLength;
    }

    String statusCodeString(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 = formatHandshakeFailureReason("Invalid status code: " + statusCodeString);
            return lineLength;
        }
    }

    bool ok = false;
    statusCode = statusCodeString.toInt(&ok);
    ASSERT(ok);

    statusText = String(space2 + 1, end - space2 - 3); // Exclude "\r\n".
    return lineLength;
}