How to detect a closed socket on the MQX httpsrv

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

How to detect a closed socket on the MQX httpsrv

Jump to solution
1,495 Views
lfschrickte
Contributor IV

Hi folks,

I'm trying to use the HTML5 Server-Sent Events approach for event streams (single direction) for a specific application I have here.

For those who are not familiar with the HTML5 Server-Sent Event approach (like me two days ago), I recommend this reading: HTML5 Server-Sent Events

Basically a client access a HTML page in the server which triggers a javascript specific code that keeps accepting incoming data from the server. The HTTP headers are passed by the server just in the first response, with Content-Type set to text/event-stream. After that all data is sent almost "as-is", with no overhead, except for some format conventions (data: nonono\n\n)

MQX httpsrv doesn't have native support for this kind of communication, but I've figured out a way to make things work:

1. I'm using the MQX httpsrv CGI support

2. Every time the cgi function is called, I detach a task for its execution (stack > 0 in the cgi_tbl) and the cgi handler enters in an infinite loop, which calls the HTTPSRV_cgi_write(&response); function every time an event happens in my application (using a sync mechanism)

3. As the internal httpsrv_write function bufferizes the data on the session buffer until it is full, this didn't work in the beginning, as the data were only being sent when the buffer get full indeed. The workaround for this is to call the (private) httpsrv_ses_flush((void *)param->ses_handle); after the HTTPSRV_cgi_write function, so the server forces a flush.

Well, things are working nice, but I've just one small issue! I can't detect when the client is not connected to the server anymore!

When I close the client, I can see in wireshark that the connection is closed - no more data is exchanged, however the HTTPSRV_cgi_write and the httpsrv_ses_flush functions in the server continue to return success values, so I've no way to find out that the socket is no longer active!

I've an ugly solution, which is to simply 'break' the while loop in the CGI script periodically, finishing the CGI handler execution, so the client must reconnect to the server (it does it automatically, another feature of the HTTP SSE approach) and, if the client is no longer available, nobody gets the events and everything is fine. However I wish there was a way to detect that the connection was lost right after the "FIN, FIN,ACK" packets were exchanged and the connection was closed. Is there a way to do this? I've already noted that the send function is not returning error, even after the connection is lost... so how can I find out that the tcp connection is already closed?

Maybe support the HTTP Server-Sent Events approach in the httpsrv default implementation in the future would be a nice idea! I think that with little effort it is possible add this support (a flag in the CGI Write call to flush data would be a beginning + http text/event-stream type support in the default content types list).

Thank you very much!

Luiz Fernando

Labels (1)
0 Kudos
Reply
1 Solution
1,116 Views
lfschrickte
Contributor IV

Thank you Mr. Mozny,

Looking at the MQX http_ses_flush function I think I've found a possible bug! It is written in the function that it should return the number of bytes flushed. However it always returns zero!

Here is the original code:

uint32_t httpsrv_ses_flush(HTTPSRV_SESSION_STRUCT *session)

{

    uint32_t length = 0;

  

    while (length != session->buffer.offset)

    {

        uint32_t remaining;

        length = send(session->sock, session->buffer.data, session->buffer.offset, 0);

  

        if (length == RTCS_ERROR)

        {

            session->state = HTTPSRV_SES_CLOSE;

            length = 0;

            break;

        }

        remaining = session->buffer.offset - length;

        if (remaining > 0)

        {

            _mem_zero(session->buffer.data, length);

            memmove(session->buffer.data, session->buffer.data+length, remaining);

            session->buffer.offset = remaining;

        }

        else

        {

            _mem_zero(session->buffer.data, session->buffer.offset);

            session->buffer.offset = 0;

        }

    }

    return(length);

}

Let's suppose we call the flush function when there is 9 bytes in the buffer, so session->buffer.offset == 9

The send call returns 9 on line 09., so length = 9. The if condition on line 11. does not hold as there is no error. As the remaining value is 9, the 'else' case is executed on the next condition, cleaning the buffer and putting session->buffer.offset to 0.

I suppose the function should return at this point, but the while condition is then verified again, and as the buffer.offset is set to 0 and length var holds 9, it executes the while loop again! The send call returns 0 (as session->buffer.offset is 0), length assumes 0 value and as a consequence the while condition holds and the function returns 0 (length). This is obviously hiding the quantity of data sent, and as it returns 0 regardless of what happened, I can't know when the send call "fails"! (the original reason of this question)

I've changed the code, putting a break; after line 29., and now I can know when the connection has dropped (the flush call returns 0). The new code is:

uint32_t httpsrv_ses_flush(HTTPSRV_SESSION_STRUCT *session)

{

    uint32_t length = 0;

  

    while (length != session->buffer.offset)

    {

        uint32_t remaining;

        length = send(session->sock, session->buffer.data, session->buffer.offset, 0);

  

        if (length == RTCS_ERROR)

        {

            session->state = HTTPSRV_SES_CLOSE;

            length = 0;

            break;

        }

        remaining = session->buffer.offset - length;

        if (remaining > 0)

        {

            _mem_zero(session->buffer.data, length);

            memmove(session->buffer.data, session->buffer.data+length, remaining);

            session->buffer.offset = remaining;

        }

        else

        {

            _mem_zero(session->buffer.data, session->buffer.offset);

            session->buffer.offset = 0;

            break; /* I've inserted this line */

        }

    }

    return(length);

}

About websockets: when is the next MQX release up to? Is there any way to get the websockets code earlier? Maybe it is even more adequate to my application than HTML5 SSE!

Thank you very much for your response!

Luiz Fernando

View solution in original post

0 Kudos
Reply
3 Replies
1,116 Views
karelm_
Contributor IV

Hi,

this sounds like a thing that can be achieved using WebSocket protocol (WebSocket.org | About WebSocket). We already have support for it in HTTPSRV and it will be released in next MQX version. I will add task for native implementation of SSE to our backlog, but I cannot promise you anything. Regarding closed connection: If client is disconnected, RTCS socket functions recv and send return RTCS_ERROR.

Best regards,

Karel.

1,117 Views
lfschrickte
Contributor IV

Thank you Mr. Mozny,

Looking at the MQX http_ses_flush function I think I've found a possible bug! It is written in the function that it should return the number of bytes flushed. However it always returns zero!

Here is the original code:

uint32_t httpsrv_ses_flush(HTTPSRV_SESSION_STRUCT *session)

{

    uint32_t length = 0;

  

    while (length != session->buffer.offset)

    {

        uint32_t remaining;

        length = send(session->sock, session->buffer.data, session->buffer.offset, 0);

  

        if (length == RTCS_ERROR)

        {

            session->state = HTTPSRV_SES_CLOSE;

            length = 0;

            break;

        }

        remaining = session->buffer.offset - length;

        if (remaining > 0)

        {

            _mem_zero(session->buffer.data, length);

            memmove(session->buffer.data, session->buffer.data+length, remaining);

            session->buffer.offset = remaining;

        }

        else

        {

            _mem_zero(session->buffer.data, session->buffer.offset);

            session->buffer.offset = 0;

        }

    }

    return(length);

}

Let's suppose we call the flush function when there is 9 bytes in the buffer, so session->buffer.offset == 9

The send call returns 9 on line 09., so length = 9. The if condition on line 11. does not hold as there is no error. As the remaining value is 9, the 'else' case is executed on the next condition, cleaning the buffer and putting session->buffer.offset to 0.

I suppose the function should return at this point, but the while condition is then verified again, and as the buffer.offset is set to 0 and length var holds 9, it executes the while loop again! The send call returns 0 (as session->buffer.offset is 0), length assumes 0 value and as a consequence the while condition holds and the function returns 0 (length). This is obviously hiding the quantity of data sent, and as it returns 0 regardless of what happened, I can't know when the send call "fails"! (the original reason of this question)

I've changed the code, putting a break; after line 29., and now I can know when the connection has dropped (the flush call returns 0). The new code is:

uint32_t httpsrv_ses_flush(HTTPSRV_SESSION_STRUCT *session)

{

    uint32_t length = 0;

  

    while (length != session->buffer.offset)

    {

        uint32_t remaining;

        length = send(session->sock, session->buffer.data, session->buffer.offset, 0);

  

        if (length == RTCS_ERROR)

        {

            session->state = HTTPSRV_SES_CLOSE;

            length = 0;

            break;

        }

        remaining = session->buffer.offset - length;

        if (remaining > 0)

        {

            _mem_zero(session->buffer.data, length);

            memmove(session->buffer.data, session->buffer.data+length, remaining);

            session->buffer.offset = remaining;

        }

        else

        {

            _mem_zero(session->buffer.data, session->buffer.offset);

            session->buffer.offset = 0;

            break; /* I've inserted this line */

        }

    }

    return(length);

}

About websockets: when is the next MQX release up to? Is there any way to get the websockets code earlier? Maybe it is even more adequate to my application than HTML5 SSE!

Thank you very much for your response!

Luiz Fernando

0 Kudos
Reply
1,116 Views
karelm_
Contributor IV

Hi Luiz,

Yes, this is a bug. We already fixed it on our development branch. WebSocket implementation will be part of Freescale KPSDK+MQX in circa 2 months (planned release). It should be possible to get code from this release and back-port it to MQX 4.1. If you want to get it sooner you might try to contact freescale support at Sales and Support|Freescale.

Best regards,

Karel.