Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

PINGPONG

  • Loading branch information...
commit b1137a0db98225ee3f5006fccc2662e7296c8879 1 parent b9bee29
Max Howell authored
Showing with 62 additions and 28 deletions.
  1. +4 −4 MBWebSocketServer.h
  2. +54 −24 MBWebSocketServer.m
  3. +4 −0 README.md
8 MBWebSocketServer.h
View
@@ -24,7 +24,6 @@
@property (nonatomic, readonly) NSUInteger port;
@property (nonatomic, weak) id<MBWebSocketServerDelegate> delegate;
-@property (nonatomic, readonly) BOOL connected;
@property (nonatomic, readonly) NSUInteger clientCount;
@end
@@ -34,9 +33,10 @@
- (void)webSocketServer:(MBWebSocketServer *)webSocketServer clientDisconnected:(GCDAsyncSocket *)connection;
- (void)webSocketServer:(MBWebSocketServer *)webSocket didReceiveData:(NSData *)data fromConnection:(GCDAsyncSocket *)connection;
-// data is passed to you as it was received from the socket, ie. with header & masked
-// if the error occurred before or during handshake, we disconnect the connection
-// immediately after your delegate call returns.
+// Data is passed to you as it was received from the socket, ie. with header & masked
+// We disconnect the connection immediately after your delegate call returns.
+// This always disconnect behavior sucks and should be fixed, but requires more
+// intelligent error handling, so feel free to fix that.
- (void)webSocketServer:(MBWebSocketServer *)webSocketServer couldNotParseRawData:(NSData *)rawData fromConnection:(GCDAsyncSocket *)connection error:(NSError *)error;
@end
78 MBWebSocketServer.m
View
@@ -20,7 +20,6 @@ static unsigned long long ntohll(unsigned long long v) {
@implementation MBWebSocketServer
-@dynamic connected;
@dynamic clientCount;
- (id)initWithPort:(NSUInteger)port delegate:(id<MBWebSocketServerDelegate>)delegate {
@@ -40,10 +39,6 @@ - (id)initWithPort:(NSUInteger)port delegate:(id<MBWebSocketServerDelegate>)dele
return self;
}
-- (BOOL)connected {
- return connections.count > 0;
-}
-
- (NSUInteger)clientCount {
return connections.count;
}
@@ -96,38 +91,72 @@ - (void)socket:(GCDAsyncSocket *)connection didReadData:(NSData *)data withTag:(
}
case 4: {
uint64_t const N = bytes[1] & 0x7f;
-
- if (!bytes[0] & 0x81)
- @throw @"Cannot handle this websocket frame format!";
- if (!bytes[1] & 0x80)
- @throw @"Can only handle websocket frames with masks!";
- if (N >= 126)
- [connection readDataToLength:N == 126 ? 2 : 8 withTimeout:-1 buffer:nil bufferOffset:0 tag:5];
- else
- [connection readDataToLength:N + 4 withTimeout:-1 buffer:nil bufferOffset:0 tag:6];
+ char const opcode = bytes[0] & 0x0f;
+
+ // TODO support fragmented frames (first bit unset in control frame)
+ if (!bytes[0] & 0x80)
+ @throw @"Can't decode fragmented frames!";
+
+ switch (opcode) {
+ case 1: // text frame
+ case 8: // close frame http://tools.ietf.org/html/rfc6455#section-5.5.1
+ case 9: // ping frame http://tools.ietf.org/html/rfc6455#section-5.5.2
+ if (!bytes[1] & 0x80)
+ @throw @"Can only handle websocket frames with masks!";
+ if (N >= 126)
+ [connection readDataToLength:N == 126 ? 2 : 8 withTimeout:-1 buffer:nil bufferOffset:0 tag:16 + opcode];
+ else
+ [connection readDataToLength:N + 4 withTimeout:-1 buffer:nil bufferOffset:0 tag:32 + opcode];
+ break;
+ default:
+ @throw @"Cannot handle this websocket frame format!";
+ }
break;
}
- case 5: {
+ case 0x11: // figure out payload length
+ case 0x18:
+ case 0x19: {
+ uint64_t N;
if (data.length == 2) {
uint16_t *p = (uint16_t *)bytes;
- [connection readDataToLength:ntohs(*p) + 4 withTimeout:-1 buffer:nil bufferOffset:0 tag:6];
+ N = ntohs(*p) + 4;
} else {
uint64_t *p = (uint64_t *)bytes;
- [connection readDataToLength:ntohll(*p) + 4 withTimeout:-1 buffer:nil bufferOffset:0 tag:6];
+ N = ntohll(*p) + 4;
}
+ [connection readDataToLength:N withTimeout:-1 buffer:nil bufferOffset:0 tag:16 + tag];
break;
}
- case 6: {
+ case 0x21: // read complete payload
+ case 0x28:
+ case 0x29: {
NSMutableData *unmaskedData = [NSMutableData dataWithCapacity:data.length - 4];
for (int x = 4; x < data.length; ++x) {
char c = bytes[x] ^ bytes[x%4];
[unmaskedData appendBytes:&c length:1];
}
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- [_delegate webSocketServer:self didReceiveData:unmaskedData fromConnection:connection];
- }];
+ switch (tag & 0xf) {
+ case 1: {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [_delegate webSocketServer:self didReceiveData:unmaskedData fromConnection:connection];
+ });
+ break;
+ }
+ case 8: { // CLOSE
+ char rsp[4] = {0x88, 2, bytes[1], bytes[0]}; // final two bytes are network-byte-order statusCode that we echo back
+ [connection writeData:[NSData dataWithBytes:rsp length:4] withTimeout:-1 tag:-1];
+ break;
+ }
+ case 9: { // PING
+ NSMutableData *pong = unmaskedData.webSocketFrameData.mutableCopy; // FIXME inefficient (but meh)
+ ((char *)pong.mutableBytes)[0] = 0x8a;
+ [connection writeData:pong withTimeout:-1 tag:-1];
+ break;
+ }
+ }
+ // configure the connection to wait for the next frame
[connection readDataToLength:2 withTimeout:-1 buffer:nil bufferOffset:0 tag:4];
break;
}
@@ -135,9 +164,10 @@ - (void)socket:(GCDAsyncSocket *)connection didReadData:(NSData *)data withTag:(
}
@catch (id msg) {
id err = [NSError errorWithDomain:@"com.methylblue.webSocketServer" code:1 userInfo:@{NSLocalizedDescriptionKey: msg}];
- [_delegate webSocketServer:self couldNotParseRawData:data fromConnection:connection error:err];
- if (tag < 4)
- [connection disconnect];
+ dispatch_sync(dispatch_get_main_queue(), ^{
+ [_delegate webSocketServer:self couldNotParseRawData:data fromConnection:connection error:err];
+ });
+ [connection disconnect]; //FIXME some cases do not require disconnect
}
}
4 README.md
View
@@ -11,6 +11,10 @@ Tested against recent Chrome, Safari and Firefox versions. Only tested on Mac.
If you want a client implementation, use Square’s SocketRocket.
+Caveats
+-------
+* There's no support for fragmented frames.
+
Requirements
------------
* ARC or Garbage Collection
Please sign in to comment.
Something went wrong with that request. Please try again.