Date | Commit message (Collapse) |
|
Bad connections or dead upstreams cannot be solved looking at a
backtrace, so avoid polluting logs with them and making other
problems less visible.
|
|
We don't need optimized dispatch methods in cold code, so use
the more space-efficient "nil?" method dispatch to save us one
word per-call site for a rough total of 24 bytes saving.
|
|
We may set binary mode upon open by passing "b" in the mode string,
so avoid the extra method dispatch and bytecode/cache overhead that
entails.
|
|
The entire idea of a one-shot-based design is all the mutual
exclusion is handled by the event dispatch mechanism (epoll or
kqueue) without burdening the user with extra locking. However, the
way the hijack works means we check the Rack env for the
'rack.hijack_io' key which is shared across requests and may
be cleared.
Ideally, this would not be a problem if the Rack dispatch allowed
returning a special value (e.g. ":ignore") instead of the normal
status-headers-body array, much like what the non-standard
"async.callback" API Thin started.
We could also avoid this problem by disallowing our "unhijack-ing"
of the socket but at a significant cost of crippling code
reusability, including that of existing middleware.
Thus, we allocate a new, empty request object here to avoid a TOCTTOU
in the following timeline:
original thread: | another thread
HttpClient#yahns_step |
r = k.app.call(env = @hs.env) # socket hijacked into epoll queue
<thread is scheduled away> | epoll_wait readiness
| ReqRes#yahns_step
| proxy dispatch ...
| proxy_busy_mod_done
************************** DANGER BELOW ********************************
| HttpClient#yahns_step
| # clears env
# sees empty env: |
return :ignore if env.include?('rack.hijack_io') |
In other words, we cannot ever touch the original env seen by the
original thread since it must see the 'rack.hijack_io' value because
both are operating in the same Yahns::HttpClient object. This will
happen regardless of GVL existence.
Avoiding errors like this is absolutely critical to every one-shot
based design.
|
|
Upstreams may shut us down while we're writing a request body,
attempt to forward any responses from the upstream back to the
client which may explain the rejection reason for giant uploads.
|
|
This giant method needs to spew an error response in case uploads
to the upstream fail, ensure the local variable is defined early.
|
|
Not all backends are capable of generating chunked responses
(especially not to HTTP/1.0 clients) nor can they generate
the Content-Length (especially when gzipping), so they'll
close the socket to signal EOF instead.
|
|
Since yahns/proxy_pass is not a drop-in replacement, reinstate
the old, synchronous version to avoid breaking existing setups
which require Rack middleware support.
|
|
kgio_writev returns nil on success instead of the number of bytes
written, so we must manually calculate the number of bytes written
intead :x
This is triggerable when sending giant chunked responses.
|
|
A dumb string comparison will do here, so there's no point
in paying the memory and CPU cost of a regexp match when we
already extracted the suffix from a header key.
|
|
hijack seems incompatible with many middlewares, so return a
wonky response tuplet just in case...
|
|
Instance variable attr methods are cheaper and we can shove the
complexity down to tmpio by allowing it to accept a nil argument
for the temporary directory.
This adds 4 bytes to tmpio but removes over 1K in http_context
on a 32-bit system.
|
|
Literal regexps costs over 400 bytes of memory on x86-64 per-site,
so there's no point in using them to cause bloat at cold call sites
where runtime performance does not matter.
|
|
On one of my ancient, cache-starved home systems, this tends to
cause pathologically bad performance during some of these tests.
|
|
We cannot pass trailers from upstreams to HTTP/1.0 clients without
fully-buffering the response body AND trailers before forwarding the
response header.
Of course, one of the reasons yahns exists is to support lazy
buffering, so fully-buffering up front is wasteful and hurts
latency. So instead, degrade to 1.0 requests to upstreams for
HTTP/1.0 clients, this should prevent upstreams from sending
trailers in the first place.
HTTP/1.0 clients on Rails apps may suffer, but there probably
are not too many HTTP/1.0 clients out there.
|
|
Rack apps may (through a round-about way) send HTTP trailers
to HTTP/1.1 clients, and we need a way to forward those responses
through without losing the trailers.
|
|
We need to ensure more uncommon cases such as gigantic upstream
headers and truncated upstream responses are handled properly
and predictably.
|
|
Pipelining uploads is rare in practice, but they must behave
properly in case some brave soul wants to start doing it.
|
|
Even if a response is screwed, we need to ensure breakage is
predictable.
|
|
We were incorrectly stashing the return value of detach_rbuf!
into the inter-thread buffer buffer which is bound to the client.
|
|
This allows our reverse proxy to avoid having an innefficient 1:1
relationship between threads and upstream connections, reducing
memory usage when there are many upstream connections (possibly to
multiple backend machines).
|
|
This allows us to write chunked response bodies without extra
copying to clients which support streaming.
|
|
We'll be supporting "un-hijacking" a client socket for proxy_pass
and we must preserve state for pipelined requests.
|
|
This should make it easier to track state for asynchronous
proxy_pass buffering.
|
|
This will allow us to write arrays for chunked output without
unnecessary data copies.
|
|
This is no longer a part of the "extras" section
|
|
This is probably the least useful tuning knob and may be removed
in the future; so at least warn users about it.
ref: <CAA2_N1t_UJZO9Vu9vx3NxysCx+okip8_pM+NpwhHrExM+f1e+Q@mail.gmail.com>
|
|
We will support "un-hijacking", so the repeated ep_insert/ep_remove
sequences in the kernel will get expensive and complicated for our
user-land code, too.
|
|
This will rely on rack.hijack in the future to support
asynchronous execution without tying up a thread when waiting
for upstreams. For now, this allows simpler code with fewer
checks and the use of monotonic time on newer versions of Ruby.
|
|
Of course, some users will prefer to bind HTTP application
servers to Unix domain sockets for better isolation and (maybe)
better performance.
|
|
This is slightly more nginx-style behavior and allows simpler
configuration.
|
|
While rack.hijack usage during application dispatch normally
prevents yahns from writing an HTTP response out of the Rack
response array, this was not correctly prevented when the
application emitted a 100-continue response when the client
was was too slow to read the 100-continue response without
triggering response buffering the server.
This bug only affects exceeding rare apps which rely on both
rack.hijack use during app dispatch _and_ emits 100-continue
responses, and even then it only affects slow clients which
refuse to read the 100-continue response sent by yahns without
blocking.
|
|
We probably do not want env["rack.input"] to become unusable
upon hijacking. Only drop the internal reference to it so
it can eventually become garbage-collected, but there's no
point in making env["rack.input"] unreadable.
|
|
No point in bloating our bytecode for single-use variables.
|
|
Some middlewares may attempt to modify the response body in
place, so sharing this is not a good idea. We shouldn't
really care about rare 502 error paths, either.
|
|
Oops, this test bug was introduced in:
commit e413325737f23c5ec27a02246f95077bc1fb038d
("acceptor: close inherited-but-unneeded sockets")
|
|
It was never used.
|
|
This release fixes a bug where previously-configured-but-now-removed
listeners were inherited across USR2 upgrades are not shutdown
immediately in the child.
There are also minor reductions in allocations which can save a few
hundred bytes statically and also whenever write buffering is necessary
for large responses.
Some minor documentation updates improvements in extras, too.
shortlog of changes since 1.5.0:
README: add link to mailing list archives
test_ssl: factor out server SSLContext creation
doc: add design_notes document
reduce File::Stat object allocations
update comments about wbuf_close return values
wbuf: lazily (re)create temporary file
fix compatibility with unicorn.git
skip tests requiring String#b on 1.9.3
use the monotonic clock under Ruby 2.1+
favor Class.new for method-less classes
extras/proxy_pass: save memory in String#split arg
extras/proxy_pass: do not name unused variable
extras/proxy_pass: log exceptions leading to 502
extras/proxy_pass: flesh out upload support + tests
acceptor: close inherited-but-unneeded sockets
See the git repository for more: git clone git://yhbt.net/yahns
|
|
When inheriting sockets from the parent via YAHNS_FD, we must close
sockets ASAP if they are unconfigured in the child. This bug exists
in yahns (and not unicorn) because of the trickier shutdown routine
we do for blocking accept system calls to work reliably with the
threading support in mainline Ruby 2.x. This bug would not exist
in a purely C server using blocking accept, either.
|
|
This module will probably become an official part of yahns
soon, so finally add tests for this module.
|
|
It may be useful for us to track down potential errors in
our code or log when an upstream misbehaves.
|
|
"ruby -w" warns on it.
|
|
This saves over 400 bytes of memory in a cold code path.
|
|
This saves over 400 bytes on x86-64.
|
|
The monotonic clock is immune to time adjustments so it is not
thrown off by misconfigured clocks. Process.clock_gettime also
generates less garbage on 64-bit systems due to the use of Flonum.
|
|
While 1.9.3 support will probably be kept for another year or so,
it's probably not worth supporting non-critical extras/ stuff on
1.9.3.
|
|
Install workarounds for running with unreleased versions of unicorn
for now, as unicorn 5.x will be dropping many needless features.
|
|
We may not need this temporary file if we've flushed everything
out and entered bypass mode.
|
|
The state management has evolved slightly over time,
so update the comments to reflect that.
|
|
Since we only support 1.9.3+, io.stat.size may be simplified
to io.size to reduce allocations of File::Stat objects.
|