Sample code for 30+ languages & platforms
C

Firebase Receive Server-Sent Events (text/event-stream)

See more Firebase Examples

Demonstrates how to start receiving server-sent events and update your JSON database with each event.

Chilkat C Downloads

C
#include <C_CkFileAccess.h>
#include <C_CkRest.h>
#include <C_CkAuthGoogle.h>
#include <C_CkUrl.h>
#include <C_CkJsonObject.h>
#include <C_CkStream.h>
#include <C_CkServerSentEvent.h>
#include <C_CkTask.h>

void ChilkatSample(void)
    {
    BOOL success;
    HCkFileAccess fac;
    const char *accessToken;
    HCkRest rest;
    HCkAuthGoogle authGoogle;
    const char *responseBody;
    const char *urlStr;
    HCkUrl url;
    HCkRest rest2;
    int responseStatusCode;
    HCkJsonObject jsonDb;
    HCkStream eventStream;
    HCkServerSentEvent sse;
    HCkTask task;
    int count;
    const char *eventStr;

    success = FALSE;

    //  Demonstrates how to begin receiving server-sent events, and to update
    //  your JSON database for each event.

    //  This example requires the Chilkat API to have been previously unlocked.
    //  See Global Unlock Sample for sample code.

    //  This example assumes a JWT authentication token, if required, has been previously obtained.
    //  See Get Firebase Access Token from JSON Service Account Private Key for sample code.

    //  Load the previously obtained Firebase access token into a string.
    fac = CkFileAccess_Create();
    accessToken = CkFileAccess_readEntireTextFile(fac,"qa_data/tokens/firebaseToken.txt","utf-8");
    if (CkFileAccess_getLastMethodSuccess(fac) == FALSE) {
        printf("%s\n",CkFileAccess_lastErrorText(fac));
        CkFileAccess_Dispose(fac);
        return;
    }

    rest = CkRest_Create();

    //  Make the initial connection (without sending a request yet).
    //  Once connected, any number of requests may be sent.  It is not necessary to explicitly
    //  call Connect before each request.  
    success = CkRest_Connect(rest,"chilkat.firebaseio.com",443,TRUE,TRUE);
    if (success == FALSE) {
        printf("%s\n",CkRest_lastErrorText(rest));
        CkFileAccess_Dispose(fac);
        CkRest_Dispose(rest);
        return;
    }

    authGoogle = CkAuthGoogle_Create();
    CkAuthGoogle_putAccessToken(authGoogle,accessToken);
    CkRest_SetAuthGoogle(rest,authGoogle);

    CkRest_AddHeader(rest,"Accept","text/event-stream");
    CkRest_AddHeader(rest,"Cache-Control","no-cache");

    responseBody = CkRest_fullRequestNoBody(rest,"GET","/.json");

    //  A 307 redirect response is expected.
    if (CkRest_getResponseStatusCode(rest) != 307) {
        printf("Unexpected response code: %d\n",CkRest_getResponseStatusCode(rest));
        printf("%s\n",responseBody);
        printf("Failed.\n");
        CkFileAccess_Dispose(fac);
        CkRest_Dispose(rest);
        CkAuthGoogle_Dispose(authGoogle);
        return;
    }

    //  Get the redirect URL
    urlStr = CkRest_lastRedirectUrl(rest);
    url = CkUrl_Create();
    CkUrl_ParseUrl(url,urlStr);

    printf("redirect URL domain: %s\n",CkUrl_host(url));
    printf("redirect URL path: %s\n",CkUrl_path(url));
    printf("redirect URL query params: %s\n",CkUrl_query(url));
    printf("redirect URL path with query params: %s\n",CkUrl_pathWithQueryParams(url));

    //  Our text/event-stream will be obtained from the redirect URL...
    rest2 = CkRest_Create();

    success = CkRest_Connect(rest2,CkUrl_host(url),443,TRUE,TRUE);
    if (success != TRUE) {
        printf("%s\n",CkRest_lastErrorText(rest2));
        CkFileAccess_Dispose(fac);
        CkRest_Dispose(rest);
        CkAuthGoogle_Dispose(authGoogle);
        CkUrl_Dispose(url);
        CkRest_Dispose(rest2);
        return;
    }

    CkRest_AddHeader(rest2,"Accept","text/event-stream");
    CkRest_AddHeader(rest2,"Cache-Control","no-cache");

    //  Add the redirect query params to the request
    CkRest_AddQueryParams(rest2,CkUrl_query(url));

    //  In our case, we don't actually need the auth query param,
    //  so remove it.
    CkRest_RemoveQueryParam(rest2,"auth");

    //  Send the request.  (We are only sending the request here.
    //  We are not yet getting the response because the response
    //  will be a text/event-stream.)
    success = CkRest_SendReqNoBody(rest2,"GET",CkUrl_path(url));
    if (success != TRUE) {
        printf("%s\n",CkRest_lastErrorText(rest2));
        CkFileAccess_Dispose(fac);
        CkRest_Dispose(rest);
        CkAuthGoogle_Dispose(authGoogle);
        CkUrl_Dispose(url);
        CkRest_Dispose(rest2);
        return;
    }

    //  Read the response header.  
    //  We want to first get the response header to see if it's a successful
    //  response status code.  If not, then the response will not be a text/event-stream
    //  and we should read the response body normally.
    responseStatusCode = CkRest_ReadResponseHeader(rest2);
    if (responseStatusCode < 0) {
        printf("%s\n",CkRest_lastErrorText(rest2));
        CkFileAccess_Dispose(fac);
        CkRest_Dispose(rest);
        CkAuthGoogle_Dispose(authGoogle);
        CkUrl_Dispose(url);
        CkRest_Dispose(rest2);
        return;
    }

    //  If successful, a 200 response code is expected.
    //  If the reponse code is not 200, then read the response body and fail..
    if (responseStatusCode != 200) {
        printf("Response Code: %d\n",responseStatusCode);
        printf("Response Status Text: %s\n",CkRest_responseStatusText(rest2));
        printf("Response Header: %s\n",CkRest_responseHeader(rest2));
        responseBody = CkRest_readRespBodyString(rest2);
        if (CkRest_getLastMethodSuccess(rest2) == TRUE) {
            printf("Error Response Body: %s\n",responseBody);
        }

        printf("Failed.\n");
        CkFileAccess_Dispose(fac);
        CkRest_Dispose(rest);
        CkAuthGoogle_Dispose(authGoogle);
        CkUrl_Dispose(url);
        CkRest_Dispose(rest2);
        return;
    }

    //  For this example, our JSON database will be empty at the beginning.
    //  The incoming events (put and patch) will be applied to this database.
    jsonDb = CkJsonObject_Create();

    //  Make sure to set the JSON path delimiter to "/".  The default is "." and this
    //  is not compatible with Firebase paths.
    CkJsonObject_putDelimiterChar(jsonDb,"/");

    //  At this point, we've received the response header.  Now it's time to begin
    //  receiving the event stream.  We'll start a background thread to read the 
    //  stream.  (Our main application (foreground) thread can cancel it at any time.)  
    //  While receiving in the background thread, our foreground thread can read the stream
    //  as it desires..
    eventStream = CkStream_Create();

    //  This sse object will be used as a helper to parse the server-sent event stream.
    sse = CkServerSentEvent_Create();

    task = CkRest_ReadRespBodyStreamAsync(rest2,eventStream,TRUE);
    CkTask_Run(task);

    //  For this example, we'll just read a few events, and then cancel the
    //  async task.
    count = 0;
    while ((count < 3) && (CkTask_getFinished(task) == FALSE)) {

        //  Get the next event, which is a series of text lines ending with
        //  a blank line. 
        //  Note: This method blocks the calling thread until a message arrives.
        //  a program might instead periodically check the availability of
        //  data via the stream's DataAvailable property, and then do the read.

        //  An alternative to writing a while loop to read the event stream
        //  would be to setup some sort of timer event in your program (using whatever timer functionality
        //  is provided in a programming language/environment), to periodically check the eventStream's
        //  DataAvailable property and consume the incoming event.
        eventStr = CkStream_readUntilMatch(eventStream,"\r\n\r\n");
        if (CkStream_getLastMethodSuccess(eventStream) != TRUE) {
            printf("%s\n",CkStream_lastErrorText(eventStream));
            //  Force the loop to exit by setting the count to a high number.
            count = 99999;
        }
        else {
            printf("Event: [%s]\n",eventStr);

            //  We have an event. Let's update our local copy of the JSON database.
            success = CkServerSentEvent_LoadEvent(sse,eventStr);
            if (success != TRUE) {
                printf("Failed to load sse event: %s\n",eventStr);
            }
            else {
                //  Now we can easily access the event name and data, and apply it to our JSON database:
                success = CkJsonObject_FirebaseApplyEvent(jsonDb,CkServerSentEvent_eventName(sse),CkServerSentEvent_data(sse));
                if (success != TRUE) {
                    printf("Failed to apply event: %s: %s\n",CkServerSentEvent_eventName(sse),CkServerSentEvent_data(sse));
                }
                else {
                    printf("Successfully applied event: %s: %s\n",CkServerSentEvent_eventName(sse),CkServerSentEvent_data(sse));
                }

            }

        }

        count = count + 1;
    }

    //  Make sure the background task is cancelled if still running.
    CkTask_Cancel(task);

    CkTask_Dispose(task);

    //  Examine the JSON database after applying events..
    CkJsonObject_putEmitCompact(jsonDb,FALSE);
    printf("----\n");
    printf("%s\n",CkJsonObject_emit(jsonDb));


    CkFileAccess_Dispose(fac);
    CkRest_Dispose(rest);
    CkAuthGoogle_Dispose(authGoogle);
    CkUrl_Dispose(url);
    CkRest_Dispose(rest2);
    CkJsonObject_Dispose(jsonDb);
    CkStream_Dispose(eventStream);
    CkServerSentEvent_Dispose(sse);

    }