GitHub Viewer
/*
* Copyright 2026 LiveKit
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and limitations.
*/
#include
#include
#include
#include
#include
#include "token_source_internal.h"
#if defined(_WIN32)
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include
#include
#else
#include
#endif
namespace livekit {
namespace {
#if !defined(_WIN32)
size_t curlWriteCallback(char* contents, size_t size, size_t nmemb, void* user_data) {
const size_t total_size = size * nmemb;
auto* response = static_cast(user_data);
response->append(contents, total_size);
return total_size;
}
#endif
std::string normalizeHttpMethod(std::string method) {
if (method.empty()) {
return "POST";
}
std::transform(method.begin(), method.end(), method.begin(),
[](unsigned char ch) { return static_cast(std::toupper(ch)); });
return method;
}
// Coerce a caller-supplied timeout into a positive millisecond count. Both
// WinHTTP and libcurl treat 0 as "wait forever" and reject negatives, neither
// of which is sensible for a token fetch, so non-positive values fall back to
// the 30s default.
std::int64_t effectiveTimeoutMs(std::chrono::milliseconds timeout) {
constexpr std::int64_t kDefaultTimeoutMs = 30000;
const std::int64_t count = timeout.count();
return count > 0 ? count : kDefaultTimeoutMs;
}
#if defined(_WIN32)
std::wstring toWide(const std::string& value) {
if (value.empty()) {
return L"";
}
const int length = MultiByteToWideChar(CP_UTF8, 0, value.c_str(), static_cast(value.size()), nullptr, 0);
if (length 0) {
object_name.append(components.lpszExtraInfo, components.dwExtraInfoLength);
}
const std::wstring wide_method = toWide(normalizeHttpMethod(method));
HINTERNET session = WinHttpOpen(L"LiveKit-CPP/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS, 0);
if (session == nullptr) {
return Result::failure("WinHttpOpen failed");
}
const int timeout_ms = static_cast(effectiveTimeoutMs(timeout));
WinHttpSetTimeouts(session, timeout_ms, timeout_ms, timeout_ms, timeout_ms);
HINTERNET connection = WinHttpConnect(session, host.c_str(), components.nPort, 0);
if (connection == nullptr) {
WinHttpCloseHandle(session);
return Result::failure("WinHttpConnect failed");
}
const DWORD flags = (components.nScheme == INTERNET_SCHEME_HTTPS) ? WINHTTP_FLAG_SECURE : 0;
HINTERNET request = WinHttpOpenRequest(connection, wide_method.c_str(), object_name.c_str(), nullptr,
WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, flags);
if (request == nullptr) {
WinHttpCloseHandle(connection);
WinHttpCloseHandle(session);
return Result::failure("WinHttpOpenRequest failed");
}
std::wstring header_block = L"Content-Type: application/json\r\n";
for (const auto& [key, value] : headers) {
header_block += toWide(key);
header_block += L": ";
header_block += toWide(value);
header_block += L"\r\n";
}
const BOOL send_ok =
WinHttpSendRequest(request, header_block.c_str(), static_cast(-1L), const_cast(json_body.data()),
static_cast(json_body.size()), static_cast(json_body.size()), 0);
if (!send_ok) {
WinHttpCloseHandle(request);
WinHttpCloseHandle(connection);
WinHttpCloseHandle(session);
return Result::failure("WinHttpSendRequest failed");
}
if (!WinHttpReceiveResponse(request, nullptr)) {
WinHttpCloseHandle(request);
WinHttpCloseHandle(connection);
WinHttpCloseHandle(session);
return Result::failure("WinHttpReceiveResponse failed");
}
DWORD status_code = 0;
DWORD status_size = sizeof(status_code);
WinHttpQueryHeaders(request, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX,
&status_code, &status_size, WINHTTP_NO_HEADER_INDEX);
std::string response_body;
DWORD available = 0;
do {
if (!WinHttpQueryDataAvailable(request, &available)) {
break;
}
if (available == 0) {
break;
}
std::string chunk(available, '\0');
DWORD read = 0;
if (!WinHttpReadData(request, chunk.data(), available, &read)) {
break;
}
chunk.resize(read);
response_body += chunk;
} while (available > 0);
WinHttpCloseHandle(request);
WinHttpCloseHandle(connection);
WinHttpCloseHandle(session);
if (status_code < 200 || status_code >= 300) {
std::ostringstream message;
message