about summary refs log tree commit homepage
path: root/lib/yahns/proxy_http_response.rb
DateCommit message (Collapse)
2016-04-27proxy_pass: drop resources immediately on errors
We don't want to wait on GC to reap sockets on errors, generational GC in Ruby is less aggressive about reaping long-lived objects such as long-lived HTTP connections.
2016-04-27proxy_http_response: do not persist upstream on slow clients
For slow clients, we want to be able to drop the connection to the upstream as soon as we are done buffering and not waste resources by leaving it in an :ignore state. We also need to remember the client for the fdmap to prevent shutdowns. Ugh, this is really hard to test locally.
2016-04-27proxy_http_response: cleanup: avoid redundant setting of "alive"
We already check for the truthiness of "alive" in the "if" statement, so re-setting is pointless.
2016-04-23proxy_http_response: fix non-terminated fast responses, too
Without this, non-terminated backends were not properly supported if they gave tiny responses or responded faster than we could stream the response to the client. This is necessary to support fast responses from some non-Rack HTTP/1.0-only backend servers which rely on connection termination to terminate responses. Tested manually with a Perl PSGI application running under "plackup". Unlike Rack, the PSGI spec does not specify whether the PSGI application or PSGI server should handle response termination: git clone https://github.com/plack/psgi-specs.git Follow-up-to: 8c9f33a5396d2 ("workaround non-terminated backends")
2016-04-23proxy_pass: honor wbuf_persist when ending response
If a static file response gets truncated while writing, we set wbuf_persist to false in wbuf_flush of lib/yahns/wbuf_common.rb Thus, we must check wbuf_persist attribute as late as possible before yielding control back to the caller in proxy_response_finish
2016-04-06proxy_http_response: workaround non-terminated backends
Without this, we could only support persistent connections if the backend gives a valid Content-Length or set "Transfer-Encoding: chunked" in the response header. Being good netizens, we want to use persistent connections as much as possible if a remote client supports it; so perform chunking ourselves when our remote clients are HTTP/1.1 and able to decode chunked responses. This is necessary to support some non-Rack HTTP/1.0-only backend servers which rely on connection termination to terminate responses. Tested manually with a Perl PSGI application running under "plackup". Unlike Rack, the PSGI spec does not specify whether the PSGI application or PSGI server should handle response termination: git clone https://github.com/plack/psgi-specs.git
2016-01-02copyright updates for 2016
Using the 'update-copyright' script from gnulib[1]: git ls-files | UPDATE_COPYRIGHT_HOLDER='all contributors' \ UPDATE_COPYRIGHT_USE_INTERVALS=2 \ xargs /path/to/gnulib/build-aux/update-copyright We're also switching to 'GPL-3.0+' as recommended by SPDX to be consistent with our gemspec and other metadata (as opposed to the longer but equivalent "GPLv3 or later"). [1] git://git.savannah.gnu.org/gnulib.git
2016-01-02enable frozen_string_literal for Ruby 2.3+
There are likely yet-to-be-discovered bugs in here. Also, keeping explicit #freeze calls for 2.2 users, since most users have not migrated to 2.3, yet.
2015-10-13proxy_http_response: use frozen string literal optimization
This can reduce the amount of garbage we have by a small amount. Once Ruby 2.3 comes out, we can rely on the "frozen_string_literal: true" directive
2015-06-30reduce constants and optimize for Ruby 2.2+
Ruby (MRI) 2.1 optimizes allocations away on String#freeze with literal strings. Furthermore, Ruby 2.2 optimizes away literal string allocations when they are used as arguments to Hash#[] and Hash#[]= Thus we can avoid expensive constant lookups and cache overhead by taking advantage of advancements in Ruby. Since Ruby 2.2 has been out for 7 months, now; it ought to be safe to introduce minor performance regressions for folks using older Rubies (1.9.3+ remains supported) to benefit folks on the latest Ruby.
2015-06-30generate response status strings dynamically
Rack::Utils::HTTP_STATUS_CODES may be altered by the underlying application, allow changes to that to be reflected in our responses and do not rely on the Unicorn::HttpResponse::CODES hash which will probably go away soon.
2015-05-18proxy_pass: allow filtering or overriding response headers
We shouldn't blindly pass the "Server" tag through, since we may be proxying Apache instances and we don't want to misadvertise, either. IMHO, it is best to say nothing at all to save bandwidth and reduce the potential for attackers in case a vulnerability is discovered in yahns.
2015-05-08proxy_pass: fix race condition due to ensure
When calling proxy_busy_mod_blocked to re-enable a descriptor via epoll, the ensure block is dangerous because the "ensure" clause modifies the object after the ReqRes is injected into epoll. This is extremely dangerous as we give up exclusive access to the object once we call epoll_ctl. This simplifies the code a bit while we're at it.
2015-05-08proxy: ensure GC sees the client socket upon reactivation
Reactivating a client socket after the proxied response is complete requires the object remain visible to the Ruby GC while no thread is accessing it. So we must place the object back into the fdmap to prevent the GC from eating it (and having epoll return an invalid pointer).
2015-04-21proxy_pass: fix race condition due to flawed hijack check
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.
2015-04-08proxy_pass: support backends which rely on EOF to terminate
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.
2015-04-07proxy_pass: send 1.0 requests to upstreams for 1.0 clients
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.
2015-04-07proxy_pass: preliminary support for passing trailers
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.
2015-04-04proxy_pass: more tests for giant headers and truncations
We need to ensure more uncommon cases such as gigantic upstream headers and truncated upstream responses are handled properly and predictably.
2015-04-03proxy_pass: rewrite to be async, using rack.hijack
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).