Skip to content

Instantly share code, notes, and snippets.

@coolaj86
Last active August 3, 2022 21:17
Show Gist options
  • Save coolaj86/4691e077c2f86e64ca0a089627a0c6a9 to your computer and use it in GitHub Desktop.
Save coolaj86/4691e077c2f86e64ca0a089627a0c6a9 to your computer and use it in GitHub Desktop.
Golang Cheatsheet

GoDoc Tricks: https://pkg.go.dev/github.com/fluhus/godoc-tricks

Browser Extension

https://chrome.google.com/webstore/detail/go-search-extension/epanejkfcekejmmfbcpbcbigfpefbnlb

new project

go mod init github.com/coolaj86/projectname

tools package

mkdir -p tools

go get github.com/goware/modvendor
go get github.com/kyleconroy/sqlc/cmd/sqlc@latest

go mod vendor
go run -mod=vendor github.com/goware/modvendor -copy="**/*.c **/*.h **/*.proto" -v

build.go

//go:generate go run -mod=vendor github.com/kyleconroy/sqlc/cmd/sqlc generate

package main

tools/tools.go:

//go:build tools

package tools

import (
    // for the modvendor command
    _ "github.com/goware/modvendor"
    
    // for the sqlc command
    _ "github.com/kyleconroy/sqlc/cmd/sqlc"
)
go mod tidy

go generate -mod=vendor ./...

go.mod exclude and replace

module github.com/example/project

require (
    github.com/SermoDigital/jose v0.0.0-20180104203859-803625baeddc
    github.com/google/uuid v1.1.0
)

exclude github.com/SermoDigital/jose v0.9.1

replace github.com/google/uuid v1.1.0 => git.coolaj86.com/coolaj86/uuid.go v1.1.1

See https://stackoverflow.com/a/53824026/151312.

Array Sort, Contains, ...

sort.Strings(haystack)
func contains(haystack []string, needle string) bool {
    i := sort.SearchStrings(haystack, needle)
    return i < len(haystack) && needle == haystack[i]
}

Internationalization

https://phrase.com/blog/posts/internationalization-i18n-go/

HTTP Request

In part from https://blog.cloudflare.com/the-complete-guide-to-golang-net-http-timeouts/

c := &http.Client{
    Transport: &http.Transport{
        Dial: (&net.Dialer{
                Timeout:   30 * time.Second,
                KeepAlive: 30 * time.Second,
        }).Dial,
        TLSHandshakeTimeout:   10 * time.Second,
        ResponseHeaderTimeout: 10 * time.Second,
        ExpectContinueTimeout: 1 * time.Second,
    }
}

c := make(chan struct{})
timer := time.AfterFunc(5*time.Second, func() {
	close(c)
})

// Serve 256 bytes every second.
req, err := http.NewRequest("GET", "http://httpbin.org/range/2048?duration=8&chunk_size=256", nil)
if err != nil {
	log.Fatal(err)
}
req.Cancel = c

log.Println("Sending request...")
resp, err := c.Do(req)
if err != nil {
	log.Fatal(err)
}
defer resp.Body.Close()

Base64 (and Random IDs)

s := base64.RawURLEncoding.EncodeToString([]byte(data))
b, err := base64.RawURLEncoding.DecodeString(s)

Random IDs

var Rando = rand.Reader
b := make([]byte, 16)
n, err := Rando.Read(b)
id := base64.RawURLEncoding.EncodeToString(b)

Parse Fractional Seconds

func ParseSeconds(s string) (int64, int64, error) {
	seconds, err := strconv.ParseFloat(s, 64)
	if nil != err {
		return 0.0, 0.0, err
	}
	secs, nanos := SecondsToInts(seconds)
	return secs, nanos, nil
}

func SecondsToInts(seconds float64) (int64, int64) {
	secs := math.Floor(seconds)
	nanos := math.Round((seconds - secs) * 1_000_000_000)
	return int64(secs), int64(nanos)
}
func ParseSeconds(secs string) (int64, int64, error) {
	// "789.0123" => []string{"789", "0123"}
	parts := strings.Split(secs, ".")
	if len(parts) > 2 {
		return 0, 0, errors.New("could not parse as seconds")
	} else if len(parts) < 2 {
		// no nanoseconds, just seconds
		// "789" => []string{"789"}
		s, err := strconv.ParseInt(parts[0], 10, 64)
		return s, 0, err
	}

	// convert the second's part
	s, err := strconv.ParseInt(parts[0], 10, 64)
	if nil != err {
		return 0, 0, err
	}

	// get nanoseconds from fractional second
	d, err := time.ParseDuration("0." + parts[1] + "s")
	return s, d.Nanoseconds(), err
}

Parse Unix Time

func ParseUnixTime(seconds string) (time.Time, error) {
	secs, nano, err := ParseSeconds(seconds)
	if nil != err {
		return time.Time{}, err
	}

	return time.Unix(secs, nano), nil
}

Time to Unix Seconds

func ToUnixSeconds(t time.Time) float64 {
    // 1614236182.651912345
    secs := float64(t.Unix())                          // 1614236182
    nanos := float64(t.Nanosecond()) / 1_000_000_000.0 // 0.651912345

    // in my case I want to truncate the precision to milliseconds
    nanos = math.Round((10000 * nanos) / 10000) // 0.6519

    s := secs + nanos // 1614236182.651912345
    return s
}

Left Pad and Right Pad

// Left pad up to 10 spaces
fmt.Sprintf("'%*s'", 10, "Hello")
// '     Hello'

// Right pad up to 10 spaces
fmt.Sprintf("'Hello%-*s'", 10, "Hello")
// 'Hello     '

// Left pad up to 10 zeros
fmt.Sprintf("'%0*s'\n", 10, "Hello")
// '00000Hello'

// Right pad up to 10 zeros... psych!
fmt.Sprintf("'%-0*s'\n", 10, "Hello")
// 'Hello     '

HTTP Request

  func SafeRequest(req *http.Request) (*http.Response, error) {
    var netTransport = &http.Transport{
      Dial: (&net.Dialer{
        Timeout: 5 * time.Second,
      }).Dial,
      TLSHandshakeTimeout: 5 * time.Second,
    }
    var netClient = &http.Client{
      Timeout:   time.Second * 10,
      Transport: netTransport,
    }
    response, err := netClient.Do(req)
    if nil != err {
      return nil, err
    }
    body := response.Body
    response.Body = io.LimitReader(body, 1024 * 1024)
    //response.Body = MaxBytesReader(body, 1024*1024)
    return response, nil
  }

HTTP Server

    srv := &http.Server{
        Addr:              runOpts.Addr,
        Handler:           r,
        ReadHeaderTimeout: 2 * time.Second,
        ReadTimeout:       10 * time.Second,
        WriteTimeout:      20 * time.Second,
        MaxHeaderBytes:    1024 * 1024, // 1MiB
    }
    if err := srv.ListenAndServe(); nil != err {
        fmt.Fprintf(os.Stderr, "%s", err)
        os.Exit(1)
        return
    }

HTTP Test Server

var srv *httptest.Server

func TestMain(m *testing.M) {
    // math/rand.Seed(0)
    
    srv = httptest.NewServer(mux)
    os.Exit(m.Run())
}
  client := srv.Client()
  urlstr, _ := url.Parse(srv.URL + "/debug/verify?exp=false")

  req := &http.Request{
    Method: "POST",
    URL:    urlstr,
    //Body:   ioutil.NopCloser(bytes.NewReader(jws)),
    Header: http.Header{},
  }
  req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", jwt))
  res, err := client.Do(req)
  if nil != err {
    t.Error(err)
    return
  }
  data, err := ioutil.ReadAll(res.Body)
  if nil != err {
    t.Error(err)
    return
  }
  if 200 != res.StatusCode {
    log.Printf(string(data))
    t.Error(fmt.Errorf("bad status code: %d", res.StatusCode))
    return
  }

HTTP Handler

r.Body = http.MaxBytesReader(w, r.Body, 1 << 20)

Tee HTTP Body

TODO

Security

Compare

import "crypto/subtle"

func ConstantTimeCompare(x, y []byte) bool {
  return 1 == subtle.ConstantTimeCompare(x, y)
}

Error Strategies

JSON to Go

JSON to Postgres

https://numidian.io/convert/json/to/postgres

410 Gone

export GO111MODULE=on
export GOPROXY=direct
export GOSUMDB=off

X-Hub-Signature

const (
	// sha1Prefix is the prefix used by GitHub before the HMAC hexdigest.
	sha1Prefix = "sha1"
	// sha256Prefix and sha512Prefix are provided for future compatibility.
	sha256Prefix = "sha256"
	sha512Prefix = "sha512"
	// signatureHeader is the GitHub header key used to pass the HMAC hexdigest.
	signatureHeader = "X-Hub-Signature"
)

	if len(secretToken) > 0 {
		sig := r.Header.Get(signatureHeader)
		if err := ValidateSignature(sig, body, secretToken); err != nil {
			return nil, err
		}
	}
    
// ValidateSignature validates the signature for the given payload.
// signature is the GitHub hash signature delivered in the X-Hub-Signature header.
// payload is the JSON payload sent by GitHub Webhooks.
// secretToken is the GitHub Webhook secret token.
//
// GitHub API docs: https://developer.github.com/webhooks/securing/#validating-payloads-from-github
func ValidateSignature(signature string, payload, secretToken []byte) error {
	messageMAC, hashFunc, err := messageMAC(signature)
	if err != nil {
		return err
	}
	if !checkMAC(payload, messageMAC, secretToken, hashFunc) {
		return errors.New("payload signature check failed")
	}
	return nil
}

// checkMAC reports whether messageMAC is a valid HMAC tag for message.
func checkMAC(message, messageMAC, key []byte, hashFunc func() hash.Hash) bool {
	expectedMAC := genMAC(message, key, hashFunc)
	return hmac.Equal(messageMAC, expectedMAC)
}

// messageMAC returns the hex-decoded HMAC tag from the signature and its
// corresponding hash function.
func messageMAC(signature string) ([]byte, func() hash.Hash, error) {
	if signature == "" {
		return nil, nil, errors.New("missing signature")
	}
	sigParts := strings.SplitN(signature, "=", 2)
	if len(sigParts) != 2 {
		return nil, nil, fmt.Errorf("error parsing signature %q", signature)
	}

	var hashFunc func() hash.Hash
	switch sigParts[0] {
	case sha1Prefix:
		hashFunc = sha1.New
	case sha256Prefix:
		hashFunc = sha256.New
	case sha512Prefix:
		hashFunc = sha512.New
	default:
		return nil, nil, fmt.Errorf("unknown hash type prefix: %q", sigParts[0])
	}

	buf, err := hex.DecodeString(sigParts[1])
	if err != nil {
		return nil, nil, fmt.Errorf("error decoding signature %q: %v", signature, err)
	}
	return buf, hashFunc, nil
}

Build Tags

Build Tag Syntax Build Tag Sample Boolean Statement
Space-separated elements // +build pro enterprise pro OR enterprise
Comma-separated elements // +build pro,enterprise pro AND enterprise
Exclamation point elements // +build !pro NOT pro
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment