|
| 1 | +# Copilot Instructions — libHttpClient |
| 2 | + |
| 3 | +## Project Overview |
| 4 | + |
| 5 | +libHttpClient is a **cross-platform C/C++ library** providing a platform abstraction layer for **HTTP** and **WebSocket** communication. It is used by Xbox Live Service API (XSAPI) and the PlayFab SDK. |
| 6 | + |
| 7 | +- **Language:** C/C++ (C++17 standard required) |
| 8 | +- **Public API:** Flat C API — all public functions use `HC*` prefix with C linkage (`STDAPI`) |
| 9 | +- **Platforms:** Win32, UWP, GDK (Xbox/PC gaming), XDK (legacy Xbox One), iOS, macOS, Android, Linux |
| 10 | +- **Build systems:** MSBuild (Windows), CMake (Linux/Android), Xcode (iOS/macOS) |
| 11 | +- **CI:** Azure DevOps (see `Utilities/Pipelines/libHttpClient.CI.yml`) |
| 12 | + |
| 13 | +## Architecture |
| 14 | + |
| 15 | +``` |
| 16 | +Include/httpClient/ — Public C API headers (httpClient.h, httpProvider.h, mock.h, trace.h, async.h) |
| 17 | +Include/ — XAsync.h, XAsyncProvider.h, XTaskQueue.h (async task queue API) |
| 18 | +Source/ |
| 19 | + Common/ — Shared utilities, types, Result<T>, error macros, pch, memory allocator |
| 20 | + Global/ — Global state, singleton, custom memory (mem.h) |
| 21 | + HTTP/ — Core HTTP call logic, retry, compression |
| 22 | + WinHttp/ — Win32/GDK HTTP provider (WinHTTP) |
| 23 | + XMLHttp/ — UWP HTTP provider |
| 24 | + Curl/ — Linux HTTP provider (libcurl) |
| 25 | + Android/ — Android HTTP provider (JNI bridge) |
| 26 | + Apple/ — iOS/macOS HTTP provider |
| 27 | + WebSocket/ — Core WebSocket logic |
| 28 | + Websocketpp/ — Linux WebSocket provider (websocketpp + asio) |
| 29 | + Android/ — Android WebSocket provider |
| 30 | + Mock/ — Mock HTTP/WebSocket layer for testing |
| 31 | + Platform/ — Platform initialization (PlatformComponents pattern) |
| 32 | + Win32/, UWP/, GDK/, XDK/, Android/, Apple/, Linux/, Generic/ |
| 33 | + SSL/ — SSL/TLS support |
| 34 | + Task/ — Async task infrastructure |
| 35 | + Logger/ — Logging/tracing |
| 36 | +Build/ — Platform-specific build projects (.vcxproj, CMakeLists.txt, .xcworkspace) |
| 37 | +Tests/UnitTests/ — TAEF/TE unit tests |
| 38 | +External/ — Git submodules: openssl, curl, websocketpp, asio, zlib |
| 39 | +Samples/ — Sample apps (Win32, UWP, GDK) |
| 40 | +Utilities/Pipelines/ — Azure DevOps CI pipeline definitions |
| 41 | +``` |
| 42 | + |
| 43 | +### Platform Abstraction Pattern |
| 44 | + |
| 45 | +Each platform implements `PlatformInitialize()` in `Source/Platform/<Platform>/PlatformComponents_<Platform>.cpp`, which creates platform-specific `IHttpProvider` and `IWebSocketProvider` implementations. Platform selection is controlled by: |
| 46 | + |
| 47 | +- **MSBuild:** `platform_select.props` auto-detects based on `ApplicationType`/`Platform` → sets `HC_PLATFORM_MSBUILD_GUESS` |
| 48 | +- **Code:** `HC_PLATFORM` preprocessor constant (e.g., `HC_PLATFORM_WIN32`, `HC_PLATFORM_GDK`, `HC_PLATFORM_ANDROID`) |
| 49 | +- **Feature flags:** `HC_NOZLIB`, `HC_NOWEBSOCKETS` to exclude optional features |
| 50 | + |
| 51 | +## Coding Conventions |
| 52 | + |
| 53 | +### Naming |
| 54 | + |
| 55 | +| Element | Convention | Example | |
| 56 | +|---------|-----------|---------| |
| 57 | +| Public C API functions | `HC` prefix, PascalCase | `HCHttpCallCreate()`, `HCWebSocketConnectAsync()` | |
| 58 | +| Internal functions | camelCase | `ShouldRetry()`, `ResetResponseProperties()` | |
| 59 | +| Member variables | `m_` prefix | `m_provider`, `m_refCount` | |
| 60 | +| Types/Classes | PascalCase | `HC_CALL`, `WinHttpProvider`, `Result<T>` | |
| 61 | +| Constants/Macros | UPPER_SNAKE_CASE | `MAX_DELAY_TIME_IN_SEC`, `HC_PLATFORM_WIN32` | |
| 62 | +| File names | PascalCase or descriptive | `PlatformComponents_Win32.cpp`, `httpcall.h` | |
| 63 | + |
| 64 | +### Namespaces |
| 65 | + |
| 66 | +```cpp |
| 67 | +NAMESPACE_XBOX_HTTP_CLIENT_BEGIN // namespace xbox { namespace httpclient { |
| 68 | +NAMESPACE_XBOX_HTTP_CLIENT_END // }} |
| 69 | +``` |
| 70 | + |
| 71 | +Sub-namespaces: `xbox::httpclient::log`, `xbox::httpclient::detail`, `xbox::httpclient::test` |
| 72 | + |
| 73 | +### Error Handling |
| 74 | + |
| 75 | +- All functions return `HRESULT` (S_OK on success) |
| 76 | +- **No exceptions thrown** from public API — all functions are `noexcept` with `try {} CATCH_RETURN()` wrapping |
| 77 | +- Use error macros from `Source/Common/ResultMacros.h`: |
| 78 | + - `RETURN_IF_FAILED(hr)` — early return on failure |
| 79 | + - `RETURN_HR_IF(hr, condition)` — conditional return |
| 80 | + - `RETURN_IF_NULL_ALLOC(ptr)` — returns E_OUTOFMEMORY if null |
| 81 | + - `LOG_IF_FAILED(hr)` — log without returning |
| 82 | +- Internal result type: `Result<T>` (Source/Common/Result.h) — wraps HRESULT + optional payload + error message |
| 83 | + |
| 84 | +### Memory Management |
| 85 | + |
| 86 | +- Caller-controlled allocation via `HCMemSetFunctions()` callback |
| 87 | +- Use custom allocator types from `Source/Common/Types.h`: |
| 88 | + - `http_internal_string`, `http_internal_vector<T>`, `http_internal_map<K,V>` |
| 89 | + - `HC_UNIQUE_PTR<T>`, `SharedPtr<T>`, `UniquePtr<T>` |
| 90 | + - `http_allocate_unique<T>(...)` for creating unique_ptr with custom allocator |
| 91 | +- **Never use raw `new`/`delete`** — use `Make<T>()`/`Delete<T>()` from `Source/Global/mem.h` |
| 92 | +- RAII patterns for all resource management |
| 93 | + |
| 94 | +### Headers |
| 95 | + |
| 96 | +- Use `#pragma once` (no traditional include guards) |
| 97 | +- Include order: `pch.h` → own header → internal headers (quoted) → platform headers (angle brackets) → STL |
| 98 | +- Public API headers use angle brackets: `#include <httpClient/httpClient.h>` |
| 99 | +- Internal headers use quoted relative paths: `#include "HTTP/httpcall.h"` |
| 100 | + |
| 101 | +### Other Patterns |
| 102 | + |
| 103 | +- Atomic reference counting for handle types (`std::atomic<int> refCount`) |
| 104 | +- Copy/move constructors deleted on handle types |
| 105 | +- Static `CALLBACK` functions for C-style callback bridges |
| 106 | +- Conditional compilation via `#if HC_PLATFORM == HC_PLATFORM_*` |
| 107 | +- Compiler warnings: Level 4, warnings as errors |
| 108 | +- Security: SDL checks, Control Flow Guard (`/guard:cf`), ASLR |
| 109 | + |
| 110 | +## Build Commands |
| 111 | + |
| 112 | +### Windows (MSBuild) |
| 113 | + |
| 114 | +```powershell |
| 115 | +# VS2022 — Win32 x64 Debug |
| 116 | +msbuild libHttpClient.vs2022.sln /p:Configuration=Debug /p:Platform=x64 |
| 117 | +
|
| 118 | +# VS2022 — Win32 x64 Release |
| 119 | +msbuild libHttpClient.vs2022.sln /p:Configuration=Release /p:Platform=x64 |
| 120 | +
|
| 121 | +# VS2022 — ARM64 |
| 122 | +msbuild libHttpClient.vs2022.sln /p:Configuration=Debug /p:Platform=ARM64 |
| 123 | +
|
| 124 | +# GDK target |
| 125 | +msbuild libHttpClient.vs2022.sln /p:Configuration=Debug /p:Platform=Gaming.Desktop.x64 |
| 126 | +``` |
| 127 | + |
| 128 | +Or open `libHttpClient.vs2022.sln` in Visual Studio and build from the IDE. |
| 129 | + |
| 130 | +**Build configuration flags** (set in `hc_settings.props`, copy from `hc_settings.props.example`): |
| 131 | +- `HCNoZlib=true` — exclude zlib/compression |
| 132 | +- `HCNoWebSockets=true` — exclude WebSocket APIs |
| 133 | +- `HCExternalOpenSSL=true` — use external OpenSSL binaries instead of bundled |
| 134 | + |
| 135 | +### Linux (CMake) |
| 136 | + |
| 137 | +```bash |
| 138 | +# Uses build scripts in Utilities/Pipelines/Scripts/ |
| 139 | +# Build OpenSSL, curl, then libHttpClient: |
| 140 | +bash Utilities/Pipelines/Scripts/openssl_Linux.bash -c Debug |
| 141 | +bash Utilities/Pipelines/Scripts/curl_Linux.bash -c Debug |
| 142 | +bash Utilities/Pipelines/Scripts/libHttpClient_Linux.bash -c Debug -st # static lib |
| 143 | +bash Utilities/Pipelines/Scripts/libHttpClient_Linux.bash -c Debug # shared lib |
| 144 | +``` |
| 145 | + |
| 146 | +### iOS/macOS (Xcode) |
| 147 | + |
| 148 | +```bash |
| 149 | +# Workspace: Build/libHttpClient.Apple.C/libHttpClient.xcworkspace |
| 150 | +# Schemes: libHttpClient, libHttpClient_NOWEBSOCKETS |
| 151 | +xcodebuild -workspace Build/libHttpClient.Apple.C/libHttpClient.xcworkspace \ |
| 152 | + -scheme libHttpClient -sdk iphoneos -configuration Debug clean build |
| 153 | +``` |
| 154 | + |
| 155 | +### Android (Gradle + NDK) |
| 156 | + |
| 157 | +```bash |
| 158 | +cd Build/libHttpClient.Android.Workspace |
| 159 | +./gradlew assembleDebug |
| 160 | +``` |
| 161 | + |
| 162 | +## Testing |
| 163 | + |
| 164 | +### Unit Tests (TAEF/TE) |
| 165 | + |
| 166 | +Test files are in `Tests/UnitTests/Tests/`: |
| 167 | +- `HttpTests.cpp`, `WebsocketTests.cpp`, `MockTests.cpp`, `GlobalTests.cpp` |
| 168 | +- `TaskQueueTests.cpp`, `AsyncBlockTests.cpp`, `LocklessQueueTests.cpp`, `ProxyTests.cpp` |
| 169 | +- `BufferSize/` — buffer size unit and E2E tests |
| 170 | + |
| 171 | +**Test frameworks:** Both TAEF and TE (Visual Studio CppUnitTest) are supported via macro abstraction in `Tests/UnitTests/Support/DefineTestMacros.h`. |
| 172 | + |
| 173 | +**Running tests:** |
| 174 | + |
| 175 | +```powershell |
| 176 | +# TAEF from command line (after building the test DLL) |
| 177 | +te.exe Out\x64\Debug\libHttpClient.UnitTest.TAEF\libHttpClient.UnitTest.TAEF.dll |
| 178 | +
|
| 179 | +# Or use Visual Studio Test Explorer with the TE project |
| 180 | +``` |
| 181 | + |
| 182 | +**Test patterns:** |
| 183 | +- Use `DEFINE_TEST_CLASS(Name)` / `DEFINE_TEST_CASE(Name)` macros (not raw TEST_CLASS/TEST_METHOD) |
| 184 | +- Use `VERIFY_ARE_EQUAL`, `VERIFY_SUCCEEDED(hr)`, `VERIFY_IS_TRUE` assertions |
| 185 | +- Async tests use `PumpedTaskQueue` helper (creates manual-dispatch XTaskQueue with worker threads) |
| 186 | +- Mock HTTP responses via `HCMockCallCreate()` + `HCMockResponseSet*()` APIs |
| 187 | +- Callback bridging via `CallbackThunk<T, R>` template |
| 188 | + |
| 189 | +### CI Pipeline |
| 190 | + |
| 191 | +Azure DevOps pipeline at `Utilities/Pipelines/libHttpClient.CI.yml`: |
| 192 | +- **Triggers:** Push to `main`, PRs to `main`/`releases/*`, nightly at 8am UTC |
| 193 | +- **Matrix:** Win32/UWP VS2022 (x86/x64/ARM64 × Debug/Release), iOS (Debug/Release), Linux (Debug/Release) |
| 194 | + |
| 195 | +## External Dependencies |
| 196 | + |
| 197 | +All managed as git submodules in `External/`: |
| 198 | +- **openssl** — SSL/TLS |
| 199 | +- **curl** — HTTP for Linux |
| 200 | +- **websocketpp** — WebSocket for Linux |
| 201 | +- **asio** — Async I/O for websocketpp |
| 202 | +- **zlib** — Compression |
| 203 | + |
| 204 | +After cloning, run: `git submodule update --init --recursive` |
| 205 | + |
| 206 | +## Key Rules |
| 207 | + |
| 208 | +1. **All public API functions must be `noexcept`** with `try {} CATCH_RETURN()` wrapping |
| 209 | +2. **Never throw exceptions** from public API — use HRESULT error codes |
| 210 | +3. **Use custom allocators** — never raw `new`/`delete`; use `http_internal_*` types and `Make<T>`/`Delete<T>` |
| 211 | +4. **Platform code stays isolated** — platform-specific logic goes in `Source/Platform/<Platform>/` or `Source/HTTP/<Provider>/` |
| 212 | +5. **All new functionality needs unit tests** — use DEFINE_TEST_CLASS/DEFINE_TEST_CASE macros |
| 213 | +6. **Submit PRs against the development branch**, not main |
| 214 | +7. **Keep changes small** — avoid unnecessary deltas |
0 commit comments