Open-Source Go Libraries
spacemonkey.com
spacemonkey.com
This doesn't come up that much, because most production-scale deployments terminate TLS at a cluster of load balancers, so you don't really get to choose which TLS library you use in prod. But if you have the option, I get warmer fuzzies from crypto/tls than from native bindings to the scariest library in cryptography.
This could change in a year or so when the forks settle out, but I hope not, because crypto/tls is a very nice piece of code.
Too bad it took such a horrible bug to shake everyone into auditing these libraries.
Edit: fwiw, the hw acceleration is a pretty big deal for us. We have little ARM devices.
Regardless, if I was stuck on hardware acceleration, I'd probably stick a reverse proxy in front of my Golang code before I incorporated an OpenSSL Golang binding.
Good advice.
[1] https://www-staging.spacemonkey.com/blog/posts/go-space-monk...
I just want to point out - I would hesitate from drawing too many conclusions from "net" and "os" (the latter in particular). Not only is "os" some of the oldest Go code, but it is also designed to wrap conveniently around external interfaces that follow very different idioms.
Another example would be the constant declarations[0], which use ALL_CAPS - this is actually not idiomatic in Go, but it is used here (a) because it was done very early on, and (b) because it more closely matches the C libraries it interfaces with[1].
I need to take a closer look at this errors package before drawing an opinion about the rest of it, but this sentence caught my attention.
> It doesn't take long during Go development before you may find yourself wondering where an error came from. In other languages, as soon as an error is raised, a stack trace is captured and is displayed as part of the language's error handling. Go error types are simply basic values, and no such magic happens to tell you what line or what stack an error came from.
I was thinking the other day that Go's "if err != nil { return err }" is just the explicit version of what's done implicitly with exceptions, except the 'err' doesn't remember where it came from. This lack of information seems like a pretty big inconvenience. How do Go developers typically deal with this problem?
Idiomatic Go is not so deeply layered as some Java stacks I've seen. It is more horizontal than vertical. If you call a function in somebody else's library and get an error back, the source of the error is probably not ten layers further down.
pc, _, line, _ := runtime.Caller(1)
name := runtime.FuncForPC(pc).Name()
Not that you need this much, but it is there if you need to return information on exactly where an error came from when logged, just put that in your logging function and whenever you log it'll trace where the log was called from, or you could store the info in your error object if you wish. log4go has some examples of this sort of thing I think. Typically I've only used that when there was an intractable bug though, normally you're not going to need it if your error objects are specific enough, it's easy to find where they came from and store whatever info you want in them, which I assume is what they're doing here.When I find some time I'll pull out my code and start using either errgo or the SpaceMonkey version.
func drinkOrangeJuice() error {
if !enoughCoffee {
return errors.New("requires enough coffee before orange juice")
}
}
func eatBreakfast() error {
err := drinkOrangeJuice()
if err != nil {
return fmt.Errorf("eating breakfast, %v")
}
return nil
}
func prepareForDay() {
if err := eatBreakfast(); err != nil {
log.Printf("Couldn't prepare for the day: %v", err)
}
}
// Outputs:
// Couldn't prepare for the day: eating breakfast, requires enough
// coffee before orange juice.
Most of the time, errors shouldn't bubble up very high, so these strings remain readable.Supplemented with simple error types:
type ErrTooMany error
func take(n int) error {
if n > 9000 {
return ErrTooMany(fmt.Errorf("%n is over 9000"))
}
}
func main() {
err := take(wayTooMany)
switch err.(type) {
case ErrTooMany:
log.Printf("Was too greedy, %v", err)
case nil:
default:
log.Printf("Dunno what happened: %v", err)
}
}
That's all you'll need until very long. I'm pretty sure that's all you need at all. I've been back and forth, and the simplest always win.If you are surfacing errors to an end user then you'll need to ensure that the error message is localised.
If your Go code is merely an API to a front-end, then you'll want to return error codes rather than a textual string. The string your errors are producing are for developers to use and not end users, and to localise them you'll want to return error codes will enable the front end to select and choose the right message to display to a user depending on their locale.
If you're a HTTP API, immediately you'll think, "Ah, HTTP codes suffice, I'll return 'HTTP 404 no coffee' or 'HTTP 428 not enough coffee'" but you'll notice straight away that neither code fitted the scenario of "not enough coffee" well. Additionally, if you tell a client that HTTP 404 means "not enough coffee" you're stuck when you want to explain that they can't have jam on toast without actually having toast.
Yes HTTP codes help to tell HTTP clients what type of error happened, but fails to tell a client which message should be shown to help the end user resolve the problem... besides, our error string isn't localised (and shouldn't be, perhaps someone implemented a Welsh client and we do not know Welsh) so would the end user understand the message? We should presume not.
We're already at the point where the correct thing to do would be to return a detailed error code along the lines of Twilio's: http://www.twilio.com/docs/errors/reference or Oracle's: http://www.ora-code.com/
With an error code we provide the ability for a user client to make a simple lookup to localised and actionable messages about an error, perhaps with additional information and detail to a developer too.
Error codes help us avoid string matching too, the strings may change in the message but the actual error remains the same.
Now it becomes obvious that Go's errors are too primitive. They may be adequate for telling a developer something happened, and with logging they can tell a developer where it happened... but they are woeful at communicating with a user to help correct an error.
Error libs need both a message, and an error code (which isn't a HTTP code).
Stack traces are nice, but I find logging is better (and log.go can tell you which file and line the message was logged from) and HTTP codes are not adequate error codes, an application should have it's own table of codes.
PS: Interesting to note that Go does have error codes and a set of constants for a large number of errors http://golang.org/pkg/syscall/#pkg-constants but it hasn't made them first class by having godoc document errors a package has, encouraging everyone to create and use error codes, etc.
Moreover, since you can bind arbitrary values to error types with our error library, internationalization is easy. Instead of binding a string user message, you can bind a locale->string map user message
Go uses AES-NI instructions on amd64 when available http://tip.golang.org/src/pkg/crypto/aes/asm_amd64.s
> Go TLS does not have wide support for backwards compatibility with less secure and older protocol versions (a good thing, until you have no other option).
LibreSSL people beg to differ about this "feature".
> Go TLS does not give much control over the certificate validation process, which makes it difficult or impossible to add additional checks and validations.
What exactly is this about?
> Oh, and Go TLS is vulnerable to timing attacks.
A mitigation is in WIP https://codereview.appspot.com/94850043
>> Go TLS does not have wide support for backwards compatibility with less secure and older protocol versions (a good thing, until you have no other option).
> LibreSSL people beg to differ about this "feature".
Agreed agreed! Don't use old protocols!
>> Go TLS does not give much control over the certificate validation process, which makes it difficult or impossible to add additional checks and validations.
> What exactly is this about?
OpenSSL allows you to specify your own validation callback, to add additional checks to the certificate validation step. The only certificate validation option Go's TLS gives you is either InsecureSkipVerify or not.
>> Oh, and Go TLS is vulnerable to timing attacks.
> A mitigation is in WIP https://codereview.appspot.com/94850043
awesome!
I'm afraid this falls into scope of the low-priority issue 4299 then: https://code.google.com/p/go/issues/detail?id=4299
> OpenSSL allows you to specify your own validation callback, to add additional checks to the certificate validation step. The only certificate validation option Go's TLS gives you is either InsecureSkipVerify or not.
I'm not sure about the necessity or the details of the particular use case, but if it is general enough, you can file an issue.
As for adding options to Go TLS, it is worth filing an issue for sure. Go 1 compatibility guarantee (and resulting standard library interface freeze) definitely restricts options here, but yeah I'll look into it.
I would also love a flexible logging package (that isn't glog ;).