Jump to content

Recommended Posts

Posted

Hi again, I'm creating a chat system with PHP. So I've created the WebSocket server in PHP and the client using the built-in browser WebSocket API. Everything is working fine when I access the client using 'http' & 'ws' protocol, but my problem is when I access the client using 'https' & 'wss', I'm getting the error in firefox saying that: "Firefox can’t establish a connection to the server at wss://192.168.1.201:9000/server.php.". The files are located on https://192.168.1.201/socket/chat2/.

 

Here are the codes:

 

 

/socket/chat2/server.php:

<?php
$host = '192.168.1.201'; //host
$port = '9000'; //port
$null = NULL; //null var

//Create TCP/IP sream socket
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false) {
    echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n";
}
//reuseable port
socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);

//bind socket to specified host
socket_bind($socket, 0, $port);

//listen to port
socket_listen($socket);

//create & add listning socket to the list
$clients = array($socket);

//start endless loop, so that our script doesn't stop
while (true) {
	//manage multipal connections
	$changed = $clients;
	//returns the socket resources in $changed array
	socket_select($changed, $null, $null, 0, 10);
	
	//check for new socket
	if (in_array($socket, $changed)) {
		$socket_new = socket_accept($socket); //accpet new socket
		$clients[] = $socket_new; //add socket to client array
		
		$header = socket_read($socket_new, 1024); //read data sent by the socket
		perform_handshaking($header, $socket_new, $host, $port); //perform websocket handshake
		
		socket_getpeername($socket_new, $ip); //get ip address of connected socket
		$response = mask(json_encode(array('type'=>'system', 'message'=>$ip.' connected'))); //prepare json data
		send_message($response); //notify all users about new connection
		
		//make room for new socket
		$found_socket = array_search($socket, $changed);
		unset($changed[$found_socket]);
	}
	
	//loop through all connected sockets
	foreach ($changed as $changed_socket) {	
		
		//check for any incomming data
		while(socket_recv($changed_socket, $buf, 1024, 0) >= 1)
		{
			$received_text = unmask($buf); //unmask data
			$tst_msg = json_decode($received_text, true); //json decode 
			$user_name = $tst_msg['name']; //sender name
			$user_message = $tst_msg['message']; //message text
			$user_color = $tst_msg['color']; //color
			
			//prepare data to be sent to client
			if(!empty($tst_msg['notification']))
				$response_text = array('type'=>'notification', 'name'=>$user_name, 'message'=>$user_message, 'color'=>$user_color);
			else
				$response_text = array('type'=>'usermsg', 'name'=>$user_name, 'message'=>$user_message, 'color'=>$user_color);
			
			$response_text = mask(json_encode($response_text));
			send_message($response_text); //send data
			break 2; //exist this loop
		}
		
		$buf = @socket_read($changed_socket, 1024, PHP_NORMAL_READ);
		if ($buf === false) { // check disconnected client
			// remove client for $clients array
			$found_socket = array_search($changed_socket, $clients);
			socket_getpeername($changed_socket, $ip);
			unset($clients[$found_socket]);
			
			//notify all users about disconnected connection
			$response = mask(json_encode(array('type'=>'system', 'message'=>$ip.' disconnected')));
			send_message($response);
		}
	}
}
// close the listening socket
socket_close($socket);

function send_message($msg)
{
	global $clients;
	foreach($clients as $changed_socket)
	{
		@socket_write($changed_socket,$msg,strlen($msg));
	}
	return true;
}


//Unmask incoming framed message
function unmask($text) {
	$length = ord($text[1]) & 127;
	if($length == 126) {
		$masks = substr($text, 4, 4);
		$data = substr($text, 8);
	}
	elseif($length == 127) {
		$masks = substr($text, 10, 4);
		$data = substr($text, 14);
	}
	else {
		$masks = substr($text, 2, 4);
		$data = substr($text, 6);
	}
	$text = "";
	for ($i = 0; $i < strlen($data); ++$i) {
		$text .= $data[$i] ^ $masks[$i%4];
	}
	return $text;
}

//Encode message for transfer to client.
function mask($text)
{
	$b1 = 0x80 | (0x1 & 0x0f);
	$length = strlen($text);
	
	if($length <= 125)
		$header = pack('CC', $b1, $length);
	elseif($length > 125 && $length < 65536)
		$header = pack('CCn', $b1, 126, $length);
	elseif($length >= 65536)
		$header = pack('CCNN', $b1, 127, $length);
	return $header.$text;
}

//handshake new client.
function perform_handshaking($receved_header,$client_conn, $host, $port)
{
	$headers = array();
	$lines = preg_split("/\r\n/", $receved_header);
	foreach($lines as $line)
	{
		$line = chop($line);
		if(preg_match('/\A(\S+): (.*)\z/', $line, $matches))
		{
			$headers[$matches[1]] = $matches[2];
		}
	}

	$secKey = $headers['Sec-WebSocket-Key'];
	$secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
	//hand shaking header
	$upgrade  = "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: wss://$host:$port/server.php\r\n".
	"Sec-WebSocket-Accept:$secAccept\r\n\r\n";
	socket_write($client_conn,$upgrade,strlen($upgrade));
}
?>

/socket/chat2/client.js:

var colors = [
	'#007AFF','#FF7000','#FF7000','#15E25F','#CFC700','#CFC700','#CF1100','#CF00BE','#F00'
];
var color_pick = Math.floor(Math.random() * colors.length);

//create a new WebSocket object.
var msgBox = $('#message-box');
var wsUri = "wss://192.168.1.201:9000/server.php";
websocket = new WebSocket(wsUri);

websocket.readyStateChange = function (e) { // connection is open 
	console.log(e);
}

websocket.onopen = function (ev) { // connection is open 
	msgBox.append('<div class="system_msg" style="color:#bbbbbb">Welcome to my "Demo WebSocket Chat box"!</div>'); //notify user
}
// Message received from server
websocket.onmessage = function (ev) {
	var response = JSON.parse(ev.data); //PHP sends Json data

	
	
	var res_type = response.type; //message type
	var user_message = response.message; //message text
	var user_name = response.name; //user name
	var user_color = response.color; //color

console.log(res_type);
	switch (res_type) {
		case 'usermsg':
			if($("#user_typing").length > 0)
				$("#user_typing").remove();
			msgBox.append('<div><span class="user_name" style="color:' + user_color + '">' + user_name + '</span> : <span class="user_msg">' + user_message + '</span></div>');
			break;
		case 'system':
			msgBox.append('<div style="color:#bbbbbb">' + user_message + '</div>');
			break;
		case 'notification':
			var user_notification = response.notification; //notification type
			if(user_notification === 'typing'){
				if($("#user_typing").length <= 0)
					msgBox.append('<div id="user_typing"><span class="user_name" style="color:' + user_color + '">' + user_name + '</span> : <span class="system_msg">' + user_message + '</span></div>');
			} else
				msgBox.append('<div id="user_notification"><span class="user_name" style="color:' + user_color + '">' + user_name + '</span> : <span class="system_msg">' + user_message + '</span></div>');
			break;
	}
	msgBox[0].scrollTop = msgBox[0].scrollHeight; //scroll message 

};

websocket.onerror = function (e) {
	msgBox.append('<div class="system_error">Error Occurred - ' + e.data + '</div>');
};
websocket.onclose = function (e) {
	if(e.wasClean)
		msgBox.append('<div class="system_msg">Connection Closed</div>');
	else
		msgBox.append('<div class="system_error">Connection Interrupted - [Error#' + e.code + '] ' + e.reason + '</div>');
};

//Message send button
$('#send-message').click(function () {
	send_message();
});

//User hits enter key 
$("#message").on("keydown", function (event) {
	if (event.which == 13) {
		send_message();
	}
});
//User typing
$("#message").on("keypress", function (event) {
	if (event.which !== 13)
		send_notification('typing','Typing...');
});

var notifications = [], isTyping = false;

//Send message
function send_message() {
	var message_input = $('#message'); //user message text
	var name_input = $('#name'); //user name

	if (message_input.val() == "") { //empty name?
		alert("Enter your Name please!");
		return;
	}
	if (message_input.val() == "") { //emtpy message?
		alert("Enter Some message Please!");
		return;
	}
	
	isTyping = false;

	//prepare json data
	var msg = {
		message: message_input.val(),
		name: name_input.val(),
		color: colors[color_pick]
	};
	//convert and send data to server
	websocket.send(JSON.stringify(msg));
	message_input.val(''); //reset message input
}

//Send message
function send_notification(type, msg) {
	var name_input = $('#name'); //user name
	notifications.push(type);
	//prepare json data
	var msg = {
		notification: type,
		message: msg,
		name: name_input.val(),
		color: colors[color_pick]
	};
	if(!isTyping){
		isTyping = true;
		//convert and send data to server
		websocket.send(JSON.stringify(msg));
	}
}

/socket/chat2/index.html:

<?php 
$colors = array('#007AFF','#FF7000','#FF7000','#15E25F','#CFC700','#CFC700','#CF1100','#CF00BE','#F00');
$color_pick = array_rand($colors);
?>
<!DOCTYPE html>
<html>
	<head>
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<style type="text/css">
			.chat-wrapper {
				font: bold 11px/normal 'lucida grande', tahoma, verdana, arial, sans-serif;
				background: #00a6bb;
				padding: 20px;
				margin: 20px auto;
				box-shadow: 2px 2px 2px 0px #00000017;
				max-width:700px;
				min-width:500px;
			}
			.system_error {
				color: #a00;
			}
			.system_msg {
				color: #bbb;
			}
			.user_msg {
				color: #00f;
			}
			#message-box {
				width: 97%;
				display: inline-block;
				height: 300px;
				background: #fff;
				box-shadow: inset 0px 0px 2px #00000017;
				overflow: auto;
				padding: 10px;
			}
			.user-panel{
				margin-top: 10px;
			}
			input[type=text]{
				border: none;
				padding: 5px 5px;
				box-shadow: 2px 2px 2px #0000001c;
			}
			input[type=text]#name{
				width:20%;
			}
			input[type=text]#message{
				width:60%;
			}
			button#send-message {
				border: none;
				padding: 5px 15px;
				background: #11e0fb;
				box-shadow: 2px 2px 2px #0000001c;
			}
		</style>
	</head>
	<body>
		<div class="chat-wrapper">
			<div id="message-box"></div>
			<div class="user-panel">
				<input type="text" name="name" id="name" placeholder="Your Name" maxlength="15" />
				<input type="text" name="message" id="message" placeholder="Type your message here..." maxlength="100" />
				<button id="send-message">Send</button>
			</div>
		</div>
		<script src="https://cdn.sgnetworks.net/jquery/lib/3.2.1/jquery-3.2.1.min.js"></script>
		<script language="javascript" type="text/javascript" src="client.js"></script>
	</body>
</html>
Posted

I'm using my own CA signed SSL for https and I'm using XCA to issue the certificates.

Posted

It’s worth noting that Firefox is the only browser that does not use the OS certificate stores...it has its own. There is a flag meant for enterprises that enables it to use the OS cert stores though. Edge, IE, and chrome all use the normal stores by default.

 

He would need to explicitly import custom certs in FF, enable the enterprise roots flag, or use literally any other browser.

Posted

@Krydos, No I just had to import the CA certificate in firefox keystore from the settings screen of the firefox to make all the certificate issued by the CA to be acceptable.

Posted

I can access the https without any problems but I'm getting the error when the request is sent via WSS.

Posted

And one more thing, when I've inspected the error in the Network tab if Firefox Developer Tools, I found the error with request is showing a error code which is "ssl_error_rx_record_too_long".

I've even tried to access the https with port 9000 like https://192.168.1.201:9000, the error was still shown but when accessed https with the same IP without port or with port 443 it's working fine.

Posted

Okay the best matching cause I've found is "If you want your website to establish secure connections you must configure it to use Port 443.". But if I configure the port 9000 as HTTPS or run the web service on port 443, then web service will not be able to connect as it will go through https protocol instead of WSS.

Posted

What I would do is install apache or nginx to handle the ssl and certificates, and then proxy the socket through the web server. Either that or just run the socket without encryption.

Posted

I'm using Apache HTTPD 2.4 on Windows with OpenSSL. How can I do this? I've tried mod_proxy with mod_wsproxy, it's not working.

Posted

I've tried that but it's not working.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...