### Start the TLSUV test server Source: https://github.com/openziti/tlsuv/blob/main/tests/Readme.md Executes the test server using the provided CA key and certificate files. Requires a working Go installation. ```console cd ./tests/test_server go run ./test-server.go -ca-key ../certs/server.key -ca ../certs/server.crt ``` -------------------------------- ### WebSocket Client Example Source: https://context7.com/openziti/tlsuv/llms.txt This example demonstrates how to initialize and connect to a WebSocket server using the tlsuv library. It includes callbacks for connection, data reception, and writing messages. Custom headers and TLS contexts can be optionally configured. ```c #include #include #include #include #include // Called to allocate buffers for incoming data static void alloc_cb(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { buf->base = malloc(suggested_size); buf->len = suggested_size; } // Called when WebSocket data is received static void ws_read_cb(uv_stream_t *h, ssize_t status, const uv_buf_t *buf) { if (status < 0) { if (status == UV_EOF) { printf("WebSocket connection closed by server\n"); } else { fprintf(stderr, "WebSocket read error: %s\n", uv_strerror((int)status)); } tlsuv_websocket_close((tlsuv_websocket_t *)h, NULL); return; } printf("Received: %.*s\n", (int)status, buf->base); free(buf->base); } // Called when write completes static void ws_write_cb(uv_write_t *req, int status) { if (status < 0) { fprintf(stderr, "WebSocket write error: %s\n", uv_strerror(status)); } free(req->data); // Free the message buffer free(req); } // Called when WebSocket connection is established static void ws_connect_cb(uv_connect_t *req, int status) { tlsuv_websocket_t *ws = (tlsuv_websocket_t *)req->handle; if (status != 0) { fprintf(stderr, "WebSocket connect failed: %s\n", uv_strerror(status)); return; } printf("WebSocket connected!\n"); // Send a message uv_write_t *wr = malloc(sizeof(uv_write_t)); const char *msg = "Hello, WebSocket Server!"; char *msg_copy = strdup(msg); wr->data = msg_copy; uv_buf_t buf = uv_buf_init(msg_copy, strlen(msg_copy)); tlsuv_websocket_write(wr, ws, &buf, ws_write_cb); // Send a ping to check connection health tlsuv_websocket_ping(ws); } static void on_ws_close(uv_handle_t *h) { printf("WebSocket closed\n"); } int main(int argc, char *argv[]) { const char *ws_url = "wss://echo.websocket.org"; if (argc > 1) { ws_url = argv[1]; } uv_loop_t *loop = uv_default_loop(); // Initialize WebSocket tlsuv_websocket_t ws; tlsuv_websocket_init(loop, &ws); // Optional: Set custom TLS context for wss:// connections // tls_context *tls = default_tls_context(ca_cert, ca_cert_len); // tlsuv_websocket_set_tls(&ws, tls); // Optional: Set custom headers for the upgrade request tlsuv_websocket_set_header(&ws, "Origin", "https://example.com"); tlsuv_websocket_set_header(&ws, "X-Custom-Header", "custom-value"); // Connect to WebSocket server uv_connect_t req; tlsuv_websocket_connect(&req, &ws, ws_url, ws_connect_cb, ws_read_cb); // Run event loop uv_run(loop, UV_RUN_DEFAULT); return 0; } ``` -------------------------------- ### Install tlsuv project files Source: https://github.com/openziti/tlsuv/blob/main/CMakeLists.txt Defines installation rules for header files and the compiled tlsuv library archive. ```cmake install(DIRECTORY include/tlsuv DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/generated/tlsuv DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) install(TARGETS tlsuv ARCHIVE DESTINATION lib ) ``` -------------------------------- ### Establish TLS Connection and Send HTTP Request Source: https://context7.com/openziti/tlsuv/llms.txt This example demonstrates establishing a TLS connection to a server, sending an HTTP request, and handling incoming data. It requires libuv and TLSUV to be set up. ```c #include #include #include #define HOST "httpbingo.org" #define PORT 443 // Memory allocation callback for incoming data static void alloc_cb(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { buf->base = (char*)malloc(suggested_size); buf->len = suggested_size; } // Called when stream is closed static void on_close(uv_handle_t *h) { printf("TLS stream closed\n"); tlsuv_stream_free((tlsuv_stream_t *)h); } // Called when data is received void on_data(uv_stream_t *h, ssize_t nread, const uv_buf_t *buf) { if (nread > 0) { printf("%.*s", (int)nread, buf->base); fflush(stdout); } else if (nread == UV_EOF) { printf("\n--- Connection closed by server ---\n"); tlsuv_stream_close((tlsuv_stream_t *)h, on_close); } else if (nread < 0) { fprintf(stderr, "Read error: %s\n", uv_strerror((int)nread)); tlsuv_stream_close((tlsuv_stream_t *)h, on_close); } free(buf->base); } // Called when write completes void write_cb(uv_write_t *wr, int status) { if (status < 0) { fprintf(stderr, "Write failed: %s\n", uv_strerror(status)); } free(wr); } // Called when TLS connection is established void on_connect(uv_connect_t *cr, int status) { if (status < 0) { fprintf(stderr, "Connect failed: %s\n", uv_strerror(status)); tlsuv_stream_close((tlsuv_stream_t *)cr->handle, on_close); return; } printf("TLS connection established to %s\n", HOST); tlsuv_stream_t *tls = (tlsuv_stream_t *)cr->handle; // Start reading data tlsuv_stream_read_start(tls, alloc_cb, on_data); // Send HTTP request uv_write_t *wr = malloc(sizeof(uv_write_t)); char req[] = "GET /json HTTP/1.1\r\n" "Host: " HOST "\r\n" "Connection: close\r\n" "\r\n"; uv_buf_t buf = uv_buf_init(req, sizeof(req) - 1); tlsuv_stream_write(wr, tls, &buf, write_cb); } int main() { // Optional: enable debug logging (levels 1-6) // tlsuv_set_debug(5, my_logger_func); uv_loop_t *loop = uv_default_loop(); // Initialize TLS stream with default TLS context (pass NULL) tlsuv_stream_t tls_stream; tlsuv_stream_init(loop, &tls_stream, NULL); // Optional: set ALPN protocols const char *protocols[] = {"http/1.1"}; tlsuv_stream_set_protocols(&tls_stream, 1, protocols); // Optional: enable TCP keepalive tlsuv_stream_keepalive(&tls_stream, 1, 60); // Connect to server uv_connect_t *req = calloc(1, sizeof(uv_connect_t)); tlsuv_stream_connect(req, &tls_stream, HOST, PORT, on_connect); // Run event loop uv_run(loop, UV_RUN_DEFAULT); free(req); return 0; } ``` -------------------------------- ### Implement a link method Source: https://github.com/openziti/tlsuv/blob/main/deps/uv_link_t/README.md Example of a custom shutdown implementation that propagates the call to the parent link. ```c static int shutdown_impl(uv_link_t* link, uv_link_t* source, uv_link_shutdown_cb cb, void* arg) { fprintf(stderr, "this will be printed\n"); return uv_link_propagate_shutdown(link->parent, source, cb, arg); } ``` -------------------------------- ### Perform HTTP Requests with tlsuv Source: https://context7.com/openziti/tlsuv/llms.txt Demonstrates initializing an HTTP client, configuring connection settings, and executing GET, POST, and form-based requests within a libuv loop. ```c #include #include #include #include // Called when response headers are received void resp_cb(tlsuv_http_resp_t *resp, void *ctx) { if (resp->code < 0) { fprintf(stderr, "Request error: %s\n", uv_strerror(resp->code)); return; } printf("HTTP %s %d %s\n", resp->http_version, resp->code, resp->status); // Access response headers const char *content_type = tlsuv_http_resp_header(resp, "Content-Type"); if (content_type) { printf("Content-Type: %s\n", content_type); } // Iterate all headers tlsuv_http_hdr *h; LIST_FOREACH(h, &resp->headers, _next) { printf(" %s: %s\n", h->name, h->value); } } // Called with response body chunks (may be called multiple times) void body_cb(tlsuv_http_req_t *req, char *body, ssize_t len) { if (len == UV_EOF) { printf("\n--- Request complete ---\n"); } else if (len < 0) { fprintf(stderr, "Body error: %s\n", uv_strerror((int)len)); } else { printf("%.*s", (int)len, body); } } void on_http_close(tlsuv_http_t *clt) { printf("HTTP client closed\n"); } int main() { uv_loop_t *loop = uv_default_loop(); // Initialize HTTP client with base URL tlsuv_http_t http; tlsuv_http_init(loop, &http, "https://httpbin.org"); // Configure connection behavior tlsuv_http_idle_keepalive(&http, 30000); // Keep connection open 30s after last request tlsuv_http_connect_timeout(&http, 5000); // 5 second connection timeout // Set default headers for all requests tlsuv_http_header(&http, "User-Agent", "tlsuv/1.0"); tlsuv_http_header(&http, "Accept", "application/json"); // Make a GET request tlsuv_http_req_t *req = tlsuv_http_req(&http, "GET", "/json", resp_cb, NULL); req->resp.body_cb = body_cb; // Make a POST request with JSON body tlsuv_http_req_t *post_req = tlsuv_http_req(&http, "POST", "/post", resp_cb, NULL); post_req->resp.body_cb = body_cb; tlsuv_http_req_header(post_req, "Content-Type", "application/json"); const char *json_body = "{\"name\":\"test\",\"value\":123}"; tlsuv_http_req_data(post_req, json_body, strlen(json_body), NULL); // Make a form POST request tlsuv_http_req_t *form_req = tlsuv_http_req(&http, "POST", "/post", resp_cb, NULL); form_req->resp.body_cb = body_cb; tlsuv_http_pair form_data[] = { {"username", "testuser"}, {"password", "secret123"}, {"remember", "true"} }; tlsuv_http_req_form(form_req, 3, form_data); // Add query parameters to request tlsuv_http_req_t *query_req = tlsuv_http_req(&http, "GET", "/get", resp_cb, NULL); query_req->resp.body_cb = body_cb; tlsuv_http_pair query_params[] = { {"page", "1"}, {"limit", "10"}, {"filter", "active"} }; tlsuv_http_req_query(query_req, 3, query_params); // Run event loop uv_run(loop, UV_RUN_DEFAULT); // Cleanup tlsuv_http_close(&http, on_http_close); uv_run(loop, UV_RUN_DEFAULT); return 0; } ``` -------------------------------- ### Configure optional build components Source: https://github.com/openziti/tlsuv/blob/main/CMakeLists.txt Conditionally includes subdirectories for examples and tests based on project-level flags. ```cmake if(PROJECT_IS_TOP_LEVEL) option(BUILD_EXAMPLES "Build examples tree." "${tlsuv_DEVELOPER_MODE}") if(BUILD_EXAMPLES) add_subdirectory(sample) endif() endif() if(tlsuv_DEVELOPER_MODE) ENABLE_TESTING() set(MEMORYCHECK_SUPPRESSIONS_FILE "${CMAKE_CURRENT_LIST_DIR}/.valgrind.suppressions") add_subdirectory(tests) endif() ``` -------------------------------- ### Create Test Server Target Source: https://github.com/openziti/tlsuv/blob/main/tests/CMakeLists.txt Defines a custom target 'test-server' that runs a Go program to start a test server. This is used for integration testing. ```cmake add_custom_target(test-server WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test_server COMMAND go run ./test-server.go -ca ../certs/ca.pem -ca-key ../certs/ca.key ) ``` -------------------------------- ### Configure SoftHSM Configuration File Source: https://github.com/openziti/tlsuv/blob/main/tests/CMakeLists.txt Configures the softhsm2.conf file from a template. This is used for SoftHSM setup. ```cmake CONFIGURE_FILE(softhsm2.conf.in ${CMAKE_CURRENT_BINARY_DIR}/softhsm2.conf) ``` -------------------------------- ### Configure MbedTLS Library in CMake Source: https://github.com/openziti/tlsuv/blob/main/src/mbedtls/CMakeLists.txt Finds the MbedTLS package and sets up include directories and library linking for a CMake project. Ensure MbedTLS is installed and discoverable by CMake. ```cmake find_package(MbedTLS REQUIRED) add_library(mbedtls-impl OBJECT engine.c keys.c keys.h ) target_include_directories(mbedtls-impl PRIVATE ${PROJECT_SOURCE_DIR}/include PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/.. PRIVATE $) target_link_libraries(mbedtls-impl PRIVATE ${MBEDTLS_LIBRARIES}) ``` -------------------------------- ### uv_link_t Data Read Operations Source: https://github.com/openziti/tlsuv/blob/main/deps/uv_link_t/docs/api.md Functions for starting and stopping data reads on uv_link_t instances. ```APIDOC ## uv_link_read_start ### Description Invoke `read_start` from the link's [`uv_link_methods_t`][]. [`uv_link_t.alloc_cb`][]/[`uv_link_t.read_cb`][] may be called after successful `uv_link_read_start`. ### Parameters * `uv_link_t* link` ## uv_link_read_stop ### Description Invoke `read_stop` from the link's [`uv_link_methods_t`][]. [`uv_link_t.alloc_cb`][]/[`uv_link_t.read_cb`][] won't be called after `uv_link_read_stop`. ### Parameters * `uv_link_t* link` ``` -------------------------------- ### Manage TLS Context and Certificates in C Source: https://context7.com/openziti/tlsuv/llms.txt Demonstrates initializing a TLS context, loading keys and certificates, generating CSRs, and using custom verification callbacks. ```c #include #include #include #include int main() { // Create TLS context with custom CA certificate const char *ca_cert = "-----BEGIN CERTIFICATE-----\n" "MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSj...\n" "-----END CERTIFICATE-----\n"; tls_context *tls = default_tls_context(ca_cert, strlen(ca_cert)); // Alternative: Create with system CA bundle // tls_context *tls = default_tls_context(NULL, 0); printf("TLS version: %s\n", tls->version()); // Load private key from PEM tlsuv_private_key_t priv_key = NULL; const char *key_pem = "-----BEGIN EC PRIVATE KEY-----\n" "MHQCAQEEILvM6ZhNs...\n" "-----END EC PRIVATE KEY-----\n"; int rc = tls->load_key(&priv_key, key_pem, strlen(key_pem)); if (rc != 0) { fprintf(stderr, "Failed to load private key: %s\n", tls->strerror(rc)); } // Load certificate from PEM tlsuv_certificate_t cert = NULL; const char *cert_pem = "-----BEGIN CERTIFICATE-----\n" "MIICpDCCAYwCCQDU+pQ4F...\n" "-----END CERTIFICATE-----\n"; rc = tls->load_cert(&cert, cert_pem, strlen(cert_pem)); if (rc != 0) { fprintf(stderr, "Failed to load certificate: %s\n", tls->strerror(rc)); } // Set client certificate for mutual TLS if (priv_key && cert) { rc = tls->set_own_cert(tls, priv_key, cert); if (rc != 0) { fprintf(stderr, "Failed to set client cert: %s\n", tls->strerror(rc)); } } // Generate new EC private key tlsuv_private_key_t new_key = NULL; rc = tls->generate_key(&new_key); if (rc == 0) { printf("Generated new EC private key\n"); // Export public key tlsuv_public_key_t pub_key = new_key->pubkey(new_key); char *pub_pem = NULL; size_t pub_len = 0; pub_key->to_pem(pub_key, &pub_pem, &pub_len); printf("Public key:\n%s\n", pub_pem); free(pub_pem); pub_key->free(pub_key); // Generate CSR char *csr_pem = NULL; size_t csr_len = 0; rc = tls->generate_csr_to_pem(new_key, &csr_pem, &csr_len, "CN", "example.com", "O", "Example Corp", "C", "US", NULL); if (rc == 0) { printf("Certificate Signing Request:\n%s\n", csr_pem); free(csr_pem); } new_key->free(new_key); } // Load key from PKCS#11 token (OpenSSL only) tlsuv_private_key_t p11_key = NULL; rc = tls->load_pkcs11_key(&p11_key, "/usr/lib/softhsm/libsofthsm2.so", // PKCS#11 driver "0", // slot "1234", // PIN NULL, // key ID (or use label) "my-key-label"); // key label if (rc == 0) { printf("Loaded key from PKCS#11 token\n"); p11_key->free(p11_key); } // Custom certificate verification int verify_callback(const tlsuv_certificate_t cert, void *ctx) { // Get certificate text info const char *cert_info = cert->get_text(cert); printf("Verifying certificate: %s\n", cert_info); // Check expiration struct tm expiry; cert->get_expiration(cert, &expiry); // Return 0 to accept, non-zero to reject return 0; } tls->set_cert_verify(tls, verify_callback, NULL); // Cleanup if (priv_key) priv_key->free(priv_key); if (cert) cert->free(cert); tls->free_ctx(tls); return 0; } ``` -------------------------------- ### Build Sample Executables Source: https://github.com/openziti/tlsuv/blob/main/sample/CMakeLists.txt Iterates through a list of sample names and creates an executable for each. Each executable is linked against the 'common' library, ensuring shared functionality. ```cmake foreach (s ${samples}) add_executable(${s} ${s}.c) target_link_libraries(${s} PUBLIC common) endforeach() ``` -------------------------------- ### Configure Proxy Connector for HTTP Requests Source: https://context7.com/openziti/tlsuv/llms.txt Demonstrates initializing an HTTP proxy connector with authentication and applying it globally or to a specific client. ```c #include #include #include #include void resp_cb(tlsuv_http_resp_t *resp, void *ctx) { printf("Response: %d %s\n", resp->code, resp->status); } void body_cb(tlsuv_http_req_t *req, char *body, ssize_t len) { if (len > 0) { printf("%.*s", (int)len, body); } } int main() { uv_loop_t *loop = uv_default_loop(); // Create HTTP proxy connector tlsuv_connector_t *proxy = tlsuv_new_proxy_connector( tlsuv_PROXY_HTTP, "proxy.example.com", "8080" ); // Set proxy authentication (if required) proxy->set_auth(proxy, tlsuv_PROXY_BASIC, "username", "password"); // Option 1: Set as global connector (affects all connections) tlsuv_set_global_connector(proxy); // Initialize HTTP client tlsuv_http_t http; tlsuv_http_init(loop, &http, "https://httpbin.org"); // Option 2: Set connector on specific client only // tlsuv_http_set_connector(&http, proxy); // Make request (will go through proxy) tlsuv_http_req_t *req = tlsuv_http_req(&http, "GET", "/ip", resp_cb, NULL); req->resp.body_cb = body_cb; // Run event loop uv_run(loop, UV_RUN_DEFAULT); // Cleanup tlsuv_http_close(&http, NULL); uv_run(loop, UV_RUN_DEFAULT); // Free proxy connector proxy->free(proxy); return 0; } ``` -------------------------------- ### uv_link_methods_t.read_start callback Source: https://github.com/openziti/tlsuv/blob/main/deps/uv_link_t/docs/api.md This callback is invoked by uv_link_read_start(). Data may be emitted after this call. Semantics are the same as uv_read_start. ```c int (*read_start)(uv_link_t* link); ``` -------------------------------- ### Initialize a uv_link_source_t Source: https://github.com/openziti/tlsuv/blob/main/deps/uv_link_t/README.md Initializes a source link from an existing libuv stream to act as the base of a chain. ```c uv_stream_t* stream = ...; uv_link_source_t source; uv_link_source_init(uv_default_loop(), &source, stream); ``` -------------------------------- ### Define and chain a uv_link_t Source: https://github.com/openziti/tlsuv/blob/main/deps/uv_link_t/README.md Defines custom link methods, initializes the link, and chains it to a source link. ```c uv_link_t link; static uv_link_methods_t methods = { .read_start = read_start_impl, .read_stop = read_stop_impl, .write = write_impl, .try_write = try_write_impl, .shutdown = shutdown_impl, .close = close_impl, /* These will be used only when chaining two links together */ .alloc_cb_override = alloc_cb_impl, .read_cb_override = read_cb_impl }; uv_link_init(&link, &methods); /* Just like in libuv */ link.alloc_cb = my_alloc_cb; link.read_cb = my_read_cb; /* Creating a chain here */ uv_link_chain(&source, &link); uv_link_read_start(&link); ``` -------------------------------- ### Integrate TLSUV with CMake using FetchContent Source: https://context7.com/openziti/tlsuv/llms.txt Use FetchContent to download and build TLSUV within your CMake project. Options are available to disable HTTP support or select a different TLS backend. ```cmake cmake_minimum_required(VERSION 3.14) project(my_app) include(FetchContent) # Fetch TLSUV FetchContent_Declare(tlsuv GIT_REPOSITORY https://github.com/openziti/tlsuv.git GIT_TAG v0.40.0 ) # Optional: Disable HTTP support to reduce dependencies # set(TLSUV_HTTP OFF CACHE BOOL "" FORCE) # Optional: Use mbedTLS instead of OpenSSL # set(TLSUV_TLSLIB mbedtls CACHE STRING "" FORCE) FetchContent_MakeAvailable(tlsuv) # Your application add_executable(my_app main.c) target_link_libraries(my_app PRIVATE tlsuv) # Example using vcpkg for dependencies ``` -------------------------------- ### Build Common Library Source: https://github.com/openziti/tlsuv/blob/main/sample/CMakeLists.txt Compiles 'common.c' into a static library object and links it against the 'tlsuv' library. It also conditionally links 'unofficial::getopt-win32::getopt' for MSVC builds. ```cmake add_library(common OBJECT common.c) target_link_libraries(common PUBLIC tlsuv) if (MSVC) target_link_libraries(common PRIVATE unofficial::getopt-win32::getopt) endif() ``` -------------------------------- ### Conditional Sample List Expansion Source: https://github.com/openziti/tlsuv/blob/main/sample/CMakeLists.txt Initializes a list of sample executables and conditionally appends HTTP-related samples if the 'TLSUV_HTTP' build option is enabled. This controls which sample programs are built. ```cmake set(samples sample engine_test sample-cf ) if (TLSUV_HTTP) list(APPEND samples um-curl repeat-fetch ws-client http-ping ) endif() ``` -------------------------------- ### Define Test Source Files Source: https://github.com/openziti/tlsuv/blob/main/tests/CMakeLists.txt Lists the source files for the test executable. Includes common files and conditionally adds HTTP/WebSocket related files if TLSUV_HTTP is enabled. ```cmake set(test_srcs engine_tests.cpp stream_tests.cpp key_tests.cpp connector_tests.cpp ) if (TLSUV_HTTP) list(APPEND test_srcs http_tests.cpp ws_tests.cpp compression_tests.cpp ) endif () ``` -------------------------------- ### Create All Tests Executable Source: https://github.com/openziti/tlsuv/blob/main/tests/CMakeLists.txt Defines the main test executable 'all_tests'. Sets C++ standard to 20, includes directories, and applies compile options and definitions. ```cmake add_executable(all_tests all_tests.cpp ${test_srcs}) set_property(TARGET all_tests PROPERTY CXX_STANDARD 20) target_include_directories(all_tests PRIVATE ../src) target_compile_options(all_tests PRIVATE ${PKCS11_OPTS}) target_compile_definitions(all_tests PRIVATE TEST_DATA_DIR=${CMAKE_CURRENT_BINARY_DIR}/testdata) ``` -------------------------------- ### uv_link_methods_t.try_write callback Source: https://github.com/openziti/tlsuv/blob/main/deps/uv_link_t/docs/api.md This callback is invoked by uv_link_try_write(). Semantics are the same as uv_try_write. ```c int (*try_write)(uv_link_t* link, const uv_buf_t bufs[], unsigned int nbufs); ``` -------------------------------- ### Integrate TLSUV with CMake FetchContent Source: https://github.com/openziti/tlsuv/blob/main/README.md Use FetchContent to include TLSUV in a CMake project and link it to an application target. ```cmake FetchContent_Declare(tlsuv GIT_REPOSITORY https://github.com/openziti/tlsuv.git GIT_TAG v0.40.0 # use latest release version ) FetchContent_MakeAvailable(tlsuv) target_link_libraries(your_app PRIVATE tlsuv) ``` -------------------------------- ### uv_link_t Initialization and Closing Source: https://github.com/openziti/tlsuv/blob/main/deps/uv_link_t/docs/api.md Functions for initializing and closing uv_link_t structures. ```APIDOC ## uv_link_init ### Description Initialize `link` structure with provided `methods`. This is a first method to be called on `uv_link_t` instance before it could be used in any other APIs. ### Parameters * `uv_link_t* link` - user allocated `uv_link_t` instance * `uv_link_methods_t const* methods` - user defined link methods, see [`uv_link_methods_t`][] below ## uv_link_close ### Description Close `uv_link_t` instance and all other `uv_link_t`s chained into it (see [`uv_link_chain()`][]), invoke `void (*cb)(uv_link_t*)` once done (may happen synchronously or on a next libuv tick, depending on particular `uv_link_t` implementation). Invokes `close` from link's [`uv_link_methods_t`][]. *NOTE: this method must be called on a leaf link that has no chaining child* ### Parameters * `uv_link_t* link` - `uv_link_t` instance to close * `uv_link_close_cb cb` - callback to be invoked upon close ``` -------------------------------- ### Configure CMake User Presets for Development Source: https://github.com/openziti/tlsuv/blob/main/HACKING.md Define custom CMake presets for development, including build directory, inherited presets, and cache variables. Ensure `` is replaced with your operating system (e.g., 'win64' or 'unix'). ```json { "version": 2, "cmakeMinimumRequired": { "major": 3, "minor": 14, "patch": 0 }, "configurePresets": [ { "name": "dev", "binaryDir": "${sourceDir}/build/dev", "inherits": ["dev-mode", "vcpkg", "ci-"], "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug" } } ], "buildPresets": [ { "name": "dev", "configurePreset": "dev", "configuration": "Debug" } ], "testPresets": [ { "name": "dev", "configurePreset": "dev", "configuration": "Debug", "output": { "outputOnFailure": true } } ] } ``` -------------------------------- ### uv_link_methods_t.write callback Source: https://github.com/openziti/tlsuv/blob/main/deps/uv_link_t/docs/api.md This callback is invoked by uv_link_write() or uv_link_propagate_write(). The callback `cb` must be called upon completion. Semantics are the same as uv_write2. ```c int (*write)(uv_link_t* link, uv_link_t* source, const uv_buf_t bufs[], unsigned int nbufs, uv_stream_t* send_handle, uv_link_write_cb cb, void* arg); ``` -------------------------------- ### Find and Configure Catch2 Source: https://github.com/openziti/tlsuv/blob/main/tests/CMakeLists.txt Finds the Catch2 testing framework configuration. This is required for running unit tests. ```cmake find_package(Catch2 CONFIG REQUIRED) message("catch2 is ${Catch2_CONFIG}") ``` -------------------------------- ### Synchronous TLS Communication with TLS Engine API Source: https://context7.com/openziti/tlsuv/llms.txt This C code demonstrates how to perform synchronous TLS communication using the TLS Engine API. It includes setting up a socket, establishing a TLS connection, sending an HTTP request, and reading the response. Ensure necessary headers and platform-specific socket initializations are included. ```c #include #include #include #include #include #ifdef _WIN32 #include #include typedef SOCKET socket_t; #else #include #include #include #include #include typedef int socket_t; #endif #define HOST "httpbingo.org" #define PORT 443 int main() { #ifdef _WIN32 WSADATA wsa; WSAStartup(MAKEWORD(2, 0), &wsa); #endif // Resolve hostname struct hostent *he = gethostbyname(HOST); if (!he) { fprintf(stderr, "Failed to resolve hostname\n"); return 1; } // Create socket socket_t sock = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_addr = *((struct in_addr *)he->h_addr); addr.sin_port = htons(PORT); // Connect socket if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) { fprintf(stderr, "Failed to connect\n"); return 1; } printf("TCP connected to %s:%d\n", HOST, PORT); // Create TLS context (NULL for system CA bundle) tls_context *tls = default_tls_context(NULL, 0); printf("TLS implementation: %s\n", tls->version()); // Create TLS engine for this connection tlsuv_engine_t engine = tls->new_engine(tls, HOST); // Set ALPN protocols const char *alpn[] = {"http/1.1"}; engine->set_protocols(engine, alpn, 1); // Associate socket with engine engine->set_io_fd(engine, (tlsuv_sock_t)sock); // Perform TLS handshake tls_handshake_state hs_state; do { hs_state = engine->handshake(engine); if (hs_state == TLS_HS_ERROR) { fprintf(stderr, "TLS handshake failed: %s\n", engine->strerror(engine)); engine->free(engine); tls->free_ctx(tls); return 1; } } while (hs_state == TLS_HS_CONTINUE); printf("TLS handshake complete, ALPN: %s\n", engine->get_alpn(engine)); // Send HTTP request const char *request = "GET /json HTTP/1.1\r\n" "Host: " HOST "\r\n" "Connection: close\r\n" "\r\n"; int written = engine->write(engine, request, strlen(request)); printf("Sent %d bytes\n", written); // Read response char buffer[4096]; size_t bytes_read; int read_result; do { read_result = engine->read(engine, buffer, &bytes_read, sizeof(buffer) - 1); if (bytes_read > 0) { buffer[bytes_read] = '\0'; printf("%s", buffer); } } while (read_result == TLS_OK || read_result == TLS_MORE_AVAILABLE); printf("\n--- Connection complete ---\n"); // Close TLS connection engine->close(engine); // Cleanup engine->free(engine); tls->free_ctx(tls); #ifdef _WIN32 closesocket(sock); WSACleanup(); #else close(sock); #endif return 0; } ``` -------------------------------- ### Default uv_link_methods_t Callbacks Source: https://github.com/openziti/tlsuv/blob/main/deps/uv_link_t/docs/api.md These functions provide default implementations for uv_link_methods_t callbacks. They can be used directly or as a base for custom implementations. ```c int uv_link_default_read_start(uv_link_t* link); int uv_link_default_read_stop(uv_link_t* link); int uv_link_default_write(uv_link_t* link, uv_link_t* source, const uv_buf_t bufs[], unsigned int nbufs, uv_stream_t* send_handle, uv_link_write_cb cb, void* arg); int uv_link_default_try_write(uv_link_t* link, const uv_buf_t bufs[], unsigned int nbufs); int uv_link_default_shutdown(uv_link_t* link, uv_link_t* source, uv_link_shutdown_cb cb, void* arg); void uv_link_default_close(uv_link_t* link, uv_link_t* source, uv_link_close_cb cb); const char* uv_link_default_strerror(uv_link_t* link, int err); ``` -------------------------------- ### CMake Build and Test Commands Source: https://github.com/openziti/tlsuv/blob/main/HACKING.md Commands to configure, build, and test the project using CMake presets. The `-j` flag can be added to specify the number of parallel jobs. ```sh cmake --preset=dev cmake --build --preset=dev ctest --preset=dev ``` -------------------------------- ### Link Libraries for Test Executable Source: https://github.com/openziti/tlsuv/blob/main/tests/CMakeLists.txt Links the necessary libraries to the 'all_tests' executable, including tlsuv, Catch2, and parson. ```cmake target_link_libraries(all_tests tlsuv Catch2::Catch2 parson::parson ) ``` -------------------------------- ### uv_link_methods_t Callback Definitions Source: https://github.com/openziti/tlsuv/blob/main/deps/uv_link_t/docs/api.md Definitions for the core methods within the uv_link_methods_t structure, including read, write, shutdown, and close operations. ```APIDOC ## uv_link_methods_t Methods ### .read_start `int (*read_start)(uv_link_t* link);` Invoked by `uv_link_read_start()`. Data may be emitted after this call. ### .read_stop `int (*read_stop)(uv_link_t* link);` Invoked by `uv_link_read_stop()`. Data MUST not be emitted after this call. ### .write `int (*write)(uv_link_t* link, uv_link_t* source, const uv_buf_t bufs[], unsigned int nbufs, uv_stream_t* send_handle, uv_link_write_cb cb, void* arg);` Invoked by `uv_link_write()` or `uv_link_propagate_write()`. Upon completion, `cb(source, ...)` must be called. ### .try_write `int (*try_write)(uv_link_t* link, const uv_buf_t bufs[], unsigned int nbufs);` Invoked by `uv_link_try_write()`. ### .shutdown `int (*shutdown)(uv_link_t* link, uv_link_t* source, uv_link_shutdown_cb cb, void* arg);` Invoked by `uv_link_shutdown()` or `uv_link_propagate_shutdown`. Upon completion, `cb(source, ...)` must be called. ### .close `void (*close)(uv_link_t* link, uv_link_t* source, uv_link_close_cb cb);` Invoked by `uv_link_close()` or `uv_link_propagate_close`. Upon completion, `cb(source, ...)` must be called. ### .strerror `const char* (*strerror)(uv_link_t* link, int err);` Invoked by `uv_link_strerror()`. Returns a description string for the error emitted by the link. ``` -------------------------------- ### Define Individual Test Cases Source: https://github.com/openziti/tlsuv/blob/main/tests/CMakeLists.txt Uses the 'mk_test' macro to define individual test cases for 'key', 'engine', and 'stream'. Conditionally defines 'http' and 'websocket' tests if TLSUV_HTTP is enabled. ```cmake mk_test(key) mk_test(engine) mk_test(stream) if (TLSUV_HTTP) mk_test(http) mk_test(websocket) endif (TLSUV_HTTP) ``` -------------------------------- ### Configure win32crypto-impl library in CMake Source: https://github.com/openziti/tlsuv/blob/main/src/win32crypto/CMakeLists.txt Defines the object library, compile definitions, include paths, and system library dependencies for the Windows crypto implementation. ```cmake add_library(win32crypto-impl OBJECT wc_context.c cert.c keys.c engine.c ) target_compile_definitions(win32crypto-impl PUBLIC SECURITY_WIN32 WIN32_LEAN_AND_MEAN WINVER=0x0A00 _WIN32_WINNT=0x0A00 ) target_include_directories(win32crypto-impl PRIVATE ${PROJECT_SOURCE_DIR}/include ) target_link_libraries(win32crypto-impl PRIVATE ncrypt PRIVATE crypt32 PRIVATE security ) ``` -------------------------------- ### Implement Custom Memory Allocator in C Source: https://context7.com/openziti/tlsuv/llms.txt Define custom malloc, realloc, calloc, and free functions to integrate with your memory management system. Ensure these functions are set before any other tlsuv calls. ```c #include #include #include static size_t total_allocated = 0; void* my_malloc(size_t size) { void *ptr = malloc(size + sizeof(size_t)); if (ptr) { *(size_t*)ptr = size; total_allocated += size; return (char*)ptr + sizeof(size_t); } return NULL; } void* my_realloc(void * ptr, size_t size) { if (!ptr) return my_malloc(size); size_t *real_ptr = (size_t*)((char*)ptr - sizeof(size_t)); size_t old_size = *real_ptr; total_allocated -= old_size; void *new_ptr = realloc(real_ptr, size + sizeof(size_t)); if (new_ptr) { *(size_t*)new_ptr = size; total_allocated += size; return (char*)new_ptr + sizeof(size_t); } return NULL; } void* my_calloc(size_t count, size_t size) { size_t total = count * size; void *ptr = my_malloc(total); if (ptr) { memset(ptr, 0, total); } return ptr; } void my_free(void *ptr) { if (ptr) { size_t *real_ptr = (size_t*)((char*)ptr - sizeof(size_t)); total_allocated -= *real_ptr; free(real_ptr); } } int main() { // Set custom allocators before any other tlsuv calls tlsuv_set_allocator(my_malloc, my_realloc, my_calloc, my_free); // Now use tlsuv APIs normally uv_loop_t *loop = uv_default_loop(); tlsuv_stream_t stream; tlsuv_stream_init(loop, &stream, NULL); printf("Memory allocated: %zu bytes\n", total_allocated); tlsuv_stream_free(&stream); printf("Memory after cleanup: %zu bytes\n", total_allocated); return 0; } ``` -------------------------------- ### Cast uv_link_observer_t to uv_link_t Source: https://github.com/openziti/tlsuv/blob/main/deps/uv_link_t/docs/api.md Demonstrates casting an observer link structure to the base uv_link_t type. ```c uv_link_observer_t* observer = ...; uv_link_t* link = (uv_link_t*) observer; ``` -------------------------------- ### Create Run Tests Target Source: https://github.com/openziti/tlsuv/blob/main/tests/CMakeLists.txt Defines a custom target 'run-tests' that depends on 'all_tests'. It executes the tests and outputs results in JUnit XML format. ```cmake add_custom_target(run-tests BYPRODUCTS ${PROJECT_BINARY_DIR}/Testing/results.xml DEPENDS all_tests COMMAND $ -r xml -o ${PROJECT_BINARY_DIR}/Testing/results.xml) ``` -------------------------------- ### Set Include Directories for openssl-impl Source: https://github.com/openziti/tlsuv/blob/main/src/openssl/CMakeLists.txt Configures the include directories for the 'openssl-impl' library. 'PRIVATE' indicates that these directories are only used for compiling the library itself. ```cmake target_include_directories(openssl-impl PRIVATE ${PROJECT_SOURCE_DIR}/include) ``` -------------------------------- ### uv_link_methods_t.shutdown callback Source: https://github.com/openziti/tlsuv/blob/main/deps/uv_link_t/docs/api.md This callback is invoked by uv_link_shutdown() or uv_link_propagate_shutdown. The callback `cb` must be called upon completion. Semantics are the same as uv_shutdown. ```c int (*shutdown)(uv_link_t* link, uv_link_t* source, uv_link_shutdown_cb cb, void* arg); ``` -------------------------------- ### Find PKCS11 Tools Source: https://github.com/openziti/tlsuv/blob/main/tests/CMakeLists.txt Finds the softhsm2-util and pkcs11-tool executables. These are necessary for managing PKCS11 tokens and keys. ```cmake find_program(softhsm_tool softhsm2-util) find_program(pk11 pkcs11-tool) ``` -------------------------------- ### uv_link_methods_t.strerror callback Source: https://github.com/openziti/tlsuv/blob/main/deps/uv_link_t/docs/api.md This callback is invoked by uv_link_strerror(). It should return a description string for the error code emitted by the link. Semantics are the same as uv_strerror. ```c const char* (*strerror)(uv_link_t* link, int err); ``` -------------------------------- ### Cast uv_link_source_t to uv_link_t Source: https://github.com/openziti/tlsuv/blob/main/deps/uv_link_t/docs/api.md Demonstrates casting a source link structure to the base uv_link_t type. ```c uv_link_source_t* source = ...; uv_link_t* link = (uv_link_t*) source; ``` -------------------------------- ### Print Build Variable Source: https://github.com/openziti/tlsuv/blob/main/sample/CMakeLists.txt Prints the value of the 'TLSUV_HTTP' build variable to the console during the CMake configuration process. Useful for debugging build options. ```cmake message(NOTICE "TLSUV_HTTP = ${TLSUV_HTTP}") ``` -------------------------------- ### Include Directories for Test Executable Source: https://github.com/openziti/tlsuv/blob/main/tests/CMakeLists.txt Adds include directories for the test executable. This ensures that header files are found during compilation. ```cmake target_include_directories(all_tests PRIVATE ../src) ``` -------------------------------- ### Enable C++ Language Support Source: https://github.com/openziti/tlsuv/blob/main/tests/CMakeLists.txt Enables C++ language support for the project. This is a foundational step for C++ development. ```cmake ENABLE_LANGUAGE(CXX) ``` -------------------------------- ### uv_link_methods_t.close callback Source: https://github.com/openziti/tlsuv/blob/main/deps/uv_link_t/docs/api.md This callback is invoked by uv_link_close() or uv_link_propagate_close. The callback `cb` must be called upon completion. Semantics are the same as uv_close. ```c void (*close)(uv_link_t* link, uv_link_t* source, uv_link_close_cb cb); ``` -------------------------------- ### Propagate Read Callback in OpenZiti Link Source: https://github.com/openziti/tlsuv/blob/main/deps/uv_link_t/docs/api.md Used internally by `uv_link_methods_t` implementation. Invokes `link->read_cb` with provided arguments. Semantics are the same as `uv_read_cb`. ```c void uv_link_propagate_read_cb(uv_link_t* link, ssize_t nread, const uv_buf_t* buf); ``` -------------------------------- ### uv_link_observer_t Source: https://github.com/openziti/tlsuv/blob/main/deps/uv_link_t/docs/api.md A helper structure to observe reads on a link without managing them. It provides a consistent way to observe reads even when links are chained. Use uv_link_observer_t.observer_read_cb to set up the read callback. ```APIDOC ## uv_link_observer_t A helper structure to observe (*not manage*) the reads of the link. When links are chained - [`uv_link_t.read_cb`][]/[`uv_link_t.alloc_cb`][] are overwritten by [`uv_link_chain()`][]. `uv_link_observer_t` provides a consistent way to observe the reads on a link, even if the link will be chained with another link later on. Use [`uv_link_observer_t.observer_read_cb`][] property to set up read callback. Can be cast to `uv_link_t`: ```c uv_link_observer_t* observer = ...; uv_link_t* link = (uv_link_t*) observer; ``` *NOTE: All `uv_link_...` methods can be used with `uv_link_source_t`* ### int uv_link_observer_init(...) * `uv_link_observer_t* observer` - structure to initialize Initialize the structure. ### .observer_read_cb ```c void (*observer_read_cb)(uv_link_observer_t* observer, ssize_t nread, const uv_buf_t* buf); ``` Invoked by `uv_link_propagate_read_cb`. MUST not manage the data in `buf`. ``` -------------------------------- ### Close a link within a read callback Source: https://github.com/openziti/tlsuv/blob/main/deps/uv_link_t/docs/implementation-guide.md Demonstrates the deferred execution of the close method when triggered from within a read callback. ```c static void read_cb(uv_link_t* link, ssize_t nread, uv_buf_t* buf) { uv_link_close(link, ...); } link->read_cb = read_cb; uv_link_propagate_read_cb(link, UV_EOF, NULL); /* link->methods->close() call will happen upon returning from `uv_link_propagate_read_cb` */ ```