if (!isset($cookie['sess'])
|| (($user = $this->get_user($cookie['sess'], $idx)) == FALSE)) {
- $content = User::stream_fini($transp, $s_a_p->rndstr, TRUE);
+ $content = User::stream_fini($transp, $s_a_p->rndstr, TRUE,
+ $header, $header_out, $enc);
$s_a_p->pendpage_try_addflush($new_socket, 20, $enc, $header_out, $content);
return TRUE;
static function fini($init_string, $base, $blockerr)
{
- return (sprintf('@BEGIN@ %s window.onbeforeunload = null; window.onunload = null; document.location.assign("%sindex.php"); @END@', ($blockerr ? 'xstm.stop(); ' : ''), $base).self::close());
+ /* The message has to be framed as a websocket frame, exactly like
+ every other one: before it went out raw, and a client receiving it
+ would not have known what to do with it. */
+ $tr = new Transport_websocket();
+ return ($tr->chunk(0, sprintf(' %s window.onbeforeunload = null; window.onunload = null; document.location.assign("%sindex.php"); ',
+ ($blockerr ? 'xstm.stop(); ' : ''), $base))
+ . self::close());
}
function is_chunked()
}
static function gettype($transp)
{
+ /* websocketsec is the encrypted variant of the same transport and
+ the class is Transport_websocket, which tells the two apart in its
+ constructor (see create() above, which indeed treats them
+ together). Missing from this list, every "websocketsec" fell back
+ on Transport_iframe, that is on an html page: the client that had
+ asked for a websocket upgrade got an html page in reply, which its
+ API cannot even read, and never learned it had to go back to the
+ login. */
+ if ($transp == 'websocketsec') {
+ return "Transport_websocket";
+ }
if ($transp == 'websocket' || $transp == 'xhr' || $transp == 'htmlfile') {
return "Transport_".$transp;
}
return ($ret);
} // function maincheck(...
- public static function stream_fini($transp, $init_string, $is_unrecoverable)
+ /* $header and $header_out are only needed by the websocket transports: see
+ below. Whoever does not pass them gets the previous behaviour. */
+ public static function stream_fini($transp, $init_string, $is_unrecoverable,
+ $header = NULL, &$header_out = NULL, $enc = 'plain')
{
// printf("xXx user::stream_fini\n");
// FIXME: dynamic "Transport_" type
$trans_class = Transport::gettype($transp);
+
+ /* The client speaking websocket asked for an upgrade. Answering with an
+ ordinary http response, its API only sees a failure and cannot read the
+ content: it never learns that the session is not valid, so it does not
+ go back to the login and retries forever (red indicator and a loop of
+ reopenings). The handshake is therefore completed, so that the exit
+ command reaches it as a real message, and right after that the
+ connection is closed. */
+ if (($transp == 'websocket' || $transp == 'websocketsec') && $header !== NULL) {
+ $tr = Transport::create($transp);
+ $tr->init($enc, $header, $header_out, $init_string, self::base_get(), 0);
+ }
+
$body = $trans_class::fini($init_string, self::base_get(), static::blocking_error($is_unrecoverable));
// ELSE IF XHR THEN:
|| ($bri = $s_a_p->app->match_get($table_idx, $table_token)) == NULL
|| (($user = $bri->get_user($cookie['sess'], $idx)) == FALSE)) {
- $content = Bin5_user::stream_fini($transp_type, $s_a_p->rndstr, TRUE);
+ /* it used to be $transp_type, which here is not the
+ requested transport: the right value is $transp, read
+ from the query a few lines above. With the wrong one the
+ client got its farewell in the format of another
+ transport. */
+ $content = Bin5_user::stream_fini($transp, $s_a_p->rndstr, TRUE,
+ $header, $header_out, $enc);
$s_a_p->pendpage_try_addflush($new_socket, 20, $enc, $header_out, $content);
return TRUE;