https://bz.apache.org/bugzilla/show_bug.cgi?id=69098
Bug ID: 69098
Summary: httpd fails to process request with mod_rewrite prg
maps when process exit
Product: Apache httpd-2
Version: 2.4.6
Hardware: Other
OS: Linux
Status: NEW
Severity: major
Priority: P2
Component: mod_rewrite
Assignee: [email protected]
Reporter: [email protected]
Target Milestone: ---
Overview:
When mod_rewrite prg process exit for any reason, all attempts to fetch URLS
that require that map will fail with '400 Bad Requests'
Steps To Reproduce
Configure in httpd.conf
RewriteEngine on
RewriteMap foo "prg:head2.sh"
RewriteMap "/x" ${foo:${REQUEST_URI}}"
Short bash script (in the server root): head2.sh. It will simply echo the first
2 line, and then exit, simulating the
#! /bin/bash
read x ; echo "$x" ; read x ; echo "$x"
Execute the below 5 times after start httpd
curl "http://localhost:8080/x/something
Expected Results: Get 5 times: 404 error for /x/something
Actual Results:
2 times 404 error for /x/something
3 times 400 bad request for /x/something, nothing in error log
Additional information:
Current implementaion of "prg" maps will get the mapping program to launched
during server restart, and will be shared by al forked children (by sharing the
created pipe).
While it's clear that map programs are supposed to be efficient, responsive and
persistent - from time to time, they will exit due to various conditions - lack
of resources, bugs, failed connection to external resources, etc.
At this time, the prg process fail - see strace below:
* Requests will continue to send the lookup value thru the pipe,
* Generating a "SIGPIPE" on the write (silently ignored),
* Reading 0 bytes (as the pipe is closed) as response.
* Generate "400 Bad Request" response to the user/client.
Ideas/Improvements:
1. Add minimal logging
This error condition should be logged to the error_log file. Something that map
'foo' - process 'foobar.pl' exit with status code NN. (The error log from the
process is already captured, hopefully, the program itself will provide more
information about the error condition).
2. Minimal error handling: as the server can not longer process any requested
with the failed program. I can think of few approaches. Not sure which one
should be the default.
* Add "heartbeat" check to the main server, where is would send a configurable
ping string to the prg program, and check for configurable test response. If
the the test fail, it will recover with
** Closing current connection
** Reopening the program
** Execute the heartbeat test
** Recycle all children, making the new process available to them.
3. Automatic per-child restart
In some scenario, it OK to have multiple instances of the mapping program - one
per thread. When running in pre-form mode, will be nice to have an option that
the recovery will launch new instances of the program in each children. Will
take little more resources - but in many cases - including my use case - extra
stability worth the minimal cost.
4. Option to run the mapping program in the forked children, launching them on
the first call, instead of pre-launching. This approach is also good when
processing program need more than few milliseconds to process data. This is
might a separate ticket - bundling it in, as it a by-product of 3.
5. I'm not expert in Linux (or windows) - but if there is an easy way to pass
connection from the root server to the sub-processes - might be possible to use
those APIS to tell sub processes to connect to new mapping processes.
STRACE:
{sa_family=AF_INET, sin_port=htons (55408), sin_addr=inet_addr("127.0.0.1")},
[128->16], SOCK_CLOEXEC) = 10 getsockname(10, {sa_family=AF_INET,
sin_port=htons (8080), sin_addr=inet_addr("127.0.0.1")}, [128->16]) ยท fcntl(10,
F_GETFL) = 0x2 (flags O_RDWR)
fcntl(10, F_SETFL, O_RDWR | O_NONBLOCK) = 0
read(10, "GET / HTTP/1.1\r\nUser-Agent: cur"..., 8000) = 79
semop (65547, [{0, -1, SEM_UNDO}], 1) = 0
// Send the lookup value (/x in this case) to the dead process
// Result in 0 bytes being sent + ignored SIGPIPE
writev (8, [fiov_base="/x", iov_len=2}, {iov_base="\n", iov_len=1}], 2) = -1
EPIPE
(Broken pipe) SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=15826,
si_uid=10539}
// Response is 0 bytes - no data available.
read(9,
1)
= 0
semop (65547, [{0, 1, SEM_UNDO}], 1) = 0
= 0
// Return 400 Bad request to the client
writev (10, [fiov_base="HTTP/1.1 400 Bad Request\r\nDate: "..., iov_len=199},
{iov_base="<!DOCTYPE HTML PUBLIC "-//IETF//"..., iov_len=226}], 2) = 425
write(6, "127.0.0.1 [02/Jun/2024:11:31"..., 87) = 87
times({tms_utime=0, tms_stime=0, tms_cutime=0, tms_cstime=0}) = 793672144
= 0
poll([{fd=10, events=POLLIN}], 1, 2000) = 1 ([{fd=10, revents=POLLIN |
POLLHUP}])
shutdown (10, SHUT_WR)
read(10, "", 512)
close(10)
= 0
= 0
--
You are receiving this mail because:
You are the assignee for the bug.
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]