}
}
+/*
+ * "Direct http" mode: nginx (or any reverse proxy) opens an ordinary http
+ * connection on the unix socket instead of handing over the client file
+ * descriptor. The head of the request therefore comes from the network, and
+ * must be read without ever blocking the event loop: the loop itself takes
+ * care of it, with a pending page in the PENDINGPAGE_WAITHEAD state. These
+ * two functions are its two steps.
+ */
+
+/* Offset right after the empty line that closes the head, or FALSE if it
+ has not arrived in full yet. */
+function spu_head_end($buf)
+{
+ if (($pos = strpos($buf, "\r\n\r\n")) !== FALSE) {
+ return ($pos + 4);
+ }
+ if (($pos = strpos($buf, "\n\n")) !== FALSE) {
+ return ($pos + 2);
+ }
+ return (FALSE);
+}
+
+/* Repackages the http request in the same format the control channel of
+ mod_proxy_fdpass2 used to produce:
+
+ The-Request:<request line>\n <header>\n ... \n\n <body, if any>
+
+ so that spu_process_info() need not know where the data came from. The
+ request line has no colon, and without the label the parser would not
+ recognise it. */
+function spu_head_to_info($buf, $sep)
+{
+ $body = mb_substr($buf, $sep, NULL, "ASCII");
+ $headpart = mb_substr($buf, 0, $sep, "ASCII");
+ $lines = preg_split("/\r?\n/", rtrim($headpart, "\r\n"));
+ $lines[0] = "The-Request:" . $lines[0];
+
+ return (implode("\n", $lines) . "\n\n" . $body);
+}
+
function spu_process_info($stream_info, &$method, &$header, &$get, &$post, &$cookie, &$rest, &$cont)
{
$check_post = FALSE;
continue;
}
$split = explode(":", $line, 2);
+ if (!isset($split[1])) {
+ continue;
+ }
$hea_id = trim(mb_convert_case($split[0], MB_CASE_TITLE, 'UTF-8'));
- $header[$hea_id] = $split[1];
+ /* the value needs trimming: in the control channel dump it arrives
+ as "Name:value", but in a real http request it is "Name: value"
+ and the space would end up inside the value, breaking the cookie
+ parsing and the comparisons on Upgrade. */
+ $header[$hea_id] = trim($split[1]);
}
return $path;
}
$cookie = array();
$rest = 0;
$cont = "";
- if (($new_socket = ancillary_getstream($new_unix, $stream_info)) !== FALSE) {
+ /* Two ways of getting the client connection:
+ - direct http: nginx opens an ordinary http connection on the unix
+ socket, and the accepted one is already the one to use
+ - descriptor handover: the frontend passes the client socket with
+ SCM_RIGHTS plus a channel carrying the headers (needs the
+ ancillary extension and, without a cleartext layer in front,
+ kTLS) */
+ if (defined('SPU_HTTP_DIRECT') && SPU_HTTP_DIRECT) {
+ /* The head of the request has not arrived yet and cannot be
+ waited for here: there is a single event loop for every
+ player, and a blocking read would stop them all. The
+ connection is added to the watched ones in the WAITHEAD
+ state; the loop will complete it when select() reports it
+ readable. */
+ stream_set_blocking($new_unix, 0);
+ $this->pendpage_add(
+ PendingPage::pendingpage_waithead($new_unix, $this->curtime, 20));
+ continue;
+ }
+ $new_socket = ancillary_getstream($new_unix, $stream_info);
+ if ($new_socket !== FALSE) {
// printf("NEW_SOCKET: %d\n", intval($new_socket));
stream_set_blocking($new_socket, $this->blocking_mode); // Set the stream to non-blocking
// error_log(sprintf("RECEIVED HEADER:\n%s", $stream_info));
fprintf(STDERR, "User associated with ID: %s not found\n", $id);
}
- if (isset($this->s2p[$id])) {
+ /* direct http mode: the head of the request arrives in
+ pieces like any other data, and here it is
+ accumulated without ever blocking */
+ if (isset($this->s2p[$id])
+ && $this->s2p[$id]->status == PENDINGPAGE_WAITHEAD) {
+ $pp = $this->s2p[$id];
+ $pp->cont .= $buf;
+ $sep = spu_head_end($pp->cont);
+ if ($sep === FALSE) {
+ if (mb_strlen($pp->cont, "ASCII") > 32768) {
+ $this->pendpage_rem($pp);
+ fclose($sock);
+ $this->socks_unset($sock);
+ }
+ continue; /* testa incompleta: si riprende al prossimo giro */
+ }
+ $stream_info = spu_head_to_info($pp->cont, $sep);
+ $new_socket = $pp->socket_get();
+ $this->pendpage_rem($pp);
+
+ $method = ""; $get = array(); $post = array();
+ $cookie = array(); $rest = 0; $cont = "";
+ if (($path = spu_process_info($stream_info, $method, $header,
+ $get, $post, $cookie, $rest, $cont)) == FALSE) {
+ fclose($new_socket);
+ $this->socks_unset($new_socket);
+ continue;
+ }
+ $addr = (array_key_exists('X-Real-Ip', $header) ? $header['X-Real-Ip']
+ : addrtoipv4(stream_socket_get_name($new_socket, TRUE)));
+ $addr = $this->pproxy_realip($header, $addr);
+
+ if ($method == "POST" && $rest > 0) {
+ /* the body is still missing: the machinery already in
+ place for partial POSTs takes over */
+ $this->pendpage_try_addwait($new_socket, 20, $method, $header,
+ $get, $post, $cookie, $path, $addr,
+ $rest, $cont);
+ continue;
+ }
+ $manage_page = TRUE;
+ }
+ else if (isset($this->s2p[$id])) {
$this->s2p[$id]->rest -= mb_strlen($buf, "ASCII");
$this->s2p[$id]->cont .= $buf;
if ($this->s2p[$id]->rest <= 0) {
$HTTP_HOST="dodo.birds.lan";
define('USOCK_PATH_PFX', "/tmp/brisk");
define('USOCK_POOL_N', 10);
+
+/* TRUE = nginx speaks http directly on the unix socket (no ancillary
+ extension, no descriptor handover modules, no kTLS)
+ FALSE = historic behaviour: the frontend hands over the client descriptor */
+define('SPU_HTTP_DIRECT', FALSE);
define('SOCK_SHARD_N', 2);
define('PENDINGPAGE_CONTINUE', 0);
define('PENDINGPAGE_WAITDATA', 1);
define('PENDINGPAGE_FLUSH', 2);
+/* waiting for the head of the http request: used only in SPU_HTTP_DIRECT
+ mode, where the daemon has to read it from the network and cannot do so by
+ blocking the event loop, which serves every player */
+define('PENDINGPAGE_WAITHEAD', 3);
class PendingPage {
public $socket; // socket handler of page stream
$this->msg_sz = $hea_sz;
}
+ /* page waiting for the head: nothing is known about the request yet,
+ bytes are accumulated in ->cont until the empty line arrives */
+ static function pendingpage_waithead($socket, $curtime, $kalive)
+ {
+ $thiz = new PendingPage($socket, $curtime, $kalive);
+ $thiz->status = PENDINGPAGE_WAITHEAD;
+ $thiz->cont = "";
+ $thiz->rest = 0;
+ return ($thiz);
+ }
+
static function pendingpage_waiting($socket, $curtime, $kalive, $method, $header,
$get, $post, $cookie, $path, $addr, $rest, $cont)
{