Commit Graph
100 Commits
Author SHA1 Message Date
Max Romanov 1950d0aee2 Closing app outgoing shared memory file descriptor.
This fixes file descriptor leakage in router.  Shared memory file used to
send data from router to application.  These files are shared among all
processes of same application and router keeps the opened file descriptor since
06017e6e3a5f commit.
2021-03-02 19:33:37 +03:00
Max Romanov 73ac0496fe Fixing warnings on Solaris.
pthread_t on Solaris is an integer type with size not equal to pointer size.
To avoid warnings, type casts to and from pointer needs to be done via
uintptr_t type.

This change originally proposed by Juraj Lutter <juraj@lutter.sk>.
2021-03-02 18:31:03 +03:00
Max Romanov fddde539c9 Fixing NetBSD compatibility.
Instead of PTHREAD_STACK_MIN define, NetBSD requires to get minimum stack
size using sysctl(_SC_THREAD_STACK_MIN).

This change originally proposed by Juraj Lutter <juraj@lutter.sk>.
2021-03-02 18:30:34 +03:00
Max Romanov 75a5dcfc4e Fixing shared app queue unmap size.
Shared app queue takes more memory than port memory.  To unmap all memory pages
correct size need to be specified for munmap() call.  Otherwise 4 Mb memory
leaked on each configured application removal.

The issue was introduced in 1d84b9e4b459.
2021-02-03 23:23:28 +03:00
Max Romanov b1685dbc76 Fixing possible NULL dereference.
For listen socket request reply port can be NULL if Router crashes immediately
after issuing the request.

Found by Coverity (CID 366310).
2021-02-03 23:23:17 +03:00
Max Romanov 8c88537e6e Using shared memory to pass configuration to main process.
This patch is required to remove fragmented messages functionality.
2021-02-03 23:23:06 +03:00
Max Romanov 46a8c98a1a Removing unused mutex from nxt_process_t. 2021-01-28 18:32:12 +03:00
Max Romanov e4e444b827 Router: fixing crash after WebSocket processing.
After WebSocket processing, the application port was released with incorrect
reason ("got request"), unnecessarily decrementing the active request counter.

The assertion was triggered only on application removal; a test was added
for this case.
2021-01-28 17:13:52 +03:00
Max Romanov 3855f1c032 Router: fixing error handling in config request.
The controller process awaits the response from the router for every
configration change request.  This patch adds error reporting for various error
conditions which may happen because of file descriptors or memory shortage.

Lack of a response lead to the controller awaiting the response, thus being
unable to process other client reconfiguration requests that also became stuck.
2021-01-27 17:32:03 +03:00
Max Romanov 9b76505bf7 Router: fixing assertion in shortage of file descriptors.
Each application in router process required fd for a request queue shared
memory.  When the number of file descripts close to the limit, and port sockets
successfully opened, router needs to properly handle the errors.

This patch closes port sockets before destroying port structure to avoid
file descriptors leakage and assertion in debug build.
2021-01-25 13:13:17 +03:00
Max Romanov d65a66f9d8 Libunit: processing single port message.
This partially reverts the optimisation introduced in 1d84b9e4b459 to avoid an
unpredictable block in nxt_unit_process_port_msg().  Under high load, this
function may never return control to its caller, and the external event loop
(in Node.js and Python asyncio) won't be able to process other scheduled
events.

To reproduce the issue, two request processing types are needed: 'fast' and
'furious'.  The 'fast' one simply returns a small response, while the 'furious'
schedules asynchronous calls to external resources.  Thus, if Unit is subjected
to a large amount of 'fast' requests, the 'furious' request processing freezes
until the high load ends.

The issue was found by Wu Jian Ping (@wujjpp) during Node.js stream
implementation discussion and relates to PR #502 on GitHub.
2020-12-29 19:01:24 +03:00
Max Romanov d3d6864bdc Node.js: ServerRequest and ServerResponse compliance to Stream API.
ServerRequest now inherit stream Readable object.  ServerResponse
provides 'writable' property.

Thanks to Wu Jian Ping (@wujjpp).

This closes #274, closes #317 issues and closes #502 PR on GitHub.
2020-12-29 19:00:54 +03:00
Max Romanov b7dba90062 Static: fixing request memory pool leakage in router.
When a static file larger than NXT_HTTP_STATIC_BUF_SIZE (128K) is served, two
buffers are allocated and chained; each retains the whole request memory pool.

Starting from 41331471eee7, the completion handler was called once for a linked
buffer chain, but the second buffer got lost.

This patch improves the completion handler's treatment of static buffers to
handle all linked buffers.
2020-12-23 11:01:36 +03:00
Max Romanov 7b669ed866 Libunit: fixed shared memory waiting.
The nxt_unit_ctx_port_recv() function may return the NXT_UNIT_AGAIN code, in
which case an attempt to reread the message should be made.

The issue was reproduced in load testing with response sizes 16k and up.
In the rare case of a NXT_UNIT_AGAIN result, a buffer of size -1 was processed,
which triggered a 'message too small' alert; after that, the app process was
terminated.
2020-12-18 00:25:28 +03:00
Max Romanov 7389a50835 Limiting app queue notifications count in socket.
Under high load, a queue synchonization issue may occur, starting from the
steady state when an app queue message is dequeued immediately after it has been
enqueued.  In this state, the router always puts the first message in the queue
and is forced to notify the app about a new message in an empty queue using a
socket pair.  On the other hand, the application dequeues and processes the
message without reading the notification from the socket, so the socket buffer
overflows with notifications.

The issue was reproduced during Unit load tests.  After a socket buffer
overflow, the router is unable to notify the app about a new first message.
When another message is enqueued, a notification is not required, so the queue
grows without being read by the app.  As a result, request processing stops.

This patch changes the notification algorithm by counting the notifications in
the pipe instead of getting the number of messages in the queue.
2020-12-18 00:25:27 +03:00
Max Romanov c0449e13f8 Router: fixed crash in OOSM processing.
Multithreaded application may create different shared memory segments in
different threads.  The segments then passed to different router threads.
Because of this multithreading, the order of adding incoming segments is
not determined and there can be situation when some of the incoming segments
are not initialized yet.

This patch simply adds check for NULL to skip non-initialized segments.

Crash reproduced during load tests with high number of simultaneous
connections (1024 and more).
2020-12-17 19:27:44 +03:00
Max Romanov d3796d1fb7 Ruby: fixed crash on thread start.
Ruby threads need to be created with GVL; otherwise, an attempt to access
locked resources may occur, causing a crash.

The issue was occasionally reproduced on Ubuntu 18.04 with Ruby 2.5.1
while running test_ruby_application_threads.
2020-12-07 18:17:25 +00:00
Max Romanov db42527b1b Node.js: avoided use of request struct for debug logging.
This fixes a crash on exit of Node.js application.  The crash reproduced
on Ubuntu 20.10 with Node.js v15.1.0.  Tests 'test_node_websockets_two_clients'
and 'test_node_websockets_7_13_1__7_13_2'.

The reason of the crash is using request struct which was already freed.

The issue was introduced in 5be509fda29e.
2020-12-07 17:56:18 +03:00
Max Romanov 55296e6ff2 Node.js: removing unnecessary warnings.
Warnings changed for debug messages.
2020-11-30 23:30:20 +03:00
Max Romanov 2a381a82a6 Libunit: fixing read buffer leakage.
If shared queue is empty, allocated read buffer should be explicitly
released.

Found by Coverity (CID 363943).
The issue was introduced in f5ba5973a0a3.
2020-11-19 13:49:12 +03:00
Max Romanov 25219a7ece Python: improving ASGI http send message processing. 2020-11-18 22:33:53 +03:00
Max Romanov 66bb41e8bb Libunit: fixing read buffer allocations on exit. 2020-11-18 22:33:53 +03:00
Max Romanov 6c3c83561a Libunit: closing active requests on quit. 2020-11-18 22:33:53 +03:00
Max Romanov 300347a5cf Libunit: making minor tweaks.
Removing unnecessary context operations from shared queue processing loop.
Initializing temporary queues only when required.
2020-11-18 22:33:53 +03:00
Max Romanov 8132e1f700 Go: removing C proxy functions and re-using goroutines. 2020-11-18 22:33:53 +03:00
Max Romanov d26afcb481 Libunit: fixing racing condition in request struct recycling.
The issue occurred under highly concurrent request load in Go applications.
Such applications are multi-threaded but use a single libunit context; any
thread-safe code in the libunit context is only required for Go applications.

As a result of improper request state reset, the recycled request structure was
recovered in the released state, so further operations with this request
resulted in 'response already sent' warnings.  However, the actual response was
never delivered to the router and the client.
2020-11-18 22:33:53 +03:00
Max Romanov 0ec69aa46e Libunit: fixing racing condition for port add / state change.
The issue only occurred in Go applications because "port_send" is overloaded
only in Go.  To reproduce it, send multiple concurrent requests to the
application after it has initialised.  The warning message "[unit] [go] port
NNN:dd not found" is the first visible aspect of the issue; the second and more
valuable one is a closed connection, an error response, or a hanging response to
some requests.

When the application starts, it is unaware of the router's worker thread ports,
so it requests the ports from the router after receiving requests from the
corresponding router worker threads.  When multiple requests are processed
simultaneously, the router port may be required by several requests, so request
processing starts only after the application receives the required port
information.  The port should be added to the Go port repository after its
'ready' flag is updated.  Otherwise, Unit may start processing some requests and
use the port before it is in the repository.

The issue was introduced in changeset 78836321a126.
2020-11-18 22:33:53 +03:00
Max Romanov 8340ca0b9c Libunit: improving logging consistency.
Debug logging depends on macros defined in nxt_auto_config.h.
2020-11-18 22:33:53 +03:00
Max Romanov 567f0a7b30 Tests: fixing tests interrupt in terminal.
KeyboardInterrupt re-raised.
2020-11-16 20:37:01 +03:00
Max Romanov 6d2b60ff3e Tests: making available versions unique. 2020-11-16 20:36:54 +03:00
Max Romanov 896d8e8bfb Fixing multi-buffer body send to application.
Application shared queue only capable to pass one shared memory buffer.
The rest buffers in chain needs to be send directly to application in response
to REQ_HEADERS_AC message.

The issue can be reproduced for configurations where 'body_buffer_size' is
greater than memory segment size (10 Mb).  Requests with body size greater
than 10 Mb are just `stuck` e.g. not passed to application awaiting for more
data from router.

The bug was introduced in 1d84b9e4b459 (v1.19.0).
2020-11-10 22:27:08 +03:00
Max Romanov 5fd2933d2e Python: supporting ASGI legacy protocol.
Introducing manual protocol selection for 'universal' apps and frameworks.
2020-11-10 22:27:08 +03:00
Max Romanov 4ca9ba3408 Java: fixing isolation mounts for Alpine musl.
Thanks to @wujjpp.

This closes #490 PR on GitHub.
2020-11-10 22:27:08 +03:00
Max Romanov a8a9d80f8d Tests: supporting instant app parameters in load(). 2020-11-10 22:27:08 +03:00
Max Romanov 702e7bcc89 Java: fixing ClassGraph deprecated API call.
The issue (deprecated API warning) introduced by ClassGraph upgrade
in ccd5c695b739 commit.
2020-11-06 20:41:56 +03:00
Max Romanov 78599f0d3f Tests: fixing racing condition in ASGI threads test.
ASGI threads read all the requests from the queue before start processing it.
This why test need to wait a little to let the ASGI thread start request
processing and block.  In virtual environment any thread or process may
be delayed and only method to avoid racing is a reasonable sleep increase.
2020-11-06 16:45:03 +03:00
Max Romanov 2220b8258f Ruby: error checking during thread creation.
Application terminates in case of thread creation failure.
2020-11-05 17:02:55 +03:00
Max Romanov fc9a012ceb Tests: added Perl threading tests. 2020-11-05 16:11:01 +03:00
Max Romanov d321d454f9 Perl: request processing in multiple threads.
This closes #486 issue on GitHub.
2020-11-05 16:10:59 +03:00
Max Romanov e17e73edda Tests: added Ruby threading tests. 2020-11-05 12:45:26 +03:00
Max Romanov b6475df79c Ruby: request processing in multiple threads.
This closes #482 issue on GitHub.
2020-11-05 12:45:10 +03:00
Max Romanov 9f8b746e77 Ruby: reusing static constant references to string objects.
This shall save a couple of CPU cycles in request processing.
2020-11-05 12:45:08 +03:00
Max Romanov 28f1eb55e7 Tests: added Java threading tests. 2020-11-05 00:06:13 +03:00
Max Romanov 29db46c52b Java: request processing in multiple threads.
This closes #458 issue on GitHub.
2020-11-05 00:06:10 +03:00
Max Romanov f27953af61 Tests: added Python threading tests. 2020-11-05 00:05:02 +03:00
Max Romanov 8e37b1cbf5 Python: fixing some arguments reference counting. 2020-11-05 00:05:00 +03:00
Max Romanov 8dcb0b9987 Python: request processing in multiple threads.
This closes #459 issue on GitHub.
2020-11-05 00:04:59 +03:00
Max Romanov 4225361f0e Python: introducting macro to simplify minor version check. 2020-11-05 00:04:58 +03:00
Max Romanov 4ba9e1d005 Java: upgrading 3rd-party components. 2020-11-02 21:05:04 +03:00
Max Romanov bbe4b97ca1 Java: supporting jsp-file attribute for servlet.
This closes #487 issue on GitHub.
2020-10-30 17:33:36 +03:00
Max Romanov 6a00bab41e Tests: improving get_application_type() and fixing its name.
This patch also enables multiversion tests running for Java.
2020-10-28 00:01:46 +03:00
Max Romanov f007ad4dcf Added threading to the libunit test app. 2020-10-28 00:01:46 +03:00
Max Romanov 80a8cb835b Preserving the app port write socket.
The socket is required for intercontextual communication in multithreaded apps.
2020-10-28 00:01:46 +03:00
Max Romanov d8cc830ea0 Libunit: waking another context with the RPC_READY message. 2020-10-28 00:01:46 +03:00
Max Romanov 4cb8aeb31a Router: introducing the PORT_ACK message.
The PORT_ACK message is the router's response to the application's NEW_PORT
message.  After receiving PORT_ACK, the application is safe to process requests
using this port.

This message avoids a racing condition when the application starts processing a
request from the shared queue and sends REQ_HEADERS_ACK.  The REQ_HEADERS_ACK
message contains the application port ID as reply_port, which the router uses
to send request data.  When the application creates a new port, it
immediately sends it to the main router thread.  Because the request is
processed outside the main thread, a racing condition can occur between the
receipt of the new port in the main thread and the receipt of REQ_HEADERS_ACK
in the worker router thread where the same port is specified as reply_port.
2020-10-28 00:01:46 +03:00
Max Romanov 131b6a7ffa Libunit: releasing cached read buffers when destroying context. 2020-10-28 00:01:46 +03:00
Max Romanov a5508cec7a Libunit: added a function to discern main and worker contexts. 2020-10-28 00:01:46 +03:00
Max Romanov 28ab1de364 Libunit: gracefully quitting a multicontext application. 2020-10-28 00:01:46 +03:00
Max Romanov ccee391ab2 Router: broadcasting the SHM_ACK message to all process ports. 2020-10-28 00:01:46 +03:00
Max Romanov 735bb2f127 Added error response logging.
Every internal server error response should have a clear description in log.
2020-10-28 00:01:46 +03:00
Max Romanov 38a9027fe5 Router: checking a buffer before accessing its memory fields.
This fixes the router's crash on buildbot; the reason was an unexpected 'last'
response from the application to the router arriving before the response
headers.  The last buffer is not a memory buffer, so the result of accessing
memory fields is unpredictable.

The unexpected 'last' message was caused by an error in libunit; fixed in
fee8fd855a00.
2020-10-28 00:01:46 +03:00
Max Romanov 00561a961f Libunit: protecting the new mmap from being used in another thread.
Until the mmap is received by the router, only the creator thread may use this
mmap, so the "mmap not found" state in the router is avoided.
2020-10-28 00:01:46 +03:00
Max Romanov 779b1131c5 Router: closing app worker's ports. 2020-10-28 00:01:46 +03:00
Max Romanov d8628a43d0 Fixing uninitialized ncpu value on unsupported platforms.
Thanks to @geyslan.

This closes #455 issue on GitHub.
2020-10-14 18:41:31 +03:00
Max Romanov 9dcb7ec4b7 Java: response locale methods implemented.
This closes #479 issue on GitHub.
2020-10-14 16:18:34 +03:00
Max Romanov 703d79042b Removing a meaningless warning message.
Data in the queue and the socket are transmitted independently; special
READ_QUEUE and READ_SOCKET message types are used for synchronization.

The warning was accidentally committed with changeset 1d84b9e4b459.
2020-10-06 19:06:33 +03:00
Max Romanov 481e950b86 Tests: pretty versions output for multi-version tests. 2020-10-05 13:26:35 +03:00
Max Romanov 12f225a43a Tests: added ASGI HTTP applications. 2020-10-01 23:55:43 +03:00
Max Romanov d97e3a3296 Tests: added ASGI WebSocket. 2020-10-01 23:55:35 +03:00
Max Romanov c4c2f90c5b Python: ASGI server introduced.
This closes #461 issue on GitHub.
2020-10-01 23:55:23 +03:00
Max Romanov bbc6d2470a Publishing libunit's malloc() and free() wrappers for apps. 2020-10-01 23:55:10 +03:00
Max Romanov c5cb2432c4 Fixing router connection pool leakage.
The connection's local socket address is allocated from the connection
pool before the request is passed to the application; however, with keep-alive
connections, this field was unconditionally reset by a socket configuration
value that could be NULL.  For the next request, the address was allocated
again from the same connection pool.  Nonetheless, all leaked addresses
were released when the connection was closed.

The issue introduced in changeset 5c7dd85fabd5.
2020-09-30 16:36:57 +03:00
Max Romanov 153e8a8779 Fixing leakage caused by incorrect in_hash flag cleanup.
Large-bodied requests are added to the request hash to be found when the body
arrives.  However, changeset 1d84b9e4b459 introduced a bug: the 'in_hash' flag,
used to remove the request from the hash at request release, was cleared after
the first successful request lookup.  As a result, the entry was never removed.
2020-09-30 01:17:09 +03:00
Max Romanov 61eba6eef1 Wrapping libunit's malloc() and free() calls for logging purposes.
This change aids heap usage analysis in applications.
The alloc and free functions are also required for lvlhash due to the upcoming
threading support, because using main nxt_memalign() and nxt_free() isn't safe
in a multithreaded app environment.  The reason is that these functions may use
thread-local structures which aren't initialized properly in applications.
2020-09-29 22:58:04 +03:00
Max Romanov c721a5378d Fixing request buffer memory leakage in router.
The issue was introduced in changeset 1d84b9e4b459.  The request buffer was
transferred via the shared application queue, but the buffer position and the
'sent' flag were not updated after the buffer had been sent.
2020-09-29 22:57:56 +03:00
Max Romanov c4b000f9cc Supporting HTTP/1.0 keep-alive.
The Apache HTTP server benchmarking tool, ab, issues HTTP/1.0 requests with
the 'Connection: Keep-Alive' header and expects a 'Connection: Keep-Alive'
header in the response.
2020-09-29 22:57:46 +03:00
Max Romanov efe65dee4d Tests: prerequisites check improved by using callable.
This is required for more flexible Python version check since ASGI works
for Python 3.5+.  Version check via 'startswith()' function removed as
not consistent.
2020-09-24 09:47:27 +03:00
Max Romanov 1fc51cf140 Tests: introduced module name configuration.
Also fixed problem with "/" in application name.
2020-09-22 19:53:19 +03:00
Max Romanov dc49c561e2 Tests: improved response receiving while upgrade.
The patch required to process non-101 response.
2020-09-22 12:40:35 +03:00
Max Romanov 767c4cb508 Tests: using dict.get() method with default value.
No functional changes.  Only code readability improved.
2020-09-22 12:40:18 +03:00
Max Romanov 449652afa1 Tests: added multiple headers concatenation test. 2020-09-21 21:18:13 +03:00
Max Romanov af964488ba Tests: changing Python application callable name. 2020-09-21 10:32:14 +03:00
Max Romanov f16ae01b12 Python: app module callable name configuration.
Now it is possible to specify the name of the application callable using
optional parameter 'callable'.  Default value is 'application'.

This closes #290 issue on GitHub.
2020-09-18 13:41:58 +03:00
Max Romanov d5973fb557 Python: changed request headers format in router protocol.
The coming ASGI support requires raw HTTP headers format.  Headers grouping
and upcase code were moved to WSGI module.
2020-09-15 20:12:25 +03:00
Max Romanov 5163551ffe Hardening header names comparation for grouping. 2020-09-15 20:11:48 +03:00
Max Romanov d94dac091f Python: split module initialization from WSGI implementation.
This is required for futher ASGI implementation.
2020-09-14 13:27:02 +03:00
Max Romanov d483aa74e6 Python: source file moved to 'python' sub-directory.
No functional changes.  Get ready for an increase in file number.
2020-09-14 12:07:30 +03:00
Max Romanov bd4ca6a057 Fixing WebSocket frame retain function.
Some of the pointers were not adjusted after frame's memory re-allocation.
Fortunately, this function was not used and the bug has no effect.
2020-09-10 12:16:32 +03:00
Max Romanov 7ffc617ae8 Supporting platforms without sendfile() implementation.
This is a quick and dirty sendfile() replacement.

This closes #452 PR on GitHub.
2020-08-17 12:28:48 +03:00
Max Romanov 4ac7a6f55f Style: changing preprocessor directives.
Using #if directives instead of #ifdef the same way as in other places.
2020-08-17 12:28:40 +03:00
Max Romanov b04b5ce430 Fixing router assertion in result of application prefork error.
Buffer for application prefork request allocated from temp conf mem_pool.
If error response from main process received before buffer completion handler,
temp conf mem_pool destroyed and router may crash in completion handler.

Assertion "src/nxt_buf.c:208 assertion failed: data == b->parent" triggered
when NXT_DEBUG_ALLOC enabled in configure.

This patch disables completion handler and memory allocated for buffer
released with memory pool.
2020-08-13 16:08:38 +03:00
Max Romanov 09685e2b41 Responding with error in case of first process start failure.
After shared application port introducing, request queue in router was
removed and requests may stuck forever waiting for another process start.
2020-08-12 15:25:29 +03:00
Max Romanov 2136eb411c Fixing issues found by static analyzer. 2020-08-12 13:37:49 +03:00
Max Romanov fd2c01c58f Fixing return value initialization. 2020-08-11 21:48:46 +03:00
Max Romanov f147943f63 Style fixes for 2 file descriptors transfer over port.
Two consecutive fd and fd2 fields replaced with array.
2020-08-11 21:48:27 +03:00
Max Romanov acb0cca49d Moving file descriptor blocking to libunit.
The default libunit behavior relies on blocking the recv() call for port file
descriptors, which an application may override if needed.  For external
applications, port file descriptors were toggled to blocking mode before the
exec() call.  If the exec() call failed, descriptor remained blocked, so the
process hanged while trying to read from it.

This patch moves file descriptor mode switch inside libunit.
2020-08-11 21:48:16 +03:00
Max Romanov 8cf522bf2d Wrapping close() call in libunit for logging. 2020-08-11 19:20:36 +03:00
Max Romanov e227fc9e62 Introducing application and port shared memory queues.
The goal is to minimize the number of syscalls needed to deliver a message.
2020-08-11 19:20:34 +03:00
Max Romanov a82cf4ffb6 Circular queues implementations and a test.
- naive circular queue, described in the article "A Scalable, Portable, and
 Memory-Efficient Lock-Free FIFO Queue" by Ruslan Nikolaev:
https://drops.dagstuhl.de/opus/volltexte/2019/11335/pdf/LIPIcs-DISC-2019-28.pdf
- circular queue, proposed by Valentin Bartenev in the "Unit router application
IPC" design draft
2020-08-11 19:20:32 +03:00
Max Romanov a1e9df2aef Port message extended to transfer 2 file descriptors. 2020-08-11 19:20:30 +03:00