Occasionally header gets corrupted
Sometimes the header key gets corrupted at the server (somewhere) before hashing and it is sent back as the wrong key to the client.
See the following log
MBWebSocketServer Concatenated Key: 83x1Pg9CUHJtPIJxX0cAyQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11
WebSocketClient Concatenated Key: ai0oBykize/qngGfSrH3Sw==258EAFA5-E914-47DA-95CA-C5AB0DC85B11
It looks like there is an issue when reading the key before it gets to the hashing stage. This only happens maybe 1/20 times.
I found that the issue was with SocketRocket sending a duplicate header with different keys, so nothing to do with this project. The issue is as follows, the handshake gets sent twice (for whatever reason) and MBWebscketServer only processes the first handshake instead of the latest one.
Although not directly a bug in this project, it would be good to add a little bit of logic to check for multiple instances of the handshake and only process the last one. This would make it much more robust. I'll submit some code tomorrow for this.
See below for duplicate header:
GET / HTTP/1.1
Host: localhost:8001
Origin: http://localhost:8001/
Sec-WebSocket-Key: KO3jnDW54UHeLHiAscOF2Q==
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Version: 13
GET / HTTP/1.1
Host: localhost:8001
Origin: http://localhost:8001/
Sec-WebSocket-Key: 4L5fa8w2NqxQeLKs4Y9JZQ==
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Version: 13
For anybody else who encounters this problem, here's my fix which only fixes the problem if there's duplicate headers sent:
Pass the string to new method in handshakeResponseForData:(NSData *)data after the string is created
// MBWebSocketServer.m
NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
// This is a check to see if there are duplicate headers
[self fixDuplicateHeader:&string];
Add this method to MBWebSocketServer.m to remove any duplicate header
// MBWebSocketServer.m
- (void)fixDuplicateHeader:(NSString **)headerString {
// Setup
NSString *lastHandshake = @"";
NSString *getStr = @"GET / HTTP/1.1\r\n";
NSScanner *scanner = [NSScanner scannerWithString:*headerString];
[scanner scanUpToString:getStr intoString:&lastHandshake]; // Scan all characters before GET str
if (lastHandshake) {
while (![scanner isAtEnd]) {
[scanner setScanLocation:([scanner scanLocation]+getStr.length)];
//[scanner scanString:@"GET / HTTP/1.1\r\n" intoString:nil]; // Scan the GET str
[scanner scanUpToString:getStr intoString:&lastHandshake]; // Scan all characters before next GET str
}
*headerString = [NSString stringWithFormat:@"%@%@",getStr,lastHandshake];
NSLog(@"[Server] Removed a duplicate header sent from client");
}
}
We should handle this properly, even if SocketRocket is behaving badly to trigger it.