pw_tls_client: Add public APIs and documentation

Change-Id: I215c5a541ae4cabb1f1f26ee2620e32fcdf7ed55
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/44567
Reviewed-by: Ali Zhang <alizhang@google.com>
Reviewed-by: Wyatt Hepler <hepler@google.com>
Reviewed-by: Zoltan Szatmary-Ban <szatmz@google.com>
Reviewed-by: Ewout van Bekkum <ewout@google.com>
Reviewed-by: Tennessee Carmel-Veilleux  <tennessee@google.com>
Reviewed-by: Terence Hampson <thampson@google.com>
Reviewed-by: David Palchak <palchak@google.com>
Commit-Queue: Yecheng Zhao <zyecheng@google.com>
diff --git a/pw_tls_client/docs.rst b/pw_tls_client/docs.rst
new file mode 100644
index 0000000..f40702f
--- /dev/null
+++ b/pw_tls_client/docs.rst
@@ -0,0 +1,157 @@
+.. _module-pw_tls_client:
+
+--------------
+pw_tls_client
+--------------
+
+This module provides a facade that defines the public APIs for establishing TLS
+sessions over arbitrary transports. Two options of backends,
+pw_tls_client_mbedtls and pw_tls_client_boringssl, which are based on BoringSSL
+and MbedTLS libraries, are under construction.
+
+The facade provides a class ``pw::tls_client::Session`` with Open(), Read(),
+Write() and Close() methods for TLS communication. An instance is created by
+``pw::tls_client::Session::Create`` method. The method takes a
+``pw::tls_client::SessionOptions`` object, which is used to configure TLS
+connection options. The list of supported configurations currently include:
+
+1. Host name of the target server. This will be used as the Server Name
+Indication(SNI) extension during TLS handshake.
+
+2. User-implemented transport. The underlying transport for the TLS
+communication. It is an object that implements the interface of
+``pw::stream::ReaderWriter``.
+
+The module will also provide mechanisms/APIs for users to specify sources of
+trust anchors, time and entropy. These are under construction.
+
+.. warning::
+  This module is under construction, not ready for use, and the documentation
+  is incomplete.
+
+Setup
+=====
+This module requires the following setup:
+
+  1. Choose a ``pw_tls_client`` backend, or write one yourself.
+  2. If using GN build, Specify the ``pw_tls_client_BACKEND`` GN build arg to
+     point the library that provides a ``pw_tls_client`` backend.
+
+Module usage
+============
+For GN build, add ``//pw_tls_client`` to the dependency list.
+
+The following gives an example code for using the module on host platform.
+The example uses a Pigweed socket stream as the transport and performs TLS
+connection to www.google.com:
+
+.. code-block:: cpp
+
+  // Host domain name
+  constexpr char kHost[] = "www.google.com";
+
+  constexpr int kPort = 443;
+
+  // Server Name Indication.
+  constexpr const char* kServerNameIndication = kHost;
+
+  // An example message to send.
+  constexpr char kHTTPRequest[] = "GET / HTTP/1.1\r\n\r\n";
+
+  // pw::stream::SocketStream doesn't accept host domain name as input. Thus we
+  // introduce this helper function for getting the IP address
+  pw::Status GetIPAddrFromHostName(std::string_view host, std::span<char> ip) {
+    char null_terminated_host_name[256] = {0};
+    auto host_copy_status = pw::string::Copy(host, null_terminated_host_name);
+    if (!host_copy_status.ok()) {
+      return host_copy_status.status();
+    }
+
+    struct hostent* ent = gethostbyname(null_terminated_host_name);
+    if (ent == NULL) {
+      return PW_STATUS_INTERNAL;
+    }
+
+    in_addr** addr_list = reinterpret_cast<in_addr**>(ent->h_addr_list);
+    if (addr_list[0] == nullptr) {
+      return PW_STATUS_INTERNAL;
+    }
+
+    auto ip_copy_status = pw::string::Copy(inet_ntoa(*addr_list[0]), ip);
+    if (!ip_copy_status.ok()) {
+      return ip_copy_status.status();
+    }
+
+    return pw::OkStatus();
+  }
+
+  int main() {
+    // Get the IP address of the target host.
+    char ip_address[64] = {0};
+    auto get_ip_status = GetIPAddrFromHostName(kHost, ip_address);
+    if (!get_ip_status.ok()) {
+      return 1;
+    }
+
+    // Use a socket stream as the transport.
+    pw::stream::SocketStream socket_stream;
+
+    // Connect the socket to the remote host.
+    auto socket_connect_status = socket_stream.Connect(ip_address, kPort);
+    if (!socket_connect_status.ok()) {
+      return 1;
+    }
+
+    // Create a TLS session. Register the transport.
+    auto options = pw::tls_client::SessionOptions()
+            .set_server_name(kServerNameIndication)
+            .set_transport(socket_stream);
+    auto tls_conn = pw::tls_client::Session::Create(options);
+    if (!tls_conn.ok()) {
+      // Handle errors.
+      return 1;
+    }
+
+    auto open_status = tls_conn.value()->Open();
+    if (!open_status.ok()) {
+      // Inspect/handle error with open_status.code() and
+      // tls_conn.value()->GetLastTLSStatus().
+      return 1;
+    }
+
+    auto write_status = tls_conn.value()->Write(std::as_bytes(std::span{kHTTPRequest}));
+    if (!write_status.ok()) {
+      // Inspect/handle error with write_status.code() and
+      // tls_conn.value()->GetLastTLSStatus().
+      return 0;
+    }
+
+    // Listen for incoming data.
+    std::array<std::byte, 4096> buffer;
+    while (true) {
+      auto res = tls_conn.value()->Read(buffer);
+      if (!res.ok()) {
+        // Inspect/handle error with res.status().code() and
+        // tls_conn.value()->GetLastTLSStatus().
+        return 1;
+      }
+
+      // Process data in |buffer|. res.value() gives the span of read bytes.
+      // The following simply print to console.
+      if (res.value().size()) {
+        auto print_status = pw::sys_io::WriteBytes(res.value());
+        if (!print_status.ok()) {
+          return 1;
+        }
+      }
+
+    }
+  }
+
+A list of other demos will be provided in ``//pw_tls_client/examples/``
+
+Warning
+============
+
+Open()/Read() APIs are synchronous for now. Support for
+non-blocking/asynchronous usage will be added in the future.