Golang Nugget - December 2, 2024

From Database Connection Patterns to Nintendo Switch Compilation and Secure Cookie Management

Hey!

Last week, I didn’t get a chance to publish either Golang Nugget or Architecture Nugget. If you’re wondering why, it’s because I was busy migrating both newsletters to their separate domains (golangnugget.com and architecturenugget.com) and switching platforms! Both are now running on Beehiiv.

I’ve also decided to publish more often but with shorter posts. That’s why this one doesn’t include as much as usual. Hopefully, my 9-to-5 will give me time to publish the next one midweek. If you’ve come across or written something interesting, feel free to share it with me—I’d be happy to pass it along if it’s relevant.

Now, let’s jump into some Golang Nuggets!

When you’re building web apps in Go, figuring out how to handle database access in your HTTP handlers can be a bit of a puzzle. Let’s dive into four different ways you can set up your database connections.

The easiest way? Just use a global variable to keep your database connection pool. You set it up in main() and then you can use it anywhere in your app:

var DB *sql.DB

func main() {
    DB, err = sql.Open("postgres", "postgres://user:pass@localhost/bookstore")
    // Use DB globally
}

But if your app is getting more complicated, dependency injection might be the way to go. You can make an Env struct to keep all your dependencies together:

type Env struct {
    db *sql.DB
    logger *log.Logger
}

func (env *Env) booksIndex(w http.ResponseWriter, r *http.Request) {
    // Access env.db directly
}

For something a bit more advanced, you can wrap your connection pool in a custom type:

type BookModel struct {
    DB *sql.DB
}

func (m BookModel) All() ([]Book, error) {
    // Database logic here
}

This lets you use interfaces for mocking when you’re testing:

type BookStore interface {
    All() ([]Book, error)
}

type Env struct {
    books BookStore
}

The last method involves using request context to store the connection pool, but it’s not really recommended because you lose type safety and it makes things a bit murky:

func injectDB(db *sql.DB, next http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        ctx := context.WithValue(r.Context(), "db", db)
        next.ServeHTTP(w, r.WithContext(ctx))
    }
}

Each of these patterns has its own place. Global variables are fine for small apps, dependency injection is great for bigger apps with lots of dependencies, and the wrapper pattern makes testing a breeze with interfaces. Pick the one that fits your project’s size, complexity, and testing needs best.

For a deeper dive into these methods, check out “Organising Database Access in Go” for all the nitty-gritty details.

Compiling Go programs for the Nintendo Switch used to be a real headache. You had to convert them to WebAssembly and then to C++, which was slow and didn’t perform well. But now there’s a new way to do it with native compilation, thanks to some clever tweaks and system call replacements.

The main trick is swapping out system calls for standard C function calls using Go’s -overlay option. This led to the creation of a new tool called Hitsumabushi to handle these swaps in an organized way.

Some key details of the implementation include:

Using libcCall to safely call C functions from the runtime:

//go:nosplit
//go:cgo_unsafe_args
func write1(fd uintptr, p unsafe.Pointer, n int32) int32 {
 return libcCall(unsafe.Pointer(abi.FuncPCABI0(write1_trampoline)), 
                unsafe.Pointer(&fd))
}

Creating pseudo systems for:

  • File system operations (stdout/stderr)

  • Memory management with calloc

  • Thread management using pthread

  • Futex operations with condition variables

Building with specific flags:

GOOS=linux GOARCH=arm64 go build -buildmode=c-archive

This approach brought some big improvements:

  • Cut down compilation time from 5-10 minutes to under 10 seconds

  • Got rid of GC-related game pauses

  • Full support for controller input, touch, and audio

If you’re into OS internals and runtime tweaks, the original article “Compiling a Go program into a native binary for Nintendo Switch” is a must-read for all the juicy details and insights into Go’s runtime architecture.

Handling cookies in Go can be a bit of a challenge, especially when you’re dealing with special characters, security, and complex data types. Let’s see how to tackle these issues the right way!

The basic way to start is with Go’s http.Cookie struct, which lets you set important cookie attributes like Name, Value, Path, and security options. Here’s how it looks:

cookie := http.Cookie{
    Name:     "exampleCookie",
    Value:    "Hello world!",
    Path:     "/",
    MaxAge:   3600,
    HttpOnly: true,
    Secure:   true,
    SameSite: http.SameSiteLaxMode,
}

There are three main hurdles when working with cookies:

  1. Special characters and length limits

  2. Cookie tampering

  3. Protecting sensitive data

To deal with special characters and size limits, base64 encoding is your friend:

func Write(w http.ResponseWriter, cookie http.Cookie) error {
    cookie.Value = base64.URLEncoding.EncodeToString([]byte(cookie.Value))
    if len(cookie.String()) > 4096 {
        return ErrValueTooLong
    }
    http.SetCookie(w, &cookie)
    return nil
}

For tamper-proof cookies, HMAC signatures are the way to go:

When it comes to sensitive data, AES-GCM encryption gives you both confidentiality and authenticity:

func WriteEncrypted(w http.ResponseWriter, cookie http.Cookie, secretKey []byte) error {
    block, _ := aes.NewCipher(secretKey)
    aesGCM, _ := cipher.NewGCM(block)
    nonce := make([]byte, aesGCM.NonceSize())
    plaintext := fmt.Sprintf("%s:%s", cookie.Name, cookie.Value)
    encryptedValue := aesGCM.Seal(nonce, nonce, []byte(plaintext), nil)
    cookie.Value = string(encryptedValue)
    return Write(w, cookie)
}

For storing complex data types, the encoding/gob package is super handy. Just make sure to register your types first:

gob.Register(&User{})

This article has loads of implementation details and security tips that we couldn’t cover here, so I’d definitely recommend checking out “A complete guide to working with Cookies in Go” for a deeper dive into cookie management in Go apps.

Reply

or to participate.