Last active
June 26, 2023 06:25
-
-
Save jgcoded/080dda1e83c1533cfd33c8ce6ec541df to your computer and use it in GitHub Desktop.
C++/WinRT Server-sent Events Implementation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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