Skip to content

Instantly share code, notes, and snippets.

@jgcoded
Last active June 26, 2023 06:25
Show Gist options
  • Save jgcoded/080dda1e83c1533cfd33c8ce6ec541df to your computer and use it in GitHub Desktop.
Save jgcoded/080dda1e83c1533cfd33c8ce6ec541df to your computer and use it in GitHub Desktop.
C++/WinRT Server-sent Events Implementation
// These should be in pch.h
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.Web.Http.h>
#include <winrt/Windows.Web.Http.Headers.h>
#include <winrt/Windows.Web.Http.Filters.h>
#include <winrt/Windows.Storage.Streams.h>
#include <iostream>
#include <Windows.h>
#include <synchapi.h>
#include <consoleapi.h>
#include <handleapi.h>
#include <processthreadsapi.h>
using namespace std;
using namespace winrt;
using namespace winrt::Windows::Web::Http;
using namespace winrt::Windows::Storage::Streams;
using namespace Windows::Foundation;
// Useful Readings:
// https://learn.microsoft.com/en-us/windows/uwp/cpp-and-winrt-apis/concurrency
// https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation
// https://learn.microsoft.com/en-us/archive/msdn-magazine/2018/june/c-effective-async-with-coroutines-and-c-winrt
winrt::handle g_CtrlCHandle;
IAsyncAction ProcessEventStream(DataReader dataReader)
{
// offload work onto the Windows thread pool
// https://learn.microsoft.com/en-us/windows/uwp/cpp-and-winrt-apis/concurrency-2#offloading-work-onto-the-windows-thread-pool
co_await resume_background();
// nested cancellation pattern described in:
// https://learn.microsoft.com/en-us/windows/uwp/cpp-and-winrt-apis/concurrency-2#register-a-cancellation-callback
auto ct { co_await winrt::get_cancellation_token() };
while (!ct())
{
DataReaderLoadOperation task = dataReader.LoadAsync(1024);
ct.callback([=]() {
task.Cancel();
});
co_await task;
auto status = task.Status();
if (status == AsyncStatus::Completed) {
auto bytesRead = task.GetResults();
hstring data = dataReader.ReadString(bytesRead);
wcout << data.c_str();
}
}
}
IAsyncAction MainAsync()
{
HttpClient httpClient{};
Uri uri{ L"https://localhost:7238/sse" };
// Set up the request per
// https://html.spec.whatwg.org/multipage/server-sent-events.html
HttpRequestMessage request{ HttpMethod::Get(), uri };
request.Headers().Accept().ParseAdd(L"text/event-stream");
request.Headers().CacheControl().ParseAdd(L"none");
auto response{ co_await httpClient.SendRequestAsync(request, HttpCompletionOption::ResponseHeadersRead) };
response.EnsureSuccessStatusCode();
auto stream = co_await response.Content().ReadAsInputStreamAsync();
DataReader dataReader{ stream };
dataReader.UnicodeEncoding(UnicodeEncoding::Utf8);
dataReader.InputStreamOptions(InputStreamOptions::Partial);
auto task = ProcessEventStream(dataReader);
// Run until ctrl-c is pressed
co_await resume_on_signal(g_CtrlCHandle.get());
task.Cancel();
}
int main()
{
init_apartment();
g_CtrlCHandle = winrt::handle{ ::CreateEvent(nullptr, true, false, nullptr) };
winrt::check_bool(SetConsoleCtrlHandler([](DWORD ctrlType) -> BOOL {
if (ctrlType == CTRL_C_EVENT)
{
cout << "Ctrl-C Received" << endl;
::SetEvent(g_CtrlCHandle.get());
return TRUE;
}
return FALSE;
}, true));
MainAsync().get();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment