<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><title>Tutorial on kmcd.dev</title><link>https://kmcd.dev/categories/tutorial/</link><description>Recent content in Tutorial on kmcd.dev</description><generator>Hugo -- gohugo.io</generator><language>en</language><copyright>All Rights Reserved</copyright><lastBuildDate>Tue, 28 Apr 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://kmcd.dev/categories/tutorial/index.xml" rel="self" type="application/rss+xml"/><item><title>Building APIs with Contracts</title><link>https://kmcd.dev/posts/api-contracts/</link><pubDate>Tue, 28 Apr 2026 00:00:00 +0000</pubDate><guid>https://kmcd.dev/posts/api-contracts/</guid><description> 
                &lt;p> &lt;img hspace="5" src="https://kmcd.dev/posts/api-contracts/cover.svg" /> &lt;/p>
                
                Building for Scale: Why contract-based APIs are the future.
                </description><content:encoded><![CDATA[<div class="disclaimer">
    This article was originally published in April 2024. It was republished in April 2026 after some significant editing and modernization.
</div>

<p>In today&rsquo;s interconnected world, APIs (Application Programming Interfaces) are the glue that connects computers. They allow different applications to talk to each other, share data, and perform actions. However, traditional methods of creating APIs often lead to frustrating challenges: breaking changes in JSON APIs, silent failures due to missing fields, frontend and backend drift, or schema mismatches that result in the classic &ldquo;works on my machine&rdquo; excuse.</p>
<p>Imagine a real-world scenario where the backend team renames a <code>userId</code> field to <code>user_id</code> and deploys their changes. Instantly, the frontend checkout process breaks in production because the API had no strict enforcement to catch the mismatch.</p>
<p>This is where <strong>contract-based APIs</strong> come in. A contract-based API is one where the schema is defined first in a formal specification, and both client and server are generated or validated against that contract. They reduce ambiguity and enforce consistency across services.</p>
<h2 id="the-power-of-pre-defined-api-contracts">The Power of Pre-defined API Contracts</h2>
<p>A contract-based API defines exactly what data can be exchanged, in what format, and what actions can be performed. This strict, pre-defined agreement unlocks several immediate advantages:</p>
<ul>
<li><strong>Improved Developer Experience:</strong> Developers on both sides (client and server) have a clear understanding of what is expected, making integration smoother.</li>
<li><strong>Automated Documentation:</strong> Contracts serve as self-documenting artifacts. This reduces the need for manual documentation maintenance and ensures the docs stay in sync with the actual API implementation.</li>
<li><strong>Reduced Errors:</strong> Mismatched data formats or API changes become less likely, leading to fewer bugs. Contracts act as a validation layer that catches potential issues early.</li>
<li><strong>Easier Integration:</strong> Contracts act as a single source of truth. Developers can quickly understand how to interact with the API without extensive back and forth communication.</li>
<li><strong>Streamlined Development:</strong> These APIs often enable tools to automatically generate code for both client and server implementations. This eliminates manual boilerplate so you can focus on core logic.</li>
</ul>
<h2 id="protobuf-the-language-of-apis">Protobuf: The Language of APIs</h2>
<p>In modern distributed systems, the foundation of many contract-based APIs lies in <a href="https://protobuf.dev/" rel="external"><strong>Protocol Buffers (protobuf)</strong></a>. It is a language-neutral data format specifically designed for structured messages.</p>
<p>Unlike JSON, which is a text-based format designed to be human-readable, Protobuf is a <strong>binary format</strong>. This means you trade the ability to natively read the raw data in transit for significant performance gains:</p>
<ul>
<li><strong>Smaller Message Sizes:</strong> Protobuf messages are compact and efficient, which leads to faster transmission and reduced bandwidth usage.</li>
<li><strong>Faster Parsing:</strong> Parsing binary protobuf messages is significantly faster compared to traditional formats like JSON or XML.</li>
<li><strong>Built-in Versioning:</strong> Protobuf uses field numbers (the <code>= 1</code>, <code>= 2</code> in the code below) to identify data. This allows for excellent backward and forward compatibility. You can add new fields without breaking older clients that do not know about them yet.</li>
<li><strong>Cross-language Compatibility:</strong> Protobuf definitions are language-agnostic. Code for interacting with the API can be generated for almost any modern programming language.</li>
</ul>
<p>Because the data is binary, you cannot simply open your browser&rsquo;s network tab and read the payloads by default. You will usually need to rely on modern browser extensions (like the gRPC-Web or Connect dev tools) to decode the traffic. It also requires setting up specialized tooling and build steps to compile the generated code.</p>
<p>Here is a basic example of a <code>.proto</code> file defining messages for a user and an address:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-protobuf" data-lang="protobuf"><span class="line"><span class="cl"><span class="n">syntax</span> <span class="o">=</span> <span class="s">&#34;proto3&#34;</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kd">message</span> <span class="nc">User</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">string</span> <span class="n">name</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">int32</span> <span class="n">id</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">string</span> <span class="n">email</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="n">Address</span> <span class="n">address</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kd">message</span> <span class="nc">Address</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">string</span> <span class="n">street</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">string</span> <span class="n">city</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">string</span> <span class="n">state</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">string</span> <span class="n">zip</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span></code></pre></div><p>In this example, the <code>User</code> message has fields for name, ID, email, and an <code>Address</code> message. These defined structures ensure consistent data exchange between applications.</p>
<blockquote>
<p><strong>Key idea:</strong> Protobuf relies on immutable field numbers instead of field names. This golden rule guarantees backward and forward compatibility.</p>
</blockquote>
<h2 id="grpc-building-apis-on-a-solid-foundation">gRPC: Building APIs on a Solid Foundation</h2>
<p><strong>gRPC (gRPC Remote Procedure Call)</strong> is a high-performance framework that builds upon protobuf&rsquo;s strengths. It provides a powerful way to implement remote procedure calls, allowing applications to interact using clients generated for each language.</p>
<h3 id="introducing-services-and-requestresponse-types-with-grpc">Introducing Services and Request/Response Types with gRPC</h3>
<p>We can expand the <code>.proto</code> file to define a service called <code>UserService</code> with methods for user management:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-protobuf" data-lang="protobuf"><span class="line"><span class="cl"><span class="n">syntax</span> <span class="o">=</span> <span class="s">&#34;proto3&#34;</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kd">service</span> <span class="n">UserService</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="k">rpc</span> <span class="n">CreateUser</span><span class="p">(</span><span class="n">CreateUserRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">User</span><span class="p">)</span> <span class="p">{}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="k">rpc</span> <span class="n">GetUser</span><span class="p">(</span><span class="n">GetUserRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">User</span><span class="p">)</span> <span class="p">{}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kd">message</span> <span class="nc">CreateUserRequest</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="n">User</span> <span class="n">user</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kd">message</span> <span class="nc">GetUserRequest</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">int32</span> <span class="n">id</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span></code></pre></div><p>This example defines a <code>UserService</code> with two methods: <code>CreateUser</code> and <code>GetUser</code>. Each method takes a specific request message and returns a response.</p>
<p>Notice how clear the intention is. A helpful mental model to contrast modern APIs is:</p>
<ul>
<li><strong>REST</strong> is resource-oriented (relying on URLs and HTTP verbs).</li>
<li><strong>gRPC</strong> is action-oriented (relying on explicit methods).</li>
</ul>
<p>A reader of this spec does not have to map vague HTTP verbs like &ldquo;POST&rdquo; to actions like &ldquo;create.&rdquo; Also, these method names are <a href="https://en.wiktionary.org/wiki/greppable" rel="external">greppable</a>. It is trivial to locate every use of <code>CreateUser</code> across several repositories, making refactoring and impact analysis much easier.</p>
<h3 id="server-reflection">Server Reflection</h3>
<p>Another powerful feature of the gRPC ecosystem is <strong>Server Reflection</strong>. This allows clients or debugging tools (like Postman or grpcurl) to query the server at runtime to discover the available services and methods. This eliminates the need to distribute <code>.proto</code> files to developers just so they can explore the API structure.</p>
<h2 id="distributing-api-contracts">Distributing API Contracts</h2>
<p>Defining a contract is only half the battle. How do the frontend and backend teams actually share that <code>.proto</code> file? If the schema is not easily accessible, the contract is useless.</p>
<p>In practice, teams usually solve this distribution problem in one of three ways:</p>
<ol>
<li><strong>Monorepos:</strong> Storing the backend, frontend, and API definitions in a single repository so all code shares the same source of truth.</li>
<li><strong>Package Managers:</strong> Generating the client SDKs in a CI/CD pipeline and publishing them as internal NPM, Maven, or Go packages.</li>
<li><strong>Schema Registries:</strong> Using dedicated tools like the <a href="https://buf.build/" rel="external">Buf Schema Registry</a> to manage, version, and distribute Protobuf files securely across an organization.</li>
</ol>
<h2 id="what-about-public-apis">What about public APIs?</h2>
<p>Historically, strict RPC contracts were tough for external, public-facing APIs. If your primary consumers were third-party developers, handing them a raw Protobuf file or expecting them to set up gRPC clients caused massive friction. They just wanted to use standard REST with JSON.</p>
<p>This is where tools like <a href="https://connectrpc.com/" rel="external"><strong>ConnectRPC</strong></a> shine. ConnectRPC allows you to define your API using Protobuf, but it automatically exposes endpoints that support standard HTTP/1.1 and JSON serialization as a fallback format.</p>
<p>This hybrid approach also solves the local debugging problem. You can configure ConnectRPC to use JSON during local development specifically so you can read the network tab in plain text, and then flip it to highly efficient binary for production. In practice, you write Protobuf once, and get both gRPC and REST/JSON APIs for free.</p>
<p>Even better, because the source of truth is still Protobuf, you can use ecosystem plugins to automatically generate an OpenAPI specification directly from your <code>.proto</code> files. You get a highly maintainable, contract-driven architecture on the backend, while your external users can still <code>curl</code> standard REST endpoints, read plain JSON, and explore your API via a generated Swagger UI. It offers the best of both worlds without compromising the developer experience on either side.</p>
<blockquote>
<p><strong>Key idea:</strong> Tools like ConnectRPC allow you to maintain strict internal Protobuf contracts while exposing standard REST/JSON APIs to external consumers.</p>
</blockquote>
<h2 id="alternatives">Alternatives</h2>
<p>While Protobuf and gRPC are a powerful duo, there are other contract-based API solutions to consider depending on your architecture:</p>
<ul>
<li><a href="https://www.openapis.org/" rel="external"><strong>OpenAPI (Swagger)</strong></a>: Contracts are not exclusive to RPC. You can use OpenAPI to define strict contracts for RESTful services. However, a harsh reality of the industry is that OpenAPI specs often drift from the actual code because they are bolted on after the fact. To make OpenAPI truly safe, teams must rely on strict framework integration (like FastAPI in Python or tsoa in Node) where the code generates the spec, or vice versa.</li>
<li><a href="https://graphql.org/" rel="external"><strong>GraphQL</strong></a>: Arguably the most mainstream contract-driven API paradigm for frontend developers. Its strictly typed schema defines the exact shape of the available data. Unlike gRPC, which has fixed responses, GraphQL allows the client to dictate the exact payload it wants to receive.</li>
<li><a href="https://twitchtv.github.io/twirp/" rel="external"><strong>Twirp</strong></a>: Developed by Twitch, Twirp is a lightweight RPC framework built on top of Protobuf and HTTP/1.1. It shares similarities with ConnectRPC but focuses on absolute simplicity. It avoids the complexity of HTTP/2 and gRPC streams while still providing generated clients, making it an excellent alternative if full gRPC is overkill for your needs.</li>
<li><a href="https://thrift.apache.org/" rel="external"><strong>Thrift</strong></a>: Originally developed at Facebook, Thrift is a language-neutral protocol for defining service contracts similar to Protobuf. It is often found in large-scale data environments and supports various RPC protocols.</li>
<li><a href="https://trpc.io/" rel="external"><strong>tRPC</strong></a>: This tool defines the API schema directly in TypeScript code to be reused on both the client and the server. While it often pairs with libraries like Zod for runtime validation, it lacks true language-agnostic safety across the network boundary since it relies entirely on a TypeScript ecosystem.</li>
<li><a href="https://avro.apache.org/" rel="external"><strong>Avro</strong></a>: This format uses JSON-like schemas but stores data in a compact binary format. It is a staple in the Apache Kafka ecosystem for streaming data pipelines. It handles schema evolution differently than Protobuf (often sending the schema alongside the data), making it highly flexible for dynamic systems.</li>
</ul>
<h2 id="when-not-to-use-api-contracts">When NOT to Use API Contracts</h2>
<p>While these tools are powerful, they are not a silver bullet. You should reconsider using strict API contracts if:</p>
<ul>
<li><strong>You are building small projects or MVPs:</strong> The initial setup, code generation, and boilerplate overhead might slow down your speed of delivery when rapid iteration is the top priority.</li>
<li><strong>Simplicity for external consumers outweighs strict contracts:</strong> If you are building a straightforward public API and are not using a hybrid tool like ConnectRPC, raw JSON over REST remains the path of least resistance for third-party developers.</li>
<li><strong>Your team lacks tooling maturity:</strong> Implementing gRPC or Protobuf requires solid CI/CD pipelines and a team that is comfortable managing build steps, code generation, and backward-compatible schema evolutions.</li>
</ul>
<blockquote>
<p><strong>Key idea:</strong> Strict API contracts add overhead and may not be suitable for small MVPs, simple public APIs, or teams lacking tooling maturity.</p>
</blockquote>
<h2 id="conclusion">Conclusion</h2>
<p>Contract-based APIs offer a significant advantage in building robust and scalable communication between applications. Protobuf and gRPC provide a powerful combination for defining clear contracts and generating highly efficient code.</p>
<p>As a general rule of thumb: if you are building an early-stage prototype, stick to what is fast and familiar. But if you are scaling a complex system across multiple teams and services, contract-based APIs transition from a nice-to-have to an absolute necessity. Once multiple teams depend on your API, contracts stop being optional. They are how you avoid chaos.</p>
]]></content:encoded></item><item><title>Unit Testing ConnectRPC Servers</title><link>https://kmcd.dev/posts/connectrpc-unittests/</link><pubDate>Tue, 11 Jun 2024 11:00:00 +0000</pubDate><guid>https://kmcd.dev/posts/connectrpc-unittests/</guid><description> 
                &lt;p> &lt;img hspace="5" src="https://kmcd.dev/posts/connectrpc-unittests/cover_hu_15a2717b59d9d13e.webp" /> &lt;/p>
                
                Learn how to test your ConnectRPC services.
                </description><content:encoded><![CDATA[<p>If you&rsquo;ve embarked on the journey of building efficient and scalable RPC systems with <a href="https://connectrpc.com/" rel="external">ConnectRPC</a>, you might be pondering the best way to ensure the reliability and correctness of your services. Unit testing is the obvious tool for this, providing a safety net that catches bugs early and empowers you to refactor code fearlessly. In the ConnectRPC world, unit testing can be daunting due to its integration with Protocol Buffers and the client-server architecture. In this guide, we&rsquo;ll unravel the mysteries of unit testing ConnectRPC services, while arming you with practical examples and advanced techniques to fortify your codebase.</p>
<p>First off, the full source code can be found <a href="https://github.com/sudorandom/kmcd.dev/tree/main/content/posts/2024/connectrpc-unittests/go" rel="external">on github</a>. If it helps, feel free to download, run, and modify as you see fit!</p>
<h2 id="why-unit-test">Why Unit Test?</h2>
<p>Before we dive in, let&rsquo;s address the &ldquo;why.&rdquo; Unit testing your ConnectRPC servers brings a multitude of benefits:</p>
<ul>
<li><strong>Isolation:</strong> Focus on testing individual components in isolation, making it easier to pinpoint and fix issues.</li>
<li><strong>Speed:</strong> Unit tests execute quickly, providing fast feedback during development.</li>
<li><strong>Refactoring Confidence:</strong> When you have solid unit tests, you can refactor your code with confidence, knowing that the tests will catch any unintended consequences.</li>
<li><strong>Documentation:</strong> Well-written unit tests can serve as living documentation, illustrating how your code is meant to be used.</li>
<li><strong>Bug Prevention:</strong> A good suite of unit tests can help you catch bugs early on, before they become harder and more expensive to fix.</li>
</ul>
<h2 id="testing-strategies-with-connectrpc">Testing Strategies with ConnectRPC</h2>
<p>ConnectRPC, built upon the Protocol Buffers ecosystem, offers a couple of primary approaches to unit testing:</p>
<ol>
<li><strong>Direct Service Testing:</strong> This is ideal for unit testing but it&rsquo;s not always possible. You directly call the methods of your service implementation (typically a struct in Go), bypassing any client and server networking.</li>
<li><strong>Server Testing:</strong> This approach creates an actual ConnectRPC server with <code>net/http/httptest</code>. It&rsquo;s helpful when you want to test the interactions between your client and server code but is usually &ldquo;overkill&rdquo; unless you&rsquo;re wanting to test interceptors or HTTP middleware.</li>
</ol>
<h2 id="hands-on-our-example-service">Hands-On: Our example service</h2>
<p>Here is the protobuf file that we&rsquo;re using for our example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-protobuf" data-lang="protobuf"><span class="line"><span class="cl"><span class="n">syntax</span> <span class="o">=</span> <span class="s">&#34;proto3&#34;</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kn">package</span> <span class="nn">greet</span><span class="o">.</span><span class="n">v1</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">option</span> <span class="n">go_package</span> <span class="o">=</span> <span class="s">&#34;example/gen/greet/v1;greetv1&#34;</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kd">message</span> <span class="nc">GreetRequest</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">string</span> <span class="n">name</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kd">message</span> <span class="nc">GreetResponse</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">string</span> <span class="n">greeting</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kd">service</span> <span class="n">GreetService</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="k">rpc</span> <span class="n">Greet</span><span class="p">(</span><span class="n">GreetRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">GreetResponse</span><span class="p">)</span> <span class="p">{}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span></code></pre></div><aside>See the full source at Github: <a href="https://github.com/sudorandom/kmcd.dev/blob/main/content/posts/2024/connectrpc-unittests/go/greet/v1/greet.proto" target="_blank">greet.proto</a>.
</aside>

<p>And here is the resulting server implementation:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span><span class="w"> </span><span class="nx">greeterService</span><span class="w"> </span><span class="kd">struct</span><span class="p">{}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">var</span><span class="w"> </span><span class="nx">_</span><span class="w"> </span><span class="nx">greetv1connect</span><span class="p">.</span><span class="nx">GreetServiceHandler</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="nx">greeterService</span><span class="p">)(</span><span class="kc">nil</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">g</span><span class="w"> </span><span class="o">*</span><span class="nx">greeterService</span><span class="p">)</span><span class="w"> </span><span class="nf">Greet</span><span class="p">(</span><span class="nx">ctx</span><span class="w"> </span><span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">,</span><span class="w"> </span><span class="nx">req</span><span class="w"> </span><span class="o">*</span><span class="nx">connect</span><span class="p">.</span><span class="nx">Request</span><span class="p">[</span><span class="nx">greetv1</span><span class="p">.</span><span class="nx">GreetRequest</span><span class="p">])</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="nx">connect</span><span class="p">.</span><span class="nx">Response</span><span class="p">[</span><span class="nx">greetv1</span><span class="p">.</span><span class="nx">GreetResponse</span><span class="p">],</span><span class="w"> </span><span class="kt">error</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">req</span><span class="p">.</span><span class="nx">Msg</span><span class="p">.</span><span class="nx">Name</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="s">&#34;&#34;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="p">,</span><span class="w"> </span><span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">&#34;missing name&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="c1">// Simulate some network call that takes 10ms</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">select</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">case</span><span class="w"> </span><span class="o">&lt;-</span><span class="nx">ctx</span><span class="p">.</span><span class="nf">Done</span><span class="p">():</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="p">,</span><span class="w"> </span><span class="nx">ctx</span><span class="p">.</span><span class="nf">Err</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">case</span><span class="w"> </span><span class="o">&lt;-</span><span class="nx">time</span><span class="p">.</span><span class="nf">After</span><span class="p">(</span><span class="mi">10</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Millisecond</span><span class="p">):</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">return</span><span class="w"> </span><span class="nx">connect</span><span class="p">.</span><span class="nf">NewResponse</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">greetv1</span><span class="p">.</span><span class="nx">GreetResponse</span><span class="p">{</span><span class="nx">Greeting</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;Hello, &#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">req</span><span class="p">.</span><span class="nx">Msg</span><span class="p">.</span><span class="nx">Name</span><span class="p">}),</span><span class="w"> </span><span class="kc">nil</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><aside>See the full source at Github: <a href="https://github.com/sudorandom/kmcd.dev/blob/main/content/posts/2024/connectrpc-unittests/go/endpoints.go" target="_blank">endpoints.go</a>.
</aside>

<p>Here we defined our <code>greetv1connect.GreetServiceHandler</code> implementation. It implements the <code>Greet</code> method defined in the protobuf file alove. Since this is for demonstration purposes, all we do is check to see if the given <code>name</code> is empty, sleep for 10 milliseconds to simulate a network call and returns the greeting as <code>&quot;Hello, {name}&quot;</code>.</p>
<p>A keep observer might notice that this file contains our first &ldquo;test&rdquo;. The line <code>var _ greetv1connect.GreetServiceHandler = (*greeterService)(nil)</code> is a way of doing a type assertion in Go. It ensures that your <code>greeterService</code> struct correctly implements the <code>GreeterService</code> interface defined by the protobuf file above. This relies on a trick of the Go syntax that will try to bind a variable <code>_</code> using the <code>greetv1connect.GreetServiceHandler</code> type. If the given <code>greeterService</code> pointer doesn&rsquo;t implement the interface then the compiler should complain about what specific methods are missing and which method signatures don&rsquo;t match.</p>
<h2 id="hands-on-direct-service-testing-example">Hands-On: Direct Service Testing Example</h2>
<p>Let&rsquo;s write some unit tests for a simple ConnectRPC service:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">TestGreet</span><span class="p">(</span><span class="nx">t</span><span class="w"> </span><span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">service</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">greeterService</span><span class="p">{}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">response</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">service</span><span class="p">.</span><span class="nf">Greet</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">(),</span><span class="w"> </span><span class="nx">connect</span><span class="p">.</span><span class="nf">NewRequest</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">greetv1</span><span class="p">.</span><span class="nx">GreetRequest</span><span class="p">{</span><span class="nx">Name</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;Alice&#34;</span><span class="p">}))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">t</span><span class="p">.</span><span class="nf">Fatalf</span><span class="p">(</span><span class="s">&#34;Greet failed: %v&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">response</span><span class="p">.</span><span class="nx">Msg</span><span class="p">.</span><span class="nx">Greeting</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="s">&#34;Hello, Alice&#34;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">t</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">&#34;Unexpected greeting: got %q, want %q&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">response</span><span class="p">.</span><span class="nx">Msg</span><span class="p">.</span><span class="nx">Greeting</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;Hello, Alice&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><aside>See the full source at Github: <a href="https://github.com/sudorandom/kmcd.dev/blob/main/content/posts/2024/connectrpc-unittests/go/direct_test.go" target="_blank">direct_test.go</a>.
</aside>

<p><strong>Explanation:</strong>
The <code>TestGreet</code> function creates an instance of your <code>greeterService</code> and directly calls its <code>Greet</code> method. We then assert that the response matches our expectations. This is, by far, the simplest method for testing a ConnectRPC service.</p>
<h2 id="hands-on-table-driven-tests-with-testify">Hands-On: Table-Driven Tests with Testify</h2>
<p>Now that we wrote a single unit test, the next example will show you how to utilize table tests in order to easily write more test cases. You will see code that looks like this in well-tested Go repositories.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">TestGreetTable</span><span class="p">(</span><span class="nx">t</span><span class="w"> </span><span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">service</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">greeterService</span><span class="p">{}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">cancelledCtx</span><span class="p">,</span><span class="w"> </span><span class="nx">cancel</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">context</span><span class="p">.</span><span class="nf">WithCancel</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nf">cancel</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">testCases</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="p">[]</span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">name</span><span class="w">    </span><span class="kt">string</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">ctx</span><span class="w">     </span><span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">req</span><span class="w">     </span><span class="o">*</span><span class="nx">connect</span><span class="p">.</span><span class="nx">Request</span><span class="p">[</span><span class="nx">greetv1</span><span class="p">.</span><span class="nx">GreetRequest</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">want</span><span class="w">    </span><span class="o">*</span><span class="nx">connect</span><span class="p">.</span><span class="nx">Response</span><span class="p">[</span><span class="nx">greetv1</span><span class="p">.</span><span class="nx">GreetResponse</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">wantErr</span><span class="w"> </span><span class="kt">string</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">name</span><span class="p">:</span><span class="w">    </span><span class="s">&#34;Success&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">req</span><span class="p">:</span><span class="w">     </span><span class="nx">connect</span><span class="p">.</span><span class="nf">NewRequest</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">greetv1</span><span class="p">.</span><span class="nx">GreetRequest</span><span class="p">{</span><span class="nx">Name</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;Bob&#34;</span><span class="p">}),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">want</span><span class="p">:</span><span class="w">    </span><span class="nx">connect</span><span class="p">.</span><span class="nf">NewResponse</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">greetv1</span><span class="p">.</span><span class="nx">GreetResponse</span><span class="p">{</span><span class="nx">Greeting</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;Hello, Bob&#34;</span><span class="p">}),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">wantErr</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">},</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">name</span><span class="p">:</span><span class="w">    </span><span class="s">&#34;Empty Name&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">req</span><span class="p">:</span><span class="w">     </span><span class="nx">connect</span><span class="p">.</span><span class="nf">NewRequest</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">greetv1</span><span class="p">.</span><span class="nx">GreetRequest</span><span class="p">{}),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">want</span><span class="p">:</span><span class="w">    </span><span class="kc">nil</span><span class="p">,</span><span class="w"> </span><span class="c1">// Expecting an error</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">wantErr</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;missing name&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">},</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">name</span><span class="p">:</span><span class="w">    </span><span class="s">&#34;Context Cancelled&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">ctx</span><span class="p">:</span><span class="w">     </span><span class="nx">cancelledCtx</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">req</span><span class="p">:</span><span class="w">     </span><span class="nx">connect</span><span class="p">.</span><span class="nf">NewRequest</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">greetv1</span><span class="p">.</span><span class="nx">GreetRequest</span><span class="p">{</span><span class="nx">Name</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;Alice&#34;</span><span class="p">}),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">want</span><span class="p">:</span><span class="w">    </span><span class="kc">nil</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">wantErr</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;context canceled&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">},</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">for</span><span class="w"> </span><span class="nx">_</span><span class="p">,</span><span class="w"> </span><span class="nx">tc</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">testCases</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">t</span><span class="p">.</span><span class="nf">Run</span><span class="p">(</span><span class="nx">tc</span><span class="p">.</span><span class="nx">name</span><span class="p">,</span><span class="w"> </span><span class="kd">func</span><span class="p">(</span><span class="nx">t</span><span class="w"> </span><span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">ctx</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">tc</span><span class="p">.</span><span class="nx">ctx</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="k">if</span><span class="w"> </span><span class="nx">ctx</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">				</span><span class="nx">ctx</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">got</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">service</span><span class="p">.</span><span class="nf">Greet</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span><span class="w"> </span><span class="nx">tc</span><span class="p">.</span><span class="nx">req</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="k">if</span><span class="w"> </span><span class="nx">tc</span><span class="p">.</span><span class="nx">wantErr</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="s">&#34;&#34;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">				</span><span class="nx">require</span><span class="p">.</span><span class="nf">Error</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">				</span><span class="nx">assert</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="p">.</span><span class="nf">Error</span><span class="p">(),</span><span class="w"> </span><span class="nx">tc</span><span class="p">.</span><span class="nx">wantErr</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">				</span><span class="nx">require</span><span class="p">.</span><span class="nf">NoError</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">				</span><span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span><span class="w"> </span><span class="nx">tc</span><span class="p">.</span><span class="nx">want</span><span class="p">,</span><span class="w"> </span><span class="nx">got</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><aside>See the full source at Github: <a href="https://github.com/sudorandom/kmcd.dev/blob/main/content/posts/2024/connectrpc-unittests/go/table_test.go" target="_blank">table_test.go</a>.
</aside>

<p><strong>Explanation:</strong></p>
<ol>
<li><strong>Table Setup:</strong> A <code>testCases</code> slice defines scenarios with varying inputs (<code>req</code>), expected outputs (<code>want</code>), and potential errors (<code>wantErr</code>).</li>
<li><strong>Context Cancellation:</strong> The test case &ldquo;Context Cancelled&rdquo; simulates a cancelled context by creating a context with <code>context.WithCancel</code> and immediately calling <code>cancel()</code>.</li>
<li><strong>Testify Assertions:</strong> The <code>require</code> package is used for assertions that should stop the test if they fail (e.g., requiring an error). The <code>assert</code> package is used for assertions that are not critical for continuing the test. Typically, errors during test setup use <code>require</code> and assertions on the results of the test use <code>assert</code>.</li>
</ol>
<h2 id="hands-on-server-testing-example">Hands-On: Server Testing Example</h2>
<p>Here&rsquo;s how you can test the same service using <code>net/http/httptest</code> server:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">TestGreetWithServer</span><span class="p">(</span><span class="nx">t</span><span class="w"> </span><span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">mux</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nf">NewServeMux</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">mux</span><span class="p">.</span><span class="nf">Handle</span><span class="p">(</span><span class="nx">greetv1connect</span><span class="p">.</span><span class="nf">NewGreetServiceHandler</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">greeterService</span><span class="p">{}))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">server</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">httptest</span><span class="p">.</span><span class="nf">NewServer</span><span class="p">(</span><span class="nx">mux</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">t</span><span class="p">.</span><span class="nf">Cleanup</span><span class="p">(</span><span class="kd">func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">server</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span><span class="w"> </span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">client</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">greetv1connect</span><span class="p">.</span><span class="nf">NewGreetServiceClient</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nx">DefaultClient</span><span class="p">,</span><span class="w"> </span><span class="nx">server</span><span class="p">.</span><span class="nx">URL</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">response</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">client</span><span class="p">.</span><span class="nf">Greet</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">(),</span><span class="w"> </span><span class="nx">connect</span><span class="p">.</span><span class="nf">NewRequest</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">greetv1</span><span class="p">.</span><span class="nx">GreetRequest</span><span class="p">{</span><span class="nx">Name</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;Alice&#34;</span><span class="p">}))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">t</span><span class="p">.</span><span class="nf">Fatalf</span><span class="p">(</span><span class="s">&#34;Greet failed: %v&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">response</span><span class="p">.</span><span class="nx">Msg</span><span class="p">.</span><span class="nx">Greeting</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="s">&#34;Hello, Alice&#34;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">t</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">&#34;Unexpected greeting: got %q, want %q&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">response</span><span class="p">.</span><span class="nx">Msg</span><span class="p">.</span><span class="nx">Greeting</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;Hello, Alice&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><aside>See the full source at Github: <a href="https://github.com/sudorandom/kmcd.dev/blob/main/content/posts/2024/connectrpc-unittests/go/server_test.go" target="_blank">server_test.go</a>.
</aside>

<ul>
<li><strong>Server Setup:</strong> We create a ConnectRPC handler and start it with <code>httptest.NewServer(mux)</code>.</li>
<li><strong>Client Setup:</strong> We create a ConnectRPC client that connects to the server that we just created.</li>
<li><strong>Test Interaction:</strong> We use the client to call the Greet method and assert the response, just like in the direct service testing example.</li>
</ul>
<h2 id="conclusion-test-with-confidence">Conclusion: Test with Confidence</h2>
<p>In this guide, we&rsquo;ve explored the &ldquo;why&rdquo; and &ldquo;how&rdquo; of unit testing your ConnectRPC services. By embracing unit testing as a core part of your development workflow, you&rsquo;ll create more robust, reliable, and maintainable RPC systems. Remember, effective testing isn&rsquo;t just about fixing bugs – it&rsquo;s about building confidence in your codebase and enabling you to iterate and evolve your services with ease.</p>
<p>The full source code can be found <a href="https://github.com/sudorandom/kmcd.dev/tree/main/content/posts/2024/connectrpc-unittests/go" rel="external">on github</a>.</p>
<p><strong>Next Steps:</strong></p>
<ul>
<li><strong>Go Beyond the Basics:</strong> Explore more advanced testing techniques, such as mocking dependencies for more complex scenarios.</li>
<li><strong>Integrate with Your CI/CD:</strong> Automate your unit tests to run as part of your continuous integration and continuous delivery (CI/CD) pipeline for immediate feedback on code changes.</li>
<li><strong>Share Your Knowledge:</strong> Help the ConnectRPC community grow by sharing your own testing strategies and experiences!</li>
</ul>
<p>Ready to put your newfound knowledge into action? Start writing those unit tests and watch your ConnectRPC projects thrive!</p>
]]></content:encoded></item><item><title>gRPC From Scratch: Part 3 - Protobuf Encoding</title><link>https://kmcd.dev/posts/grpc-from-scratch-part-3/</link><pubDate>Tue, 07 May 2024 00:00:00 +0000</pubDate><guid>https://kmcd.dev/posts/grpc-from-scratch-part-3/</guid><description><![CDATA[ 
                <p> <img hspace="5" src="https://kmcd.dev/posts/grpc-from-scratch-part-3/cover_hu_bcc84d40218afdb0.webp" /> </p>
                
                Let&#39;s look under the hood of gRPC by getting into the weeds of protocol buffers.
                ]]></description><content:encoded><![CDATA[<p>In the last two parts, I showed how to make an extremely simple gRPC client and server that&hellip; kind-of works. But I punted on a topic last time that is pretty important: I used generated protobuf types and the Go protobuf library to do all of the heavy lifting of encoding and decoding protobufs for me. That ends today. I&rsquo;ll start by looking at the <a href="https://pkg.go.dev/google.golang.org/protobuf/encoding/protowire" rel="external"><code>protowire</code></a> library directly, which is a bit closer to what is actually happening on the wire. The library includes a fun disclaimer:</p>
<blockquote>
<p>For marshaling and unmarshaling entire protobuf messages, use the google.golang.org/protobuf/proto package instead.</p>
</blockquote>
<p>Am I going to listen to this solid advice? No! I want to know how this works! No reflection and no reliance on generated code. All of the code in this post are taken from unit tests <a href="https://github.com/sudorandom/kmcd.dev/blob/main/content/posts/2024/grpc-from-scratch-part-3/go/protowire_test.go" rel="external">available here</a> so feel free to download the tests and play around with it locally. Now, let&rsquo;s get started.</p>
<h2 id="wire-types">Wire Types</h2>
<p>I discuss in my <a href="https://kmcd.dev/posts/inspecting-protobuf-messages/">Inspecting Protobuf Messages</a> post that protobuf only has a small handful of types. Here they are again:</p>
<table>
  <thead>
      <tr>
          <th>ID</th>
          <th>Name</th>
          <th>Used for</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>0</td>
          <td>VARINT</td>
          <td>int32, int64, uint32, uint64, sint32, sint64, bool, enum</td>
      </tr>
      <tr>
          <td>1</td>
          <td>I64</td>
          <td>fixed64, sfixed64, double</td>
      </tr>
      <tr>
          <td>2</td>
          <td>LEN</td>
          <td>string, bytes, embedded messages, packed repeated fields</td>
      </tr>
      <tr>
          <td>3</td>
          <td>SGROUP</td>
          <td>group start (deprecated)</td>
      </tr>
      <tr>
          <td>4</td>
          <td>EGROUP</td>
          <td>group end (deprecated)</td>
      </tr>
      <tr>
          <td>5</td>
          <td>I32</td>
          <td>fixed32, sfixed32, float</td>
      </tr>
  </tbody>
</table>
<p>For today we&rsquo;re only going to implement some of the <code>LEN</code> and <code>VARINT</code> wire types. Protobuf messages are a series of key-value pairs. Keys are &ldquo;field numbers&rdquo; and values are one of the types in the table above. To save space, protobuf decided to encode both the field number and wire type into a single byte. The lower three bits are the wire type and the other 5 (and maybe more, more on that later) are used for the field number. This is what the &ldquo;Tag-Length&rdquo; part looks like for field number <code>1</code> with the <code>VARINT</code> wire type:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plaintext" data-lang="plaintext"><span class="line"><span class="cl">0000 1000
</span></span></code></pre></div><p>You can shift the three least significant bits off to get the protobuf wire type and the rest is used for the field number. It is at this point that the bitwise operators start becoming useful:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plaintext" data-lang="plaintext"><span class="line"><span class="cl">(field_number &lt;&lt; 3) | wire_type
</span></span></code></pre></div><p>So&hellip; 3 bits for the wire type leaves room for 8 different options so protobuf has room for two more wire types before they have to break compatibility with older versions or add another byte to describe more types. And 5 bits for the field number which leaves room for&hellip;&hellip; <strong>32 different fields</strong>??? What?! You can&rsquo;t have a message that has more than 32 fields?! Why is no one talking about this <em>glaring</em> limitation where the number of fields is limited to a four-year-old&rsquo;s counting ability?! Well, obviously this is not true and, at this point, I am now forced to explain what protobuf refers to as <code>Base 128 Varints</code>.</p>
<h3 id="big-numbers">Big numbers</h3>
<p>In the previous example, we saw <code>VARINT</code> take a single byte. What does it look like when your number is too big? Where does the variableness part of <code>VARINT</code> come in? The protobuf encoding uses what it calls Base-128 Variable Integers in several places. &ldquo;Base 128&rdquo; means you can count to 127 before rolling over to the next &ldquo;digit&rdquo; (or in this case, byte). The most significant bit is used as a continuation bit, which is a signal that there&rsquo;s at least one more byte worth of data to complete this integer. Let&rsquo;s decode one for practice:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plaintext" data-lang="plaintext"><span class="line"><span class="cl">11000000 11000100 00000111   // Original inputs.
</span></span><span class="line"><span class="cl"> 1000000  1000100  0000111   // Drop continuation bits.
</span></span><span class="line"><span class="cl"> 0000111  1000100  1000000   // Convert to big-endian.
</span></span><span class="line"><span class="cl"> 000011110001001000000       // Concatenate.
</span></span><span class="line"><span class="cl"> (1 × 2^16) + (1 × 2^15) +   // Convert binary to decimal
</span></span><span class="line"><span class="cl"> (1 × 2^14) + (1 × 2^13) +   // because we&#39;re not a computer.
</span></span><span class="line"><span class="cl"> (1 × 2^9) + (1 × 2^6)
</span></span><span class="line"><span class="cl"> = 123456                    // Interpret as an unsigned 64-bit integer.
</span></span></code></pre></div><p>Next, I will show you some go code that can do this <code>VARINT</code> encoding process for us. Note that this code is actually in Go&rsquo;s <a href="https://pkg.go.dev/encoding/binary" rel="external">standard library</a> and makes reference to protocol-buffers directly <a href="https://pkg.go.dev/encoding/binary" rel="external">in the documentation</a>; (<a href="https://github.com/golang/go/blob/go1.22.2/src/encoding/binary/varint.go#L39-L47" rel="external">source</a>).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// AppendUvarint appends the varint-encoded form of x,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// as generated by [PutUvarint], to buf and returns the extended buffer.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">func</span><span class="w"> </span><span class="nf">AppendUvarint</span><span class="p">(</span><span class="nx">buf</span><span class="w"> </span><span class="p">[]</span><span class="kt">byte</span><span class="p">,</span><span class="w"> </span><span class="nx">x</span><span class="w"> </span><span class="kt">uint64</span><span class="p">)</span><span class="w"> </span><span class="p">[]</span><span class="kt">byte</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">for</span><span class="w"> </span><span class="nx">x</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mh">0x80</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">buf</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nb">append</span><span class="p">(</span><span class="nx">buf</span><span class="p">,</span><span class="w"> </span><span class="nb">byte</span><span class="p">(</span><span class="nx">x</span><span class="p">)|</span><span class="mh">0x80</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">x</span><span class="w"> </span><span class="o">&gt;&gt;=</span><span class="w"> </span><span class="mi">7</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">return</span><span class="w"> </span><span class="nb">append</span><span class="p">(</span><span class="nx">buf</span><span class="p">,</span><span class="w"> </span><span class="nb">byte</span><span class="p">(</span><span class="nx">x</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>What&rsquo;s happening here? Let&rsquo;s break it down:</p>
<ul>
<li>The core logic is in a <code>for</code> loop that continues as long as x is greater than or equal to 128 (represented by <code>0x80</code> in hexadecimal). Why 128? That&rsquo;s the highest number (in hexadecimal) that you can count to only using 7 bits. Remember that the most significant bit (MSB) is used as a continuation bit so we can only use 7 out 8 of the available bits in each byte.
<ul>
<li>The next line appends the <code>uint64</code> argument truncated to a byte, so the last 8 bits. One of those bits isn&rsquo;t actually used because of the <code>|0x80</code> part of the line. This combines our extracted 7 bits with <code>0x80</code> to ensure that the continuation bit is set to true.</li>
<li>The next line uses the right shift operator to shift 7 bits off of our current <code>uint64</code> value because we&rsquo;ve &ldquo;dealt with&rdquo; these bits already. Next, we check the for-loop condition again until we get a number lower than <code>128</code>.</li>
</ul>
</li>
<li>Finally, we append the final byte as-is because we know that it&rsquo;s less than 128 which assures us that the MSB is not set, so we are done building this integer.</li>
</ul>
<p>Next, we have the decoding code for VARINT. That looks like this (<a href="https://github.com/golang/go/blob/go1.22.2/src/encoding/binary/varint.go#L69-L88" rel="external">source</a>):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Uvarint decodes a uint64 from buf and returns that value and the</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// number of bytes read (&gt; 0). If an error occurred, the value is 0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// and the number of bytes n is &lt;= 0 meaning:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">//	n == 0: buf too small</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">//	n  &lt; 0: value larger than 64 bits (overflow)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">//	        and -n is the number of bytes read</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">func</span><span class="w"> </span><span class="nf">Uvarint</span><span class="p">(</span><span class="nx">buf</span><span class="w"> </span><span class="p">[]</span><span class="kt">byte</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">uint64</span><span class="p">,</span><span class="w"> </span><span class="kt">int</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="kd">var</span><span class="w"> </span><span class="nx">x</span><span class="w"> </span><span class="kt">uint64</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="kd">var</span><span class="w"> </span><span class="nx">s</span><span class="w"> </span><span class="kt">uint</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">for</span><span class="w"> </span><span class="nx">i</span><span class="p">,</span><span class="w"> </span><span class="nx">b</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">buf</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">if</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="nx">MaxVarintLen64</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="c1">// Catch byte reads past MaxVarintLen64.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="c1">// See issue https://golang.org/issues/41185</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="k">return</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="o">-</span><span class="p">(</span><span class="nx">i</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="c1">// overflow</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">if</span><span class="w"> </span><span class="nx">b</span><span class="w"> </span><span class="p">&lt;</span><span class="w"> </span><span class="mh">0x80</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="k">if</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="nx">MaxVarintLen64</span><span class="o">-</span><span class="mi">1</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="nx">b</span><span class="w"> </span><span class="p">&gt;</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">				</span><span class="k">return</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="o">-</span><span class="p">(</span><span class="nx">i</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="c1">// overflow</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="k">return</span><span class="w"> </span><span class="nx">x</span><span class="w"> </span><span class="p">|</span><span class="w"> </span><span class="nb">uint64</span><span class="p">(</span><span class="nx">b</span><span class="p">)</span><span class="o">&lt;&lt;</span><span class="nx">s</span><span class="p">,</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">x</span><span class="w"> </span><span class="o">|=</span><span class="w"> </span><span class="nb">uint64</span><span class="p">(</span><span class="nx">b</span><span class="o">&amp;</span><span class="mh">0x7f</span><span class="p">)</span><span class="w"> </span><span class="o">&lt;&lt;</span><span class="w"> </span><span class="nx">s</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">s</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="mi">7</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">return</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><ul>
<li>It loops through each byte:
<ul>
<li>Checks for overflow (reading past expected max size).</li>
<li>Handles the last byte (if the continuation bit is <code>0</code>):
<ul>
<li>Validates it and returns the decoded value.</li>
</ul>
</li>
<li>Processes continuation bytes:
<ul>
<li>Extracts data bits and combines them with the accumulated value, shifting them to their correct position. The expression <code>b&amp;0x7f</code> ensures that the continuation bit is not included in the result by using a bitwise AND with the current byte and <code>0x7f</code>, which looks like this in binary <code>01111111</code>.</li>
<li><code>s</code> keeps track of the total bits already processed. It increments by 7 because that&rsquo;s how many bits we can use for each byte because of the continuation bit.</li>
</ul>
</li>
</ul>
</li>
<li>If the loop finishes without a valid ending, it signals an error.</li>
</ul>
<p>As a short aside, you should probably know that the modern protobuf library doesn&rsquo;t use these functions from the <code>encoding/binary</code> standard library package. I suspect that they were initially used but the current <a href="https://pkg.go.dev/google.golang.org/protobuf/encoding/protowire" rel="external"><code>protowire</code></a> implementation doesn&rsquo;t use <code>for</code> loops at all because it has performed an optimization technique called <a href="https://en.wikipedia.org/wiki/Loop_unrolling" rel="external">loop unrolling</a>. Here&rsquo;s <a href="https://github.com/protocolbuffers/protobuf-go/blob/v1.33.0/encoding/protowire/wire.go#L184-L263" rel="external">AppendVarint</a> and <a href="https://github.com/protocolbuffers/protobuf-go/blob/v1.33.0/encoding/protowire/wire.go#L265-L367" rel="external">ConsumeVarint</a> that are now used in the modern protobuf library. That code looks insane but it is likely a bit faster than the version of the code that I just showed you.</p>
<p>As a test for comprehension of this last section, you should now understand that smaller numbers take up less room on the wire. That&rsquo;s true for all numeric types except for the fixed-length types: <code>I64</code> and <code>I32</code>. Encoding <code>20</code> in protobbuf takes a single byte on the wire but <code>1234</code> would take two bytes.</p>
<h2 id="integers">Integers</h2>
<p>Now that we know how the <code>VARINT</code> wire-type works we now have enough raw material to write some protobuf packets. Again, we need three things to make a message with a single field: Field number, wire type, and the encoded value. As mentioned earlier, the field number and wire type are merged into a single <code>VARINT</code> value using the formula: <code>(field_number &lt;&lt; 3) | wire_type</code>. This essentially means that the the least significant bits are reserved for the wire type and the rest are used for the field number, and we expand using the Base-128 varint method above if the field number needs more bits to be represented. Now, let&rsquo;s write a full field in protobuf! Here&rsquo;s how we encode a message with a single int32 field:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plaintext" data-lang="plaintext"><span class="line"><span class="cl">1: 1234
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">buf</span><span class="w"> </span><span class="p">[]</span><span class="kt">byte</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nx">buf</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">protowire</span><span class="p">.</span><span class="nf">AppendVarint</span><span class="p">(</span><span class="nx">buf</span><span class="p">,</span><span class="w"> </span><span class="nb">uint64</span><span class="p">(</span><span class="mi">1</span><span class="o">&lt;&lt;</span><span class="mi">3</span><span class="p">)|</span><span class="nb">uint64</span><span class="p">(</span><span class="mi">0</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nx">buf</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">protowire</span><span class="p">.</span><span class="nf">AppendVarint</span><span class="p">(</span><span class="nx">buf</span><span class="p">,</span><span class="w"> </span><span class="mi">1234</span><span class="p">)</span><span class="w">
</span></span></span></code></pre></div><p>And&hellip; that&rsquo;s it. We&rsquo;ve fully encoded probably the simplest (non-empty) protobuf message. We can check that it works by running a test with the &ldquo;real&rdquo; protobuf library:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">TestEncodeRaw</span><span class="p">(</span><span class="nx">t</span><span class="w"> </span><span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="kd">var</span><span class="w"> </span><span class="nx">buf</span><span class="w"> </span><span class="p">[]</span><span class="kt">byte</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">buf</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">protowire</span><span class="p">.</span><span class="nf">AppendVarint</span><span class="p">(</span><span class="nx">buf</span><span class="p">,</span><span class="w"> </span><span class="nb">uint64</span><span class="p">(</span><span class="mi">1</span><span class="o">&lt;&lt;</span><span class="mi">3</span><span class="p">)|</span><span class="nb">uint64</span><span class="p">(</span><span class="mi">0</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">buf</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">protowire</span><span class="p">.</span><span class="nf">AppendVarint</span><span class="p">(</span><span class="nx">buf</span><span class="p">,</span><span class="w"> </span><span class="mi">1234</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">res</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">gen</span><span class="p">.</span><span class="nx">TestMessage</span><span class="p">{}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">require</span><span class="p">.</span><span class="nf">NoError</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span><span class="w"> </span><span class="nx">proto</span><span class="p">.</span><span class="nf">Unmarshal</span><span class="p">(</span><span class="nx">buf</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">res</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span><span class="w"> </span><span class="nb">int32</span><span class="p">(</span><span class="mi">1234</span><span class="p">),</span><span class="w"> </span><span class="nx">res</span><span class="p">.</span><span class="nx">IntValue</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>By the way, these tests are using a protobuf file that looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-protobuf" data-lang="protobuf"><span class="line"><span class="cl"><span class="n">syntax</span> <span class="o">=</span> <span class="s">&#34;proto3&#34;</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kn">package</span> <span class="nn">example</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="kd">message</span> <span class="nc">TestMessage</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="c1">// Basic types
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kt">int32</span> <span class="n">int_value</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">uint64</span> <span class="n">uint_value</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">bool</span> <span class="n">bool_value</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kt">string</span> <span class="n">string_value</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="c1">// Repeated fields (varint encoding)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">repeated</span> <span class="kt">int32</span> <span class="n">repeated_int_value</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="c1">// Nested message
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kd">message</span> <span class="nc">NestedMessage</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>    <span class="kt">string</span> <span class="n">nested_string</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="p">}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="n">NestedMessage</span> <span class="n">nested_message</span> <span class="o">=</span> <span class="mi">11</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span></code></pre></div><p>All of the field numbers and corresponding types in this article match up to this protobuf file.</p>
<p>Okay, let&rsquo;s show what it looks like to read this message:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">tagNumber</span><span class="p">,</span><span class="w"> </span><span class="nx">protoType</span><span class="p">,</span><span class="w"> </span><span class="nx">n</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">protowire</span><span class="p">.</span><span class="nf">ConsumeTag</span><span class="p">(</span><span class="nx">b</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="o">...</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nx">b</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">b</span><span class="p">[</span><span class="nx">n</span><span class="p">:]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nx">i</span><span class="p">,</span><span class="w"> </span><span class="nx">n</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">protowire</span><span class="p">.</span><span class="nf">ConsumeVarint</span><span class="p">(</span><span class="nx">b</span><span class="p">)</span><span class="w">
</span></span></span></code></pre></div><p>Note that I omitted error handling and in a real scenario we need to check the <code>tagNumber</code> and <code>protoType</code> to decide how to read this field.</p>
<h2 id="stringsbyte-arrays">Strings/byte arrays</h2>
<p>Next, we&rsquo;re going to start with strings and byte arrays. These use the <code>LEN</code> wire type. This type is a little more complex than <code>VARINT</code>. It&rsquo;s composed of a <code>VARINT</code> that tells us the size of the <code>LEN</code> field followed by the actual content in bytes. For <code>LEN</code> this content can be a string, a byte array, an embedded message or packed repeated fields. Here&rsquo;s what a field looks like:</p>
<ul>
<li>Field Tag Byte (field number along with the field type, set to <code>2</code> for the <code>LEN</code> type)</li>
<li>Byte size of our content as a <code>VARINT</code></li>
<li>The actual content</li>
</ul>
<p>Encode:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">buf</span><span class="w"> </span><span class="p">[]</span><span class="kt">byte</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nx">buf</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">protowire</span><span class="p">.</span><span class="nf">AppendTag</span><span class="p">(</span><span class="nx">buf</span><span class="p">,</span><span class="w"> </span><span class="nx">protowire</span><span class="p">.</span><span class="nf">Number</span><span class="p">(</span><span class="mi">4</span><span class="p">),</span><span class="w"> </span><span class="nx">protowire</span><span class="p">.</span><span class="nx">BytesType</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nx">buf</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">protowire</span><span class="p">.</span><span class="nf">AppendString</span><span class="p">(</span><span class="nx">buf</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;Hello World!&#34;</span><span class="p">)</span><span class="w">
</span></span></span></code></pre></div><p>Decode:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">tagNumber</span><span class="p">,</span><span class="w"> </span><span class="nx">protoType</span><span class="p">,</span><span class="w"> </span><span class="nx">n</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">protowire</span><span class="p">.</span><span class="nf">ConsumeTag</span><span class="p">(</span><span class="nx">b</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nx">b</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">b</span><span class="p">[</span><span class="nx">n</span><span class="p">:]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nx">i</span><span class="p">,</span><span class="w"> </span><span class="nx">n</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">protowire</span><span class="p">.</span><span class="nf">ConsumeVarint</span><span class="p">(</span><span class="nx">b</span><span class="p">)</span><span class="w">
</span></span></span></code></pre></div><h2 id="integer-arrays-packed">Integer Arrays (Packed)</h2>
<p>Packed repeated fields are a space-saving optimization for integer arrays. It&rsquo;s a feature that&rsquo;s enabled by default for most <code>repeated</code> scalar types when using the proto3 syntax. Instead of encoding each element individually as a separate field/value pair an entire array is packed into a single <code>LEN</code> type. Therefore it looks like the following for repeated <code>int32</code> types:</p>
<ul>
<li><code>VARINT</code> of our field number with the three least significant bits being reserved for field type for <code>LEN</code> which is <code>2</code>. This is the</li>
<li>The raw integer data, also encoded with VARINT for our <code>int32</code> type, one after another</li>
</ul>
<p>Here&rsquo;s an example of how to encode a packed repeated integer field:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">arr</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="p">[]</span><span class="kt">int32</span><span class="p">{</span><span class="mi">100002130</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="mi">4</span><span class="p">,</span><span class="w"> </span><span class="mi">5</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">var</span><span class="w"> </span><span class="nx">buf</span><span class="p">,</span><span class="w"> </span><span class="nx">buf2</span><span class="w"> </span><span class="p">[]</span><span class="kt">byte</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nx">buf</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">protowire</span><span class="p">.</span><span class="nf">AppendVarint</span><span class="p">(</span><span class="nx">buf</span><span class="p">,</span><span class="w"> </span><span class="nb">uint64</span><span class="p">(</span><span class="mi">10</span><span class="o">&lt;&lt;</span><span class="mi">3</span><span class="p">)|</span><span class="nb">uint64</span><span class="p">(</span><span class="mi">2</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">for</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="p">&lt;</span><span class="w"> </span><span class="nb">len</span><span class="p">(</span><span class="nx">arr</span><span class="p">);</span><span class="w"> </span><span class="nx">i</span><span class="o">++</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">buf2</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">protowire</span><span class="p">.</span><span class="nf">AppendVarint</span><span class="p">(</span><span class="nx">buf2</span><span class="p">,</span><span class="w"> </span><span class="nb">uint64</span><span class="p">(</span><span class="nx">arr</span><span class="p">[</span><span class="nx">i</span><span class="p">]))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nx">buf</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">protowire</span><span class="p">.</span><span class="nf">AppendVarint</span><span class="p">(</span><span class="nx">buf</span><span class="p">,</span><span class="w"> </span><span class="nb">uint64</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="nx">buf2</span><span class="p">)))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nx">buf</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nb">append</span><span class="p">(</span><span class="nx">buf</span><span class="p">,</span><span class="w"> </span><span class="nx">buf2</span><span class="o">...</span><span class="p">)</span><span class="w">
</span></span></span></code></pre></div><p>Notice that we are writing the list of <code>int32</code> values to a second buffer. I do this so that we can know the size of the encoded/packed <code>int32</code> values so that I can properly set the size for the encapsulating <code>LEN</code> wire type.</p>
<p>Decoding a packed repeated field is similar. First, you read the field tag (like always). Then we are using <code>protowire.ConsumeBytes</code> that reads the entire <code>LEN</code> value into a byte array. Then you read <code>varint</code> values until you exhaust the buffer. Remember that <code>varint</code> values have that continuation but so the <code>protowire.ConsumeVarint</code> function knows when to finish reading each <code>varint</code> value.</p>
<p>Here&rsquo;s what that looks like in code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">tagNumber</span><span class="p">,</span><span class="w"> </span><span class="nx">protoType</span><span class="p">,</span><span class="w"> </span><span class="nx">n</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">protowire</span><span class="p">.</span><span class="nf">ConsumeTag</span><span class="p">(</span><span class="nx">b</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="o">...</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nx">b</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">b</span><span class="p">[</span><span class="nx">n</span><span class="p">:]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nx">int32buf</span><span class="p">,</span><span class="w"> </span><span class="nx">n</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">protowire</span><span class="p">.</span><span class="nf">ConsumeBytes</span><span class="p">(</span><span class="nx">b</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nx">b</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">b</span><span class="p">[</span><span class="nx">n</span><span class="p">:]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="o">...</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nx">res</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="p">[]</span><span class="kt">int32</span><span class="p">{}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">for</span><span class="w"> </span><span class="nb">len</span><span class="p">(</span><span class="nx">int32buf</span><span class="p">)</span><span class="w"> </span><span class="p">&gt;</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">v</span><span class="p">,</span><span class="w"> </span><span class="nx">n</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">protowire</span><span class="p">.</span><span class="nf">ConsumeVarint</span><span class="p">(</span><span class="nx">int32buf</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">int32buf</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">int32buf</span><span class="p">[</span><span class="nx">n</span><span class="p">:]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">res</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nb">append</span><span class="p">(</span><span class="nx">res</span><span class="p">,</span><span class="w"> </span><span class="nb">int32</span><span class="p">(</span><span class="nx">v</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><h2 id="conclusion">Conclusion</h2>
<p>In this part of the series, we&rsquo;ve taken a deep dive into the world of manual protobuf encoding. We explored a few wire types and the primitives that they are built on, like Base-128 varints, and built functions to encode and decode basic protobuf data types. While for most use cases, you&rsquo;ll probably want to leverage the efficiency and convenience of generated code and libraries like <code>golang/protobuf</code>, understanding how to manually encode these messages can be a valuable asset for debugging, creating a protobuf library implementation in your favorite new language or for possibly creating your own binary encoding that&rsquo;s better in some way than protobuf.</p>
<p>In the next part of the series, we will put more pieces together with a layer on top of our encoding code and integrate this protobuf library into the client and server that we made in parts 1 and 2. Build a real gRPC client and server that uses protobufs for data exchange!</p>
]]></content:encoded></item><item><title>Dropping Unknown Fields in ConnectRPC</title><link>https://kmcd.dev/posts/connectrpc-dropping-unknown-fields/</link><pubDate>Tue, 02 Apr 2024 00:00:00 +0000</pubDate><guid>https://kmcd.dev/posts/connectrpc-dropping-unknown-fields/</guid><description> 
                &lt;p> &lt;img hspace="5" src="https://kmcd.dev/posts/connectrpc-dropping-unknown-fields/cover_hu_6b091745410a087d.webp" /> &lt;/p>
                
                Learn how to drop unknown fields in ConnectRPC to enhance the security of your gRPC services exposed to the internet.
                </description><content:encoded><![CDATA[<p>gRPC, with its focus on performance and language neutrality, remains a popular choice for building microservices and APIs. But when exposing your gRPC service to the internet, there are a few security considerations to account for. Protobuf, the serialization format often used with gRPC, offers various encoding options that can significantly impact your service&rsquo;s security posture.</p>
<p>One crucial optimization for internet-facing gRPC services is customizing the behavior towards <strong>unknown fields</strong>. I&rsquo;ve talked about <a href="https://kmcd.dev/posts/protobuf-unknown-fields/">unknown fields in a previous post</a>, so read that one if unknown fields are still a mystery to you and then come back here. By default, protobuf messages can contain fields that are not defined in the current version of the proto schema. While convenient for development and can help with forward compatibility, this poses a security risk in a public environment.</p>
<p>Here&rsquo;s why you should consider dropping unknown fields when exposing gRPC to the internet:</p>
<ul>
<li><strong>Preventing Malicious Data:</strong> Unknown fields can be exploited by malicious actors to inject unexpected data into your service. This could lead to potential security vulnerabilities like code injection or unexpected behavior.</li>
<li><strong>Ensuring Compatibility:</strong> Uncontrolled unknown fields can cause compatibility issues if your clients are using different versions of the proto schema. Dropping them enforces stricter adherence to the defined message format.</li>
<li><strong>Improving Performance:</strong> Skipping unknown fields during message parsing can lead to performance gains, especially when dealing with large datasets.</li>
</ul>
<h3 id="how-to-drop-unknown-fields">How to Drop Unknown Fields</h3>
<p>Here is how you can drop unknown fields while using the standard <code>proto.UnmarshalOptions</code> struct provided by the <code>google.golang.org/protobuf/proto</code> package. Here&rsquo;s how to do it in your Go code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;google.golang.org/protobuf/proto&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="o">...</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// Configure unmarshalling options to discard unknown fields</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nx">opts</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">proto</span><span class="p">.</span><span class="nx">UnmarshalOptions</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">DiscardUnknown</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// Use the options when unmarshalling incoming messages</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nx">msg</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">MyMessage</span><span class="p">{}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">proto</span><span class="p">.</span><span class="nf">Unmarshal</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span><span class="w"> </span><span class="nx">msg</span><span class="p">,</span><span class="w"> </span><span class="nx">opts</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="c1">// Handle error</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>By setting the <code>DiscardUnknown</code> field to <code>true</code> in the <code>proto.UnmarshalOptions</code> struct before unmarshalling incoming messages, you ensure that any unknown fields are ignored. This helps mitigate the security risks associated with unknown fields while processing internet-facing gRPC requests.</p>
<h2 id="how-to-drop-unknown-fields-in-connect-rpc-servers">How to Drop Unknown Fields in Connect RPC Servers</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;log&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;net/http&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;golang.org/x/net/http2&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;golang.org/x/net/http2/h2c&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;go.akshayshah.org/connectproto&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">func</span><span class="w"> </span><span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">greeter</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">GreetServer</span><span class="p">{}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">mux</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nf">NewServeMux</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">path</span><span class="p">,</span><span class="w"> </span><span class="nx">handler</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">greetv1connect</span><span class="p">.</span><span class="nf">NewGreetServiceHandler</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">greeter</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="c1">// Add an option that customizes protobuf marshalling/unmarshalling behavior</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">connectproto</span><span class="p">.</span><span class="nf">WithBinary</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">proto</span><span class="p">.</span><span class="nx">MarshalOptions</span><span class="p">{},</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">proto</span><span class="p">.</span><span class="nx">UnmarshalOptions</span><span class="p">{</span><span class="nx">DiscardUnknown</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">},</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="c1">// Add an option to customize JSON marshalling/unmachalling</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">connectproto</span><span class="p">.</span><span class="nf">WithJSON</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">protojson</span><span class="p">.</span><span class="nx">MarshalOptions</span><span class="p">{},</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">protojson</span><span class="p">.</span><span class="nx">UnmarshalOptions</span><span class="p">{</span><span class="nx">DiscardUnknown</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">},</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">mux</span><span class="p">.</span><span class="nf">Handle</span><span class="p">(</span><span class="nx">path</span><span class="p">,</span><span class="w"> </span><span class="nx">handler</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">log</span><span class="p">.</span><span class="nf">Fatal</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nf">ListenAndServe</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="s">&#34;localhost:9000&#34;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="nx">h2c</span><span class="p">.</span><span class="nf">NewHandler</span><span class="p">(</span><span class="nx">mux</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">http2</span><span class="p">.</span><span class="nx">Server</span><span class="p">{}),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>In this example, <code>connectproto.WithBinary</code> ensures only messages with defined fields are processed, enhancing the security of your gRPC service. <code>connectproto.WithJSON</code> does the same thing but with JSON.</p>
<h3 id="additional-considerations">Additional Considerations</h3>
<p>While dropping unknown fields is a valuable security practice, it&rsquo;s important to consider potential trade-offs:</p>
<ul>
<li><strong>Backward compatibility:</strong> Clients using older versions of the proto schema will encounter errors if they rely on previously defined unknown fields.</li>
<li><strong>Logging and Debugging:</strong> Dropping unknown fields might make it harder to identify the source of unexpected behavior during development or debugging.</li>
</ul>
<p>In such cases, it&rsquo;s recommended to document these trade-offs and have a clear versioning policy for your gRPC service and client applications.</p>
<h3 id="conclusion">Conclusion</h3>
<p>Exposing gRPC services to the internet requires careful security considerations. By customizing protobuf encoding options, specifically by dropping unknown fields using <code>proto.UnmarshalOptions</code>, you can significantly improve the security posture of your service. Remember to weigh the benefits against potential drawbacks and implement a solution that aligns with your specific needs.</p>
]]></content:encoded></item><item><title>Inspecting Protobuf Messages</title><link>https://kmcd.dev/posts/inspecting-protobuf-messages/</link><pubDate>Sun, 25 Feb 2024 00:00:00 +0000</pubDate><guid>https://kmcd.dev/posts/inspecting-protobuf-messages/</guid><description> 
                &lt;p> &lt;img hspace="5" src="https://kmcd.dev/posts/inspecting-protobuf-messages/cover_hu_9163ecae201b3fce.webp" /> &lt;/p>
                
                
                </description><content:encoded><![CDATA[<p><a href="https://protobuf.dev/" rel="external">Protocol Buffers</a> is an amazing message format. It&rsquo;s <a href="https://nilsmagnus.github.io/post/proto-json-sizes/" rel="external">incredibly compact</a> and <a href="https://medium.com/@akresling/go-benchmark-json-v-protobuf-4ec3c62ec8d4" rel="external">performant</a>. However, these advantages come at a cost. Since Protobuf is a binary format it lacks a lot in readability compared to text-based formats like JSON or XML. If you look at encoded protobuf data it just looks like meaningless ones and zeros.</p>
<p>However, all hope is not lost. Even if you just have a binary protobuf file with no knowledge of the corresponding protobuf file we can still get some information out of it. Let me introduce a tool called <a href="https://github.com/protocolbuffers/protoscope" rel="external">Protoscope</a>. Protoscope is a tool for inspecting protobuf binary. It can do this with or without the protobuf files or the equivalent <a href="https://protobuf.com/docs/descriptors" rel="external">descriptor set</a> (but it can do a better job with the protobuf data).</p>
<hr>
<h3 id="install-protoscope">Install Protoscope</h3>
<p>Okay, let&rsquo;s hit the ground running. Here&rsquo;s how to install protoscope (<a href="https://go.dev/dl/" rel="external">requires go</a>).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">go install github.com/protocolbuffers/protoscope/cmd/protoscope@latest
</span></span></code></pre></div><h3 id="using-protoscope">Using Protoscope</h3>
<p>If you have a binary protobuf file, here&rsquo;s what you can run to get protoscope output:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># protoscope [filename]</span>
</span></span><span class="line"><span class="cl"><span class="c1"># variety.pb contains binary protobuf content.</span>
</span></span><span class="line"><span class="cl">$ protoscope -explicit-wire-types variety.pb
</span></span><span class="line"><span class="cl">1:LEN <span class="o">{</span><span class="s2">&#34;Hello World!&#34;</span><span class="o">}</span>
</span></span><span class="line"><span class="cl">2:VARINT <span class="m">3</span>
</span></span><span class="line"><span class="cl">3:VARINT <span class="m">175</span>
</span></span><span class="line"><span class="cl">4:LEN <span class="o">{</span>
</span></span><span class="line"><span class="cl">  <span class="sb">`</span>736563726574fa43bcab0ddfd7f3582699331e7ebbe267196804216a885fad5a3b0b01da25577220<span class="sb">`</span>
</span></span><span class="line"><span class="cl">  <span class="sb">`</span>f583a5ac18b5c28516de341db3a7b44226e21ed85a6cdb571019fbee016574b8b99cd4ceab728ddd<span class="sb">`</span>
</span></span><span class="line"><span class="cl">  <span class="sb">`</span>34a3e0b54605f7c7d1181ee3e13f4d9a07655f6ec843e74a997fd4b8ab87dc61754a60bd513d0121<span class="sb">`</span>
</span></span><span class="line"><span class="cl">  <span class="sb">`</span>e4ad1fdc9e07a632<span class="sb">`</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">5:LEN <span class="o">{</span><span class="sb">`</span>01020304<span class="sb">`</span><span class="o">}</span>
</span></span><span class="line"><span class="cl">6:LEN <span class="o">{</span>
</span></span><span class="line"><span class="cl">  1:LEN <span class="o">{</span><span class="s2">&#34;Fluffy&#34;</span><span class="o">}</span>
</span></span><span class="line"><span class="cl">  2:VARINT <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">7:VARINT <span class="m">921</span>
</span></span><span class="line"><span class="cl">8:VARINT <span class="m">1</span>
</span></span><span class="line"><span class="cl">9:I32 1.2345i32   <span class="c1"># 0x3f9e0419i32</span>
</span></span><span class="line"><span class="cl">10:LEN <span class="o">{</span>
</span></span><span class="line"><span class="cl">  1:LEN <span class="o">{</span><span class="s2">&#34;key&#34;</span><span class="o">}</span>
</span></span><span class="line"><span class="cl">  2:LEN <span class="o">{</span>1:LEN <span class="o">{</span><span class="s2">&#34;Fred&#34;</span><span class="o">}}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">19:LEN <span class="o">{</span><span class="sb">`</span>ffffffffffffffffff01feffffffffffffffff01fdffffffffffffffff01fcffffffffffffffff01<span class="sb">`</span><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Often times you might see binary files encoded with hexadecimal.</span>
</span></span><span class="line"><span class="cl">$ cat variety.pb.hex
</span></span><span class="line"><span class="cl">0a0c48656c6c6f20576f726c6421100318af01228001736563726574fa43bcab0ddfd7f3582699331e7ebbe267196804216a885fad5a3b0b01da25577220f583a5ac18b5c28516de341db3a7b44226e21ed85a6cdb571019fbee016574b8b99cd4ceab728ddd34a3e0b54605f7c7d1181ee3e13f4d9a07655f6ec843e74a997fd4b8ab87dc61754a60bd513d0121e4ad1fdc9e07a6322a0401020304320a0a06466c75666679100138990740014d19049e3f520d0a036b657912060a04467265649a0128ffffffffffffffffff01feffffffffffffffff01fdffffffffffffffff01fcffffffffffffffff01
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># We can use xxd to convert it to binary then pipe it into protoscope</span>
</span></span><span class="line"><span class="cl">$ xxd -r -ps variety.pb.hex <span class="p">|</span> protoscope -explicit-wire-types
</span></span><span class="line"><span class="cl">... <span class="o">[</span>same as above<span class="o">]</span> ...
</span></span></code></pre></div><hr>
<p>Note that with the examples above protoscope needs to guess types because we did not pass in the <code>-descriptor-set</code> and <code>-message-type</code> options. Why does protoscope need to guess types? One thing that will help with understanding this topic is knowing that <em>protobuf encoding only has <em>6 types</em>.</em> And two of them aren&rsquo;t even used in the latest version. You may be saying to yourself &ldquo;I remember seeing a <a href="https://protobuf.dev/programming-guides/proto3/#scalar" rel="external">table of protobuf types</a> and it had way more than 6!&rdquo; and you would be correct. Although protobufs support many types they are all serialized into 6 &ldquo;wire types.&rdquo;:</p>
<table>
  <thead>
      <tr>
          <th>ID</th>
          <th>Name</th>
          <th>Used for</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>0</td>
          <td>VARINT</td>
          <td>int32, int64, uint32, uint64, sint32, sint64, bool, enum</td>
      </tr>
      <tr>
          <td>1</td>
          <td>I64</td>
          <td>fixed64, sfixed64, double</td>
      </tr>
      <tr>
          <td>2</td>
          <td>LEN</td>
          <td>string, bytes, embedded messages, packed repeated fields</td>
      </tr>
      <tr>
          <td>3</td>
          <td>SGROUP</td>
          <td>group start (deprecated)</td>
      </tr>
      <tr>
          <td>4</td>
          <td>EGROUP</td>
          <td>group end (deprecated)</td>
      </tr>
      <tr>
          <td>5</td>
          <td>I32</td>
          <td>fixed32, sfixed32, float</td>
      </tr>
  </tbody>
</table>
<p>How protobuf encodes each protobuf type into a wire type differs depending on the type. The full explanation exists in <a href="https://protobuf.dev/programming-guides/encoding/" rel="external">the programming guide for the protobuf encoding</a>. I recommend reading through it to fully understand the implications of using the protoscope tool.</p>
<h3 id="strings">Strings</h3>
<p>Let&rsquo;s see what it looks like with a trivial example that is sourced from <a href="https://buf.build/connectrpc/eliza/docs/main:connectrpc.eliza.v1#connectrpc.eliza.v1.SayRequest" rel="external">connectrpc.eliza.v1.SayRequest</a> <code>(protoscope -explicit-wire-types eliza.SayRequest.pb)</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">1:LEN {&#34;Hello World!&#34;}
</span></span></code></pre></div><p>What does this tell us? Well, it tells us the message has a single field with field number 1. It also tells us the value of the string is <code>&quot;World&quot;</code>. <strong>That&rsquo;s pretty incredible compared to the nothing we knew about this collection of bytes a second ago!</strong> Let me quickly explain protobuf field numbers. The <code>1</code> in this example is a field number and it corresponds to the protobuf <a href="https://protobuf.com/docs/language-spec#field-numbers" rel="external">field number</a>. For repeated values, you may see this number appear multiple times. But notice how the name is completely missing. That&rsquo;s because protobuf doesn&rsquo;t want to waste resources transmitting or storing metadata like that. It would be fair to summarize protobuf as a list of key/value pairs. The key is the field number and the values are one of the basic types in protobuf.</p>
<p>For the record, here&rsquo;s what <a href="https://buf.build/connectrpc/eliza/docs/main:connectrpc.eliza.v1#connectrpc.eliza.v1.SayRequest" rel="external">connectrpc.eliza.v1.SayRequest</a> looks like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-protobuf" data-lang="protobuf"><span class="line"><span class="cl"><span class="kd">message</span> <span class="nc">SayRequest</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>	<span class="kt">string</span> <span class="n">sentence</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span></code></pre></div><blockquote>
<p>Disclaimer: Protoscope has to guess types in a lot of instances because the protobuf encoding has only 6 wire types (two deprecated): <code>VARINT</code>, <code>I64</code>, <code>LEN</code>, and <code>I32</code> are used with modern protobuf files. In the example above, field 1 could have been a string, byte array, an embedded message, or packed repeated fields. Protoscope <strong>guessed</strong> that it was a string and showed it to us as a string. It <em>can</em> guess wrong.</p>
</blockquote>
<h3 id="more-strings">More Strings</h3>
<p>Okay, now we&rsquo;re going to look at a message derived from a different type: <a href="https://buf.build/connectrpc/eliza/docs/main:connectrpc.eliza.v1#connectrpc.eliza.v1.IntroduceRequest" rel="external">connectrpc.eliza.v1.IntroduceRequest</a> <code>(protoscope -explicit-wire-types eliza.IntroduceRequest.pb)</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">1:LEN {&#34;Hello World!&#34;}
</span></span></code></pre></div><p>Wait, what? It&rsquo;s the <em>exact</em> same? Yep. If the field numbers and types match there&rsquo;s no distinguishable difference when protobuf is encoded into binary. Here&rsquo;s the protobuf type:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-protobuf" data-lang="protobuf"><span class="line"><span class="cl"><span class="kd">message</span> <span class="nc">IntroduceRequest</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>	<span class="kt">string</span> <span class="n">name</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span></code></pre></div><p>Notice the message contains a single string field which is similar to `SayRequest`` above, but there are a few notable differences. The message name and the field name are different but since those two things are never transmitted over the wire with protobuf we can&rsquo;t tell the difference between these two message types without prior knowledge. This flexibility allows you to make certain significant changes to your protobuf file without changing what is encoded&hellip; but you do have to <a href="https://earthly.dev/blog/backward-and-forward-compatibility/" rel="external">follow some rules</a>. These rules make more sense with more knowledge of the protobuf encoding.</p>
<h3 id="bytes">Bytes</h3>
<p>Okay, now let&rsquo;s look at a new type: bytes. Let&rsquo;s take a look at what that looks like with a byte array <code>(protoscope -explicit-wire-types bytes.pb)</code>):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">4:LEN {
</span></span><span class="line"><span class="cl">  `7365637265745b060fd327e7efb49cb89b479b65b71043859c2bafbd2d712fbcea2a759b230ceed4`
</span></span><span class="line"><span class="cl">  `af7177eef821cd43935bdc74b682aa939ad99379b6a0e9c4e156b42691bf5e7cb7c8194eea230de4`
</span></span><span class="line"><span class="cl">  `8981314872d7286920d6c5d2799546ce6131391ecd75edf27c17f413e257f50f9834454566c3439d`
</span></span><span class="line"><span class="cl">  `7d2e52204aa57ba7`
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>What you&rsquo;re seeing here is a hexadecimal representation of the bytes in our field. In this example, we mostly have random data. But if you pass this hexadecimal text through a hex-to-string converter you may notice that the beginning text, <code>736563726574</code>, decodes to <code>secret</code>. Even though there is ASCII string content in the byte array, protoscope still treats this as a byte array, not a string. Here&rsquo;s a byte array with a string that says <code>Hello World!</code> as the content <code>(protoscope -explicit-wire-types bytes2.pb)</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">4:LEN {&#34;Hello World!&#34;}
</span></span></code></pre></div><p>Wait, what? Protoscope renders the text as text! What gives? Protoscope is, again, guessing the type of the data. It notices that all of the included bytes are in the ASCII range so it renders the content as text. Could this be the wrong thing to do? Maybe!</p>
<h2 id="numbers">Numbers</h2>
<p>Now let&rsquo;s at numbers represented in protobuf. You may see some&hellip; odd things. We&rsquo;ll break it down field by field <code>(protoscope -explicit-wire-types numbers.pb)</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">2:VARINT 3
</span></span><span class="line"><span class="cl">3:VARINT 175
</span></span><span class="line"><span class="cl">5:LEN {`01020304`}
</span></span><span class="line"><span class="cl">7:VARINT 921
</span></span><span class="line"><span class="cl">8:VARINT 1
</span></span><span class="line"><span class="cl">9:I32 1.2345i32   # 0x3f9e0419i32
</span></span><span class="line"><span class="cl">19:LEN {`ffffffffffffffffff01feffffffffffffffff01fdffffffffffffffff01fcffffffffffffffff01`}
</span></span></code></pre></div><p>Refer to this table to see the actual protobuf types and the intended values. You will notice that several of them don&rsquo;t match up with what protoscope outputs at all.</p>
<table>
  <thead>
      <tr>
          <th>Field Number</th>
          <th>Actual Type</th>
          <th>Actual Value</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>2</td>
          <td>enum</td>
          <td>3 (AnEnum.C)</td>
      </tr>
      <tr>
          <td>3</td>
          <td>uint32</td>
          <td>175</td>
      </tr>
      <tr>
          <td>5</td>
          <td>repeated uint64</td>
          <td>1, 2, 3, 4</td>
      </tr>
      <tr>
          <td>7</td>
          <td>int64</td>
          <td>921</td>
      </tr>
      <tr>
          <td>8</td>
          <td>bool</td>
          <td>true</td>
      </tr>
      <tr>
          <td>9</td>
          <td>float</td>
          <td>1.2345</td>
      </tr>
      <tr>
          <td>19</td>
          <td>I32</td>
          <td>-1, -2, -3, -4</td>
      </tr>
  </tbody>
</table>
<ul>
<li><strong>2</strong>: Enum fields are encoded as numbers on the wire. because of this, the name of the enum value may be unknown to you without the protobuf file.</li>
<li><strong>3</strong>: uint32 types look like what you&rsquo;d expect! Nice.</li>
<li><strong>5</strong>: This is the first super weird one. Why is it shown as a string? This has to do with <a href="https://protobuf.dev/programming-guides/encoding/#packed" rel="external">packed repeated fields</a>. The protobuf encoding packs repeated primitive types into a single <code>LEN</code> field (instead of using <code>VARINT</code>, <code>I64</code> or <code>I32</code> as normal). Therefore, protoscope may simply represent this as a string or byte array because it can&rsquo;t tell the difference on the wire.</li>
<li><strong>7</strong>: int64 types also look like what you&rsquo;d expect! Nice.</li>
<li><strong>8</strong>: Booleans are encoded as false = <code>0</code> and true = <code>1</code>.</li>
<li><strong>9</strong>: In this case, protobufs treated the float correctly and we get the correct value.</li>
<li><strong>19</strong>: This has to be the weirdest case. This looks so strange because it&rsquo;s a result of using <code>repeated int32</code> type, <a href="https://protobuf.dev/programming-guides/encoding/#packed" rel="external">which is packed</a> with negative values. Protoscope thinks this value looks like a <code>LEN</code> wire type with binary data in it. It guessed the type incorrectly this time.</li>
</ul>
<h2 id="submessages-and-maps">Submessages and Maps</h2>
<p>In protobuf you can put messages instead of other messages, so let&rsquo;s look at what that looks like from protoscope&rsquo;s perspective <code>(protoscope -explicit-wire-types submessages.pb)</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">6:LEN {
</span></span><span class="line"><span class="cl">  1:LEN {&#34;Harey&#34;}
</span></span><span class="line"><span class="cl">  2:VARINT 1
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">14:LEN {
</span></span><span class="line"><span class="cl">  1:LEN {&#34;buf.build/connectrpc/eliza/connectrpc.eliza.v1.SayRequest&#34;}
</span></span><span class="line"><span class="cl">  2:LEN {1:LEN {&#34;Hello World!&#34;}}
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">17:LEN {}
</span></span><span class="line"><span class="cl">18:LEN {1:LEN {&#34;alpha&#34;}}
</span></span><span class="line"><span class="cl">18:LEN {1:LEN {&#34;beta&#34;}}
</span></span></code></pre></div><p>From the protoscope output above you might also notice that we have two field <code>18</code> values. That is because submessages cannot be packed as primitive types can. The &ldquo;unpacked&rdquo; way of representing a repeated value in the protobuf encoding is to simply write the field multiple times with different values. Simple. So field <code>18</code> is likely a repeated submessage field.</p>
<p>Now let&rsquo;s look at maps: <code>(protoscope -explicit-wire-types maps.pb)</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">10:LEN {
</span></span><span class="line"><span class="cl">  1:LEN {&#34;key&#34;}
</span></span><span class="line"><span class="cl">  2:LEN {
</span></span><span class="line"><span class="cl">    1:LEN {&#34;Corgi&#34;}
</span></span><span class="line"><span class="cl">    2:VARINT 1
</span></span><span class="line"><span class="cl">  }
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">20:LEN {
</span></span><span class="line"><span class="cl">  1:LEN {&#34;Knock, knock&#34;}
</span></span><span class="line"><span class="cl">  2:LEN {&#34;who&#39;s there?&#34;}
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">20:LEN {
</span></span><span class="line"><span class="cl">  1:LEN {&#34;Java&#34;}
</span></span><span class="line"><span class="cl">  2:LEN {&#34;Coffee, not code.&#34;}
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>Wait, what? This looks a lot like submessages! There&rsquo;s a reason for that! Maps ARE submessages in the protobuf encoding. Here&rsquo;s basically what the encoder is doing.</p>
<p>A map that looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-protobuf" data-lang="protobuf"><span class="line"><span class="cl"><span class="kd">message</span> <span class="nc">TestWithMap</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="n">map</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="kt">int32</span><span class="p">&gt;</span> <span class="n">name_to_age</span> <span class="o">=</span> <span class="mi">7</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span></code></pre></div><p>&hellip; is converted into a submessage that looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-protobuf" data-lang="protobuf"><span class="line"><span class="cl"><span class="kd">message</span> <span class="nc">TestWithMap</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="kd">message</span> <span class="nc">name_to_age_Entry</span> <span class="p">{</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>    <span class="k">optional</span> <span class="kt">string</span> <span class="n">key</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>    <span class="k">optional</span> <span class="kt">int32</span> <span class="n">value</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="p">}</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="k">repeated</span> <span class="n">name_to_age_Entry</span> <span class="n">name_to_age</span> <span class="o">=</span> <span class="mi">7</span><span class="p">;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="p">}</span><span class="err">
</span></span></span></code></pre></div><p>This shows that protobuf is a very practical encoding that re-uses basic concepts to support more complex structures.</p>
<h2 id="summary">Summary</h2>
<p>In this blog post, we delved into the intricacies of inspecting binary Protobuf messages using the Protoscope tool. We highlighted its ability to decipher binary data even without the corresponding Protobuf files. We also covered the six wire types used in Protobuf encoding and explored various scenarios involving strings, bytes, numbers, submessages, and maps.</p>
<p>I didn&rsquo;t cover all of the weird edge cases. There are features in the, now deprecated, proto2 format that I didn&rsquo;t show. However, I hope that I&rsquo;ve shown that you can get <em>something</em> from a binary protobuf file. This, alone, is quite impressive for a binary format. You would usually have a very hard time understanding anything without knowledge of the specific binary protocol. This demonstrates how protobufs takes some of the benefits you might get from text-based encodings (composability, support for &ldquo;unknown&rdquo; fields, some amount of discoverability) with the performance of binary formats (speed, reduced size) but protobuf does bring in an extra ingredient: contracts. Because protobuf files are the source of truth for the format and type-safe serialization code, gRPC client code, gRPC server code, and documentation can all be generated from protobuf files this shows the strength of the format&hellip; which is why you should try to never be in a situation where you NEED to use protoscope. You should always have a <a href="https://protobuf.com/docs/descriptors" rel="external">descriptor set</a> or the protobuf files nearby to decode these messages.</p>
<p>For a more extensive overview of the protobuf binary encoding refer to <a href="https://protobuf.dev/programming-guides/encoding/" rel="external">the official documentation</a>.</p>
]]></content:encoded></item></channel></rss>