Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/windows-driver.md
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,12 @@ portal URL changes, then rebuild the Windows package. No Polar access token or
webhook secret is compiled into the client:
activation, validation, and deactivation use Polar's
[public customer license-key API](https://polar.sh/docs/features/benefits/license-keys).
Those requests pin Polar's date-based API contract to `2026-04` with the
`Polar-Version` header. Before Polar removes that version, update
`polar_request_headers` in
`src/platform/windows/broker/libvirtualhid_broker.cpp`, review Polar's
[API versioning guidance](https://polar.sh/docs/api-reference/versioning), and
validate the license response contract before rebuilding the Windows package.

The production configuration accepts organization
`3db9f05a-44d7-42f1-ba7c-a0f198235fb7` with yearly license-key benefit
Expand Down
7 changes: 5 additions & 2 deletions src/platform/windows/broker/libvirtualhid_broker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ namespace lvh::detail::windows_broker_service {
constexpr int polar_connect_timeout = 5000;
constexpr int polar_send_timeout = 5000;
constexpr int polar_receive_timeout = 10000;
constexpr auto polar_request_headers =
L"Accept: application/json\r\n"
L"Content-Type: application/json\r\n"
L"Polar-Version: 2026-04\r\n";
constexpr auto license_validation_interval = std::chrono::days {1};
constexpr auto license_validation_retry_interval = std::chrono::seconds {60};
constexpr auto license_outage_device_retention = std::chrono::hours {1};
Expand Down Expand Up @@ -944,8 +948,7 @@ namespace lvh::detail::windows_broker_service {
return result;
}

auto body = request_body.dump(-1, ' ', false, nlohmann::json::error_handler_t::replace);
if (constexpr auto headers = L"Accept: application/json\r\nContent-Type: application/json\r\n"; ::WinHttpSendRequest(request.get(), headers, static_cast<DWORD>(-1), body.data(), static_cast<DWORD>(body.size()), static_cast<DWORD>(body.size()), 0) == FALSE) {
if (auto body = request_body.dump(-1, ' ', false, nlohmann::json::error_handler_t::replace); ::WinHttpSendRequest(request.get(), polar_request_headers, static_cast<DWORD>(-1), body.data(), static_cast<DWORD>(body.size()), static_cast<DWORD>(body.size()), 0) == FALSE) {
result.error = "WinHttpSendRequest failed: " + windows_error_message(::GetLastError());
return result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ namespace lvh::detail::test {
int connect_timeout = 0;
int send_timeout = 0;
int receive_timeout = 0;
std::wstring request_headers;
std::string body;
std::string error;
};
Expand Down
17 changes: 14 additions & 3 deletions tests/fixtures/windows_broker_service_test_hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ namespace {
int connect_timeout = 0;
int send_timeout = 0;
int receive_timeout = 0;
std::wstring request_headers;
};

BrokerServiceTestState &broker_service_test_state() {
Expand Down Expand Up @@ -285,14 +286,22 @@ namespace {

BOOL WINAPI broker_test_win_http_send_request(
HINTERNET,
LPCWSTR,
DWORD,
LPCWSTR headers,
DWORD headers_length,
std::byte *,
DWORD,
DWORD,
DWORD_PTR
) {
if (broker_service_test_state().polar_scenario == BrokerPolarScenario::send_failure) {
auto &state = broker_service_test_state();
if (headers == nullptr) {
state.request_headers.clear();
} else if (headers_length == static_cast<DWORD>(-1)) {
state.request_headers = headers;
} else {
state.request_headers.assign(headers, headers_length);
}
if (state.polar_scenario == BrokerPolarScenario::send_failure) {
::SetLastError(ERROR_WINHTTP_CONNECTION_ERROR);
return FALSE;
}
Expand Down Expand Up @@ -466,6 +475,7 @@ namespace lvh::detail::test {
state.connect_timeout = 0;
state.send_timeout = 0;
state.receive_timeout = 0;
state.request_headers.clear();
const auto result =
lvh::detail::windows_broker_service::post_polar_license_request(
L"/test",
Expand All @@ -480,6 +490,7 @@ namespace lvh::detail::test {
.connect_timeout = state.connect_timeout,
.send_timeout = state.send_timeout,
.receive_timeout = state.receive_timeout,
.request_headers = state.request_headers,
.body = result.body,
.error = result.error,
};
Expand Down
1 change: 1 addition & 0 deletions tests/unit/test_windows_broker_service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,7 @@ TEST(WindowsBrokerImplementationTest, ReportsDpapiAndPolarFailures) {
EXPECT_EQ(succeeded.connect_timeout, 5000);
EXPECT_EQ(succeeded.send_timeout, 5000);
EXPECT_EQ(succeeded.receive_timeout, 10000);
EXPECT_NE(succeeded.request_headers.find(L"Polar-Version: 2026-04\r\n"), std::wstring::npos);
}

TEST(WindowsBrokerImplementationTest, BoundsOfflineSubscriptionValidation) {
Expand Down
Loading