This is meant to accomplish a few things:
1. graceful no longer spawns an additional goroutine per connection.
Instead, it maintains a sharded set of idle connections that a single
reaper goroutine can go through when necessary.
2. graceful's connection struct has a more orthogonal set of connection
state flags, replacing the harder-to-understand state machine. The
underlying mechanics are largely the same, however.
3. graceful now uses the Go 1.3 ConnState API to avoid the "200-year
SetReadDeadline hack." It still falls back on SetReadDeadline on Go
1.2 or where ConnState does not apply.
This feature can be used in place of the pile of hacks in middleware.go,
and doesn't involve awkwardly shimming out a http.ResponseWriter. Sounds
like a win-win!
It turns out WriterProxy is pretty generally useful, especially when
defining custom http loggers. Expose it in a util package so that other
packages can use it.
If you're manipulating your middleware stack concurrently with active
requests you're probably doing something wrong, and it's not worth
either the complexity or runtime cost to support you hitting yourself.
We can probably take this principle a bit further and disallow mutating
the middleware stack after any requests have been made (which will
eliminate even more complexity) but that can be a project for another
day.
App Engine disallows package unsafe. As a workaround for the (unsafe)
RCU atomic pointer shenanigans we pull in order to avoid taking a lock
in the hot routing path, let's just grab the lock. Honestly, I doubt
anyone will notice anyways, especially considering the fact that App
Engine is single-threaded anyways.
Fixes#52.
Previously, a state machine invalidation could have raced against an
in-flight routing attempt: if the invalidation occured after the routing
attempt had already completed its nil-check (choosing not to compile a
new state machine) but before the state machine was atomically loaded to
perform routing, the routing goroutine would begin to panic from
dereferencing nil.
The meat of this change is that we now return the state machine that we
compiled (while still holding the lock), and we only ever interact with
the state machine through atomic pointer loads.
Expose an additional function, bind.WithFlag(), which allows callers to
use the previously-default "global flag" mode.
This change allows the bind string parser (etc.) to be used without
unwanted side effects. The behavior of the top-level "goji" package has
not been changed.
Fixes#47.
Previously, a set of standard signals would be handled automatically via
an init() function, however that made the package difficult to use in
packages in which an HTTP server would only be spawned some of the times
(perhaps keyed on an environment variable or flag). Now, signals must be
registered manually.
By default, the top-level "goji" package automatically registers
signals with graceful, so this will result in no behavior changes for
most people.
Fixes#35.
Many common panic values, e.g. nil pointer dereferences, don't print
very well under "%#v", emitting something like
"runtime.errorCString{cstr:0x54b2a4}" or similar.
If WriteHeader is called multiple times on a http.ResponseWriter, the
first status is the one that is used, not the last. Fix the wrapped
writer to reflect this fact.
For whatever reason, Go insisted on loading rm.sm[i] in several chunks,
even though it could be loaded in a single 64-bit block. Instead, let's
reorder our loads to minimize the amount of memory we're uselessly
moving around.
This gives us about a 15% perf boost in
github.com/julienschmidt/go-http-routing-benchmark's
BenchmarkGoji_StaticAll, and questionable benefits (i.e., not
distinguishable from noise but certainly no worse) on Goji's own
benchmarks.
This eliminates the race condition mentioned in a54c913a by forbidding
duplicate binds to the same socket (well, at least in the sense that
attempting to do so will *always* result in an error instead of
nondeterministically resulting in an error).
This fixes a race condition between package bind and the garbage
collector, where if the garbage collector ran between einhornInit and
einhornBind, bind would fatal with the error "dup: bad file descriptor"
The core of the bug is that Go's os.File uses runtime.SetFinalizer to
register a callback to close the underlying file descriptor an os.File
points at when the os.File itself is being garbage collected. However,
the Einhorn initialization code in bind, in the process of ensuring that
every Einhorn-passed socket had CloseOnExec set on it, allocated
os.File's pointing at each of these passed file descriptors, but did not
keep references to these os.File's, allowing them to be garbage
collected. Subsequently, if you attempted to bind one of these sockets,
you'd find that it was no longer open.
This is the simplest fix to the bug, which is to only allocate an
os.File when we actually attempt to bind the socket. Note that there's
still a race condition here if you attempt to bind the same file
descriptor twice, since a GC between the two binds will likely cause the
file to be collected. Fortunately, that one can be worked around by
simply not allowing such silly behavior :). Another patch that makes
this more clear will follow.
Closes#29.