void RFC6455::Client::HandleHandshake (const HTTP::Request& inRequest) {
    
    HTTP::Response response (HTTP::Code::BAD_REQUEST, inRequest.mVersion);
    
    bool isUpgraded (false);

    try {
        if (inRequest.GetHeaderValue ("Connection") == "Upgrade" && 
            inRequest.GetHeaderValue ("Upgrade") == "websocket" &&  
            inRequest.GetHeaderValue ("Sec-WebSocket-Key") != "" && 
            inRequest.mMethod == HTTP::Method::GET && 
            inRequest.mVersion == HTTP::Version::V11 
        ) {
            
            const auto key (inRequest.GetHeaderValue ("Sec-WebSocket-Key"));
            const auto keyWithMagicString (key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
            
            char base64[SHA1_BASE64_SIZE];
            sha1 (keyWithMagicString.c_str ()).finalize().print_base64 (base64);
            
            response.SetHeader (HTTP::Header ("Connection", "Upgrade"));
            response.SetHeader (HTTP::Header ("Upgrade", "websocket"));
            response.SetHeader (HTTP::Header ("Sec-WebSocket-Accept", std::string (base64)));
            
            response.mCode = HTTP::Code::SWITCHING_PROTOCOLS;
            isUpgraded = true;
        }
    } 
    catch (...) {}
    
    mResponseEncoder.Write (response);
    
    {
        using namespace HTTP;
        LOGINFO << "HTTP/" << VersionToString (response.mVersion) << " " << MethodToString (inRequest.mMethod) 
                << " " << inRequest.mPath << " - " << response.mCode << " " << CodeToString (response.mCode) << " - RFC6455";
    }
 
    if (!isUpgraded) {
        Quit ();
    }
    else {
        // Clear the stream, route the pipe through the frame decoder.
        GetReadStream ().Clear ().Pipe (mFrameDecoder).Pipe (this, &Client::HandleReceivedFrame);
        mResponseEncoder.Clear ();
        
        mPayloadStringEncoder.Pipe (mFrameEncoder).Pipe (GetWriteStream ());
        mPayloadBinaryEncoder.Pipe (mFrameEncoder);
    }
}
Esempio n. 2
0
void SSE::Client::HandleHandshake (const HTTP::Request& inRequest) {
    
    HTTP::Response response (HTTP::Code::BAD_REQUEST, inRequest.mVersion);
    
    bool isUpgraded (false);

    try {
        if (inRequest.GetHeaderValue ("Accept") == "text/event-stream" && 
            inRequest.mMethod == HTTP::Method::GET && 
            inRequest.mVersion == HTTP::Version::V11
        ) {
            
            // Standard SSE headers
            response.SetHeader (HTTP::Header ("Connection", "keep-alive"));
            response.SetHeader (HTTP::Header ("Content-Type", "text/event-stream"));
            
            // Prevent caching
            response.SetHeader (HTTP::Header ("Cache-Control", "no-cache"));
            
            // This SSE server most likely does not host the origin of the request
            // ==> Enable CORS.
            response.SetHeader (HTTP::Header ("Access-Control-Allow-Origin", "*"));
            response.SetHeader (HTTP::Header ("Access-Control-Allow-Credentials", "true"));
            
            // The response constructor sets the length to zero, this makes connections
            // assume there is no more data coming. Note: there is a stream in response, so the body 
            // has variable length! Remove this header.
            response.RemoveHeaders ("Content-Length"); 
            
            // Optionally check for last event ID.
            try {
                const auto list = inRequest.GetHeaders ("Last-Event-ID");
                if (!list.empty ()) {
                    uint8_t lastId = std::stoi (list[0].GetValue ());
                    mEventEncoder.SetLastId (lastId);
                }
            }
            catch (...) {}
            
            // Handshake completed.
            response.mCode = HTTP::Code::OK;
            isUpgraded = true;
        }
    } 
    catch (...) {}
    
    // Send the handshake response
    mResponseEncoder.Write (response);
    
    {
        using namespace HTTP;
        LOGINFO << "HTTP/" << VersionToString (response.mVersion) << " " << MethodToString (inRequest.mMethod) 
                << " " << inRequest.mPath << " - " << response.mCode << " " << CodeToString (response.mCode) << " - SSE";
    }
 
    if (!isUpgraded) {
        Quit ();
    }
    else {
        // Clear the read stream (SSE only contains outgoing events)
        GetReadStream ().Clear ();
        mResponseEncoder.Clear ();
        mToStringConverter.Clear ();
        mToPacketConverter.Clear ();
        mRequestDecoder.Clear ();
        mResponseEncoder.Clear ();
        mEventEncoder.Pipe (mToPacketConverter).Pipe (GetWriteStream ());
    }
}