Date | Commit message (Collapse) |
|
These are followups to the following two commits:
* commit d16326723d
("proxy_http_response: fix non-terminated fast responses, too")
* commit 8c9f33a539
("proxy_http_response: workaround non-terminated backends")
|
|
We should not infinite loop, oops :x
Also, ensure 'yahns' is in the directory in case tests are
SIGKILL-ed and directories are left over.
|
|
We need to ensure SERVER_PORT is still parsed from the Host:
header when it is given, there.
|
|
This helps Rack::Request#url and similar methods generate proper
URLs instead of the obviously wrong: "https://example.com:80/"
Note: we don't track the actual port the listener is bound to,
and it may not be worth it since the use of the Host: header
is long-established and Host: headers include the port number
if non-standard.
|
|
The "threads:" option for the "listen" directive is worthless.
Having a dedicated thread per-process is already more than enough
(and ideal) for a multi-process setup. Multiple acceptor threads
is still wrong for a single-process setup (even if we did not
have a GVL) as it still incurs contention with the worker
pool within the kernel.
So remove the documentation regarding "listen ... threads: ",
for now; at least until somebody can prove it's useful and not
taking up space.
Additionally, "atfork_parent" may be useful for restarting
background threads/connections if somebody wants to run
background jobs in the master process, so stop saying
it's completely useless.
|
|
env['HTTPS'] is not documented in rack SPEC, but appears to be
used by Rack::Request since 2010[*]. Also, set rack.url_scheme
as documented by rack SPEC.
[*] - commit 4defbe5d7c07b3ba721ff34a8ff59fde480a4a9f
("Improves performance by lazy loading the session.")
|
|
We cannot use the sendfile(2) syscall when serving static files
to TLS clients without breaking them. We currently rely on
OpenSSL to encrypt the data before it hits the socket, so it
must be read into userspace buffers before being written to the
socket.
|
|
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
|
|
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.
|
|
We need to ensure the master process is done writing to logs
when we check them. Do that by sending a SIGQUIT to the master
to shut it down, as the master process will defer the SIGQUIT
until after the test log is written to the kernel.
|
|
Linux 4.2 gained the ability to do true zero-copy sendfile support
for Unix sockets; so buffer space is accounted differently.
Previously Linux only avoided copies in userspace when doing
sendfile for Unix sockets, not internally within the kernel.
This kernel change has no bearing on normal code which would need to
account for concurrent draining by the client; only test code
designed to create a failure condition.
|
|
Future updates may use the update-copyright script in gnulib:
git ls-files | UPDATE_COPYRIGHT_HOLDER='all contributors' \
UPDATE_COPYRIGHT_USE_INTERVALS=2 \
xargs /path/to/gnulib/build-aux/update-copyright
|
|
Process.spawn is faster under Linux since it may use vfork
to avoid marking pages copy-on-write.
|
|
Getting the logs to show up in order is tricky in a multithreaded
server...
|
|
systemd socket emulation shares FDs across execve, just like
the built-in SIGUSR2 upgrade process in unicorn. Thus it is
easy to support inheriting sockets from systemd.
|
|
We need to wait for the hijacked process to run and write to the
log before we check it. Since we may not have inotify, try to
trigger a sched_yield() syscall instead.
|
|
TCP socket options are now set when inheriting existing sockets from
a parent process. I'm fairly certain all the TCP setsockopt knobs
we use are idempotent and harmless to change.
If anything, the only directive I'd be uncomfortable changing is
shortening the listen(2) (aka :backlog) size, but we've always
changed that anyways since it also applies to UNIX sockets.
Note: removing a configuration knob in a yahns config file can not
reset the value to the OS-provided default setting. Inherited
sockets must use a new setting to override existing ones.
(or the socket needs to be closed and re-created in the process
launcher before yahns inherits it).
Based on unicorn commit 1db9a8243d42cc86d5ca4901bceb305061d0d212
Noticed-by: Christos Trochalakis <yatiohi@ideopolis.gr>
<20150626114129.GA25883@luke.ws.skroutz.gr>
|
|
We want to avoid race conditions if tests become multithreaded
from Kernel#warn internally issuing a second write.
|
|
Middlewares such as Rack::Lock (used by Rails) break badly unless
the response body is closed on hijack, so we will close it to follow
the lead of other popular Rack servers.
While it's unclear if there's anybody using rack.hijack besides
yahns/proxy_pass we'll try to emulate the behavior of other servers
as much as possible.
ref: https://github.com/ngauthier/tubesock/issues/10
While we're at it, use DieIfUsed correctly in test_ssl.rb :x
|
|
We don't require kcar to be installed since most users will never
use the proxy_pass feature, but show an informative error in case
they want to test this functionality.
|
|
Rack::TempfileReaper was added in rack 1.6 to cleanup temporary
files. Make Yahns::TmpIO ducktype-compatible and put it into
env['rack.tempfiles'] array so Rack::TempfileReaper may be used to
free up space used by temporary buffer files.
ref: commit 3bdf5481e49d76b4502c51e5bdd93f68bfd1f0b4 in unicorn
|
|
We do not want rack.hijack users relying on kgio_* methods since
kgio is trying to make itself obsolete (as Ruby itself adopts
kgio features). This is a bit wonky since our common case tries
to minimize object allocation by only using the Kgio::Socket
derived class.
|
|
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.
|
|
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.
|
|
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 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 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.
|
|
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.
|
|
Oops, this test bug was introduced in:
commit e413325737f23c5ec27a02246f95077bc1fb038d
("acceptor: close inherited-but-unneeded sockets")
|
|
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.
|
|
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.
|
|
Since we only support 1.9.3+, io.stat.size may be simplified
to io.size to reduce allocations of File::Stat objects.
|
|
We'll be writing more SSL-related tests in the future...
|
|
If we're streaming large files and sendfile fails (due to a
client aborting the connection), we need to ensure middleware
proxies are closed to ensure proper logging of a partial request.
This affects users of the "clogger" gem serving static files.
Unfortunately with clogger (or any Rack API-compliant middleware
using "to_path"), we still cannot log the amount of bytes
transferred for a static file.
|
|
We rely on exception-free non-blocking I/O for performance,
so it is easier for us to avoid supporting new features on
old Rubies.
|