IIS Native Module - Websocket

Viewed 90

I'm looking to implement a native websocket handler via a native module in IIS. I'm finding the documentation around it to be pretty vague and missing a lot of details.

I've created a IIS Native module DLL and it is working. I can keep it simple and just return a hello world html file with HTTP/200 response, and all is well.

However, I'm running into an issue when attempting to have it handle a websocket connection. There was a blog post on microsoft's IIS blog site here which describes how to handle a websocket connection. I've followed it and have tested and, yes, the connection is opened from a web browser, however I cannot read data from the socket in the native module, and the connection often appears to close in error on the client - after a random amount of time it seems.

The OnBeginRequest method of the module is:

REQUEST_NOTIFICATION_STATUS CKIIS::OnBeginRequest(IN IHttpContext* pHttpContext, IN IHttpEventProvider* pProvider) {
    UNREFERENCED_PARAMETER(pProvider);

    HRESULT hr;
    
    // I've only placed this here so I can attach a debugger.
    std::this_thread::sleep_for(std::chrono::seconds(10));

    this->_context = pHttpContext;

    IHttpResponse* pHttpResponse = pHttpContext->GetResponse();

    if (pHttpResponse != NULL)
    {
        pHttpResponse->Clear();

        pHttpResponse->SetStatus(101, "Switching Protocols");
        pHttpResponse->SetHeader(
            HttpHeaderUpgrade, "websocket",
            (USHORT)strlen("websocket"), TRUE);
        pHttpResponse->SetHeader(
            HttpHeaderConnection, "Upgrade",
            (USHORT)strlen("Upgrade"), TRUE);
        DWORD cbSent = 0;
        BOOL fCompletionExpected = false;
        hr = pHttpResponse->Flush(false, true, &cbSent, &fCompletionExpected);

        std::this_thread::sleep_for(std::chrono::seconds(10));

        IHttpContext3* pHttpContext3;
        HttpGetExtendedInterface(this->_server, pHttpContext, &pHttpContext3);

        IWebSocketContext* cts = (IWebSocketContext*)pHttpContext3->GetNamedContextContainer()->GetNamedContext(L"websockets");
    
        char buffer[1024 * 100];
        DWORD sz = 1024 * 100;
        BOOL utf;
        BOOL finalfrag;
        BOOL conclose;
        DWORD clxc = 78;
        BOOL expected;

        // This method call returns E_NOTIMPL.  
        // The documentation does not even indicate this is an expected return of this.
        HRESULT res = cts->ReadFragment(
            &buffer,
            &sz,
            false,
            &utf,
            &finalfrag,
            &conclose,
            Compl,
            &clxc,
            &expected);

        // Start a thread to read/write from the websocket.
        this->_runner = thread(&CKIIS::RunWork, this);

        // Tell IIS to keep the connection pending...
        return RQ_NOTIFICATION_PENDING;
    }

    // Return processing to the pipeline.
    return RQ_NOTIFICATION_CONTINUE;
}

void CKIIS::RunWork() {

    IHttpContext3* pHttpContext3;
    HttpGetExtendedInterface(this->_server, this->_context, &pHttpContext3);

    IWebSocketContext* cts = (IWebSocketContext*)pHttpContext3->GetNamedContextContainer()->GetNamedContext(L"websockets");
    
    for (;;) {
        // Loop to read/write the socket... 
        
        // If I call cts->ReadFragment() or cts->WriteFragment() here
        // the method will return E_NOTIMPL too.

        /// Eventually break out of the loop.
    }

    try {
        //this->_context->IndicateCompletion(RQ_NOTIFICATION_FINISH_REQUEST);
        this->_context->PostCompletion(0);
    }
    catch(const std::exception& e){
        const char* barf = e.what();
        std::cout << e.what();
    }
}

Few questions:

  • Why does ReadFragment/WriteFragment return E_NOTIMPL. Both in the OnBeginRequest method, or if I attempt to do it in its own thread.
  • Is using a new thread to handle the read/writing to the websocket the preferred pattern here? Having a busy loop like that polling for data seems pretty wasteful, but I cannot see any way to register for notifications when data comes available from the client
0 Answers
Related