php/f02FetchServer.php
<?php
require_once('env.php');
if (isset($_GET['sleep'])) {
if (! is_numeric($t = $_GET['sleep'])) {
echo "sleep $t not an integer";
} else {
$b = date('c');
sleep($t);
echo "<br>$b<br>sleep $t<br>" . date('c');
}
} else {
echo 'bad inpuT $_GET ' . a2str($_GET);
}
exit;
outBegin(__file__);
$dbg=9;
$hostnm = gethostname();
out('start at ' . date('c') . ', pid ' . getmypid() . ", host $hostnm, php " . phpversion());
$prot = $hostnm === 'wk13' ? 'ws' : 'wss' . err("security policy for websocket ports on host $hostnm?????") ;
$host = $hostnm === 'wk13' ? "192.168.1.135" : 'wlkl.ch';
$port = 4321;
function sErr($t) {
err($t . ' ' . ($c = socket_last_error()) . ' => ' . socket_strerror($c));
}
function sSend($s, $d) { # send data to socket s
if (false === $r = socket_send($s, $d, strlen($d), 0))
sErr("socket_send $d");
else
out("senttt $r bytes data", preg_replace('/[[:^print:]]/', '?', $d));
}
function wsDecode($e, &$t) { # decode websocket message e into clear text t, return opcode, according to rfc6455 https://www.rfc-editor.org/rfc/rfc6455.html
($fin = ord($e[0]) >> 7) || err("implement continuation wsFin $fin");
$op = ord($e[0]) & 0x0f;
if(0x7e > $len = ord($e[1]) & 0x7f) {
$o = 2;
} elseif ($len === 0x7e) { # 16bit
$o = 4;
$len = unpack('n', $e, 2)[1];
} else { # $len === 0x7f 64bit
$o = 10;
$len = unpack('J', $e, 2)[1];
}
if (! ($isMskd = ord($e[1]) >> 7)) {
$t = $s = substr($e, 2);
} else {
(strlen($e) >= $o+4) || err("wsDecode to short for mask " . bin2hex($e));
$msk = substr($e, $o, 4);
$s = substr($e, $o+4);
}
if ($len != strlen($s))
err("strlen " . strlen($s) . " <> len $len");
if ($isMskd) { # decode with mask
$t = $s ^ str_repeat($msk, (3+strlen($s)) >> 2);
}
dbg1( "wsDecode: fin $fin, opcode $op"
. ($isMskd ? (", mask " . bin2hex($msk)) : ', not masked') . ", len $len"
. " decoded $t from " . bin2hex(substr($e, 0, 20)));
return $op;
}
function wsEncode($op, $t) { # encode text t with opCode op and return encoded data, according to rfc6455 https://www.rfc-editor.org/rfc/rfc6455.html
(($op & ~ 0xf) === 0) || err("encode bad op $op");
$r = chr(0x80 | $op);
$r .= (($l = strlen($t)) < 0x7e) ? chr($l) : ($l <= 0xffff ? "\x7e" . pack('n', $l) : "\x7f" . pack('J', $l));
return $r . $t;
}
function wsSend($c, $m) { # send in websocket protocoll
sSend($c, wsEncode(1, $m));
}
function sSendHTTP($s, $ii, $m) {
out($m = "<html><body>$m <ul><li>$ii</li><li>at " . date('c') . "</ul></li>");
/* $hm = "HTTP/1.1 200 OK\nDate: Mon, 27 Jul 2009 12:28:53 GMT\nServer: Apache/2.2.14 (Win32)\nLast-Modified: Wed, 22 Jul 2009 19:15:56 GMT"
. "\nContent-Length: " . strlen($m) ."\nContent-Type: text/html\nConnection: Closed\n\n$m"; */
sSend($s, "HTTP/1.1 200 OK\nContent-Length: " . strlen($m) ."\nContent-Type: text/html\nConnection: keep-alive\n\n$m");
}
dbg1("Creating master socket...");
if (false === $sockSrv = socket_create(AF_INET, SOCK_STREAM, SOL_TCP))
sErr('socket_create');
$max_clients = 10;
dbg1("Setting socket options...");
socket_set_option($sockSrv, SOL_SOCKET, SO_REUSEADDR, 1) || sErr('socket_set_option');
dbg1("Binding socket host=$host, port=$port...");
socket_bind($sockSrv, $host, $port) || sErr('socket_bind');
dbg1("Listening...");
socket_listen($sockSrv, $max_clients) || sErr('socket_listen');
$sckts = [$sockSrv];
$sckII = ['sockSrv'];
dbg1('sckts', count($sckts), $sckts);
$nn = null;
while(TRUE) {
dbg1("while ... select sckts " . count($sckts), $sckts);
$reaS = $sckts;
(false !== $ready = socket_select($reaS, $nn, $nn, null)) || sErr("socket_select ready=$ready");
dbg1("socket_select returned " . $ready);
foreach ($reaS as $k => $s) {
if ($s !== ($sckts[$k] ?? '?none?')) {
err("key changed $k => $s but sockets", $sckts);
$kO = $k;
(false === ($k = $array_search($s, $sckts, true))) && err('selected socket', $s, 'not in', $sckts);
}
if ($s === $sockSrv) {
(false !== $c = socket_accept($sockSrv)) || sErr('socket_accept');
(false === (array_search($c, $sckts, true))) || err("accept", $c, 'already in sckts', $sckts);
$sckts[$nx=count($sckts)] = $c;
socket_getsockname($c, $cSH, $cSP) || sErr('socket_getsockname');
socket_getpeername($c, $cPH, $cPP) || sErr('socket_getpeername');
$sckII[$nx] = "socket from server $cSH:$cSP to peer $cPH:$cPP";
if ($prot === 'http') {
sSendHTTP($s, $sckII[$nx], 'accept connect');
} elseif ($prot === 'ws') {
(false === $handsh = socket_read($c, 5000)) && sErr('socket_read handshake');
dbg1("handshake read $handsh");
dbg1('match', preg_match('#Upgrade: +([^\r\n]*)#', $handsh, $matches), $matches);
dbg1('match', preg_match('#Sec-WebSocket-Key: +([^\r\n]*)#', $handsh, $matches), $matches);
dbg1("handshake sec key ". bin2hex($matches[1]) ." <{$matches[1]}>");
$sah1 = sha1($matches[1] . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
$rKey = base64_encode(pack('H*', $sah1));
# sSend($c, "HTTP/1.1 101 Switching Protocols\nUpgrade: websocket\nConnection: Upgrade\nSec-WebSocket-Accept: $rKey\n\n");
sSend($c, "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
"Upgrade: websocket\r\n" .
"Connection: Upgrade\r\n" .
"WebSocket-Origin: $host\r\n" .
"WebSocket-Location: ws://$host:$port\r\n". # /deamon.php
"Sec-WebSocket-Accept:$rKey\r\n\r\n");
wsSend($c, "ws connected 1 to {$sckII[$nx]}");
} else err("prot $prot");
} else {
$buf = ' ';
socket_clear_error();
if (false === $buf = @socket_read($s, 2048)) {
if (SOCKET_ENOTCONN === $e = socket_last_error()) {
dbg1('recv buf', $buf);
out("endpoint not connected $sckII[$k] ===>" . ' => ' . socket_strerror($e));
socket_close($s);
$sckts[$k] = $sckts[count($sckts) -1];
$sckII[$k] = $sckII[count($sckts) -1];
array_pop($sckts);
array_pop($sckII);
} else {
sErr('socket_recv');
}
} else {
out("server received from client " . strlen($buf) . ' ' . bin2hex(substr($buf, 0, 10)), preg_replace('/[[:^print:]]/', '', $buf));
$op = wsDecode($buf, $txt);
if ($op === 1) { # text message
wsSend($s, "server received text from client $sckII[$k]: $txt");
} elseif ($op === 8) { # close handshake
sSend($s, "\x88\x00"); # send back close handshake
socket_close($s);
dbg1("closed $sckII[$k] after closeHandshake");
$sckts[$k] = $sckts[count($sckts) -1];
$sckII[$k] = $sckII[count($sckts) -1];
array_pop($sckts);
array_pop($sckII);
} else {
err("implement websocket opcode $op");
}
}
}
} #
} #while
outEnd(__file__);