Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[AK] Refactor JFRResponse into a separate private class #24

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion JFRWebSocket.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,6 @@
Set your own custom queue.
Default setting is dispatch_get_main_queue.
*/
@property(nonatomic, strong)dispatch_queue_t queue;
@property(nonatomic)dispatch_queue_t queue;

@end
95 changes: 23 additions & 72 deletions JFRWebSocket.m
Original file line number Diff line number Diff line change
Expand Up @@ -8,50 +8,8 @@
/////////////////////////////////////////////////////////////////////////////

#import "JFRWebSocket.h"
#import "JFRResponse.h"

//this get the correct bits out by masking the bytes of the buffer.
static const uint8_t JFRFinMask = 0x80;
static const uint8_t JFROpCodeMask = 0x0F;
static const uint8_t JFRRSVMask = 0x70;
static const uint8_t JFRMaskMask = 0x80;
static const uint8_t JFRPayloadLenMask = 0x7F;
static const size_t JFRMaxFrameSize = 32;

//get the opCode from the packet
typedef NS_ENUM(NSUInteger, JFROpCode) {
JFROpCodeContinueFrame = 0x0,
JFROpCodeTextFrame = 0x1,
JFROpCodeBinaryFrame = 0x2,
//3-7 are reserved.
JFROpCodeConnectionClose = 0x8,
JFROpCodePing = 0x9,
JFROpCodePong = 0xA,
//B-F reserved.
};

typedef NS_ENUM(NSUInteger, JFRCloseCode) {
JFRCloseCodeNormal = 1000,
JFRCloseCodeGoingAway = 1001,
JFRCloseCodeProtocolError = 1002,
JFRCloseCodeProtocolUnhandledType = 1003,
// 1004 reserved.
JFRCloseCodeNoStatusReceived = 1005,
//1006 reserved.
JFRCloseCodeEncoding = 1007,
JFRCloseCodePolicyViolated = 1008,
JFRCloseCodeMessageTooBig = 1009
};

//holds the responses in our read stack to properly process messages
@interface JFRResponse : NSObject

@property(nonatomic, assign)BOOL isFin;
@property(nonatomic, assign)JFROpCode code;
@property(nonatomic, assign)NSInteger bytesLeft;
@property(nonatomic, assign)NSInteger frameCount;
@property(nonatomic, strong)NSMutableData *buffer;

@end

@interface JFRWebSocket ()<NSStreamDelegate>

Expand Down Expand Up @@ -429,27 +387,27 @@ -(void)processRawMessage:(uint8_t*)buffer length:(NSInteger)bufferLen
self.fragBuffer = [NSData dataWithBytes:buffer length:bufferLen];
return;
}
if(response.bytesLeft > 0) {
NSInteger len = response.bytesLeft;
NSInteger extra = bufferLen - response.bytesLeft;
if(response.bytesLeft > bufferLen) {
if(response.bytesRemaining > 0) {
NSInteger len = response.bytesRemaining;
NSInteger extra = bufferLen - response.bytesRemaining;
if(response.bytesRemaining > bufferLen) {
len = bufferLen;
extra = 0;
}
response.bytesLeft -= len;
response.bytesRemaining -= len;
[response.buffer appendData:[NSData dataWithBytes:buffer length:len]];
[self processResponse:response];
NSInteger offset = bufferLen - extra;
if(extra > 0) {
[self processExtra:(buffer+offset) length:extra];
}
} else {
BOOL isFin = (JFRFinMask & buffer[0]);
uint8_t receivedOpcode = (JFROpCodeMask & buffer[0]);
BOOL isMasked = (JFRMaskMask & buffer[1]);
uint8_t payloadLen = (JFRPayloadLenMask & buffer[1]);
BOOL isFin = (JFRResponseFinMask & buffer[0]);
uint8_t receivedOpcode = (JFRResponseOpCodeMask & buffer[0]);
BOOL isMasked = (JFRResponseMaskMask & buffer[1]);
uint8_t payloadLen = (JFRResponsePayloadLenMask & buffer[1]);
NSInteger offset = 2; //how many bytes do we need to skip for the header
if((isMasked || (JFRRSVMask & buffer[0])) && receivedOpcode != JFROpCodePong) {
if((isMasked || (JFRResponseRSVMask & buffer[0])) && receivedOpcode != JFROpCodePong) {
if([self.delegate respondsToSelector:@selector(websocketDidDisconnect:error:)]) {
[self.delegate websocketDidDisconnect:self error:[self errorWithDetail:@"masked and rsv data is not currently supported" code:JFRCloseCodeProtocolError]];
}
Expand Down Expand Up @@ -551,12 +509,12 @@ -(void)processRawMessage:(uint8_t*)buffer length:(NSInteger)bufferLen
}
isNew = YES;
response = [JFRResponse new];
response.code = receivedOpcode;
response.bytesLeft = dataLength;
response.opCode = receivedOpcode;
response.bytesRemaining = dataLength;
response.buffer = [NSMutableData dataWithData:data];
} else {
if(receivedOpcode == JFROpCodeContinueFrame) {
response.bytesLeft = dataLength;
response.bytesRemaining = dataLength;
} else {
if([self.delegate respondsToSelector:@selector(websocketDidDisconnect:error:)]) {
[self.delegate websocketDidDisconnect:self error:[self errorWithDetail:@"second and beyond of fragment message must be a continue frame" code:JFRCloseCodeProtocolError]];
Expand All @@ -566,9 +524,9 @@ -(void)processRawMessage:(uint8_t*)buffer length:(NSInteger)bufferLen
}
[response.buffer appendData:data];
}
response.bytesLeft -= len;
response.bytesRemaining -= len;
response.frameCount++;
response.isFin = isFin;
response.isFinished = isFin;
if(isNew) {
[self.readStack addObject:response];
}
Expand All @@ -594,11 +552,11 @@ -(void)processExtra:(uint8_t*)buffer length:(NSInteger)bufferLen
/////////////////////////////////////////////////////////////////////////////
-(BOOL)processResponse:(JFRResponse*)response
{
if(response.isFin && response.bytesLeft <= 0) {
if(response.isFinished && response.bytesRemaining <= 0) {
NSData *data = response.buffer;
if(response.code == JFROpCodePing) {
if(response.opCode == JFROpCodePing) {
[self dequeueWrite:response.buffer withCode:JFROpCodePong];
} else if(response.code == JFROpCodeTextFrame) {
} else if(response.opCode == JFROpCodeTextFrame) {
NSString *str = [[NSString alloc] initWithData:response.buffer encoding:NSUTF8StringEncoding];
if(!str) {
[self writeError:JFRCloseCodeEncoding];
Expand Down Expand Up @@ -629,7 +587,7 @@ -(BOOL)processCloseCode:(uint16_t)code
return NO;
}
/////////////////////////////////////////////////////////////////////////////
-(void)dequeueWrite:(NSData*)data withCode:(JFROpCode)code
-(void)dequeueWrite:(NSData*)data withCode:(JFRResponseOpCode)code
{
if(!self.writeQueue) {
self.writeQueue = [[NSOperationQueue alloc] init];
Expand All @@ -650,9 +608,9 @@ -(void)dequeueWrite:(NSData*)data withCode:(JFROpCode)code
uint64_t offset = 2; //how many bytes do we need to skip for the header
uint8_t *bytes = (uint8_t*)[data bytes];
uint64_t dataLength = data.length;
NSMutableData *frame = [[NSMutableData alloc] initWithLength:(NSInteger)(dataLength + JFRMaxFrameSize)];
NSMutableData *frame = [[NSMutableData alloc] initWithLength:(NSInteger)(dataLength + JFRResponseMaxFrameSize)];
uint8_t *buffer = (uint8_t*)[frame mutableBytes];
buffer[0] = JFRFinMask | code;
buffer[0] = JFRResponseFinMask | code;
if(dataLength < 126) {
buffer[1] |= dataLength;
} else if(dataLength <= UINT16_MAX) {
Expand All @@ -666,7 +624,7 @@ -(void)dequeueWrite:(NSData*)data withCode:(JFROpCode)code
}
BOOL isMask = YES;
if(isMask) {
buffer[1] |= JFRMaskMask;
buffer[1] |= JFRResponseMaskMask;
uint8_t *mask_key = (buffer + offset);
SecRandomCopyBytes(kSecRandomDefault, sizeof(uint32_t), (uint8_t *)mask_key);
offset += sizeof(uint32_t);
Expand Down Expand Up @@ -724,10 +682,3 @@ -(void)dealloc
}
/////////////////////////////////////////////////////////////////////////////
@end

/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
@implementation JFRResponse

@end
/////////////////////////////////////////////////////////////////////////////
55 changes: 55 additions & 0 deletions Private/JFRResponse.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//
// JFRResponse.h
// SimpleTest
//
// Created by Adam Kaplan on 4/13/15.
// Copyright (c) 2015 Vluxe. All rights reserved.
//

#import <Foundation/Foundation.h>

/** this get the correct bits out by masking the bytes of the buffer. */
extern const uint8_t JFRResponseFinMask;
extern const uint8_t JFRResponseOpCodeMask;
extern const uint8_t JFRResponseRSVMask;
extern const uint8_t JFRResponseMaskMask;
extern const uint8_t JFRResponsePayloadLenMask;

extern const size_t JFRResponseMaxFrameSize;

/** opcode from websocket frame spec. See RFC-6455 https://tools.ietf.org/html/rfc6455#page-28 */
typedef NS_ENUM(NSUInteger, JFRResponseOpCode) {
JFROpCodeContinueFrame = 0x0,
JFROpCodeTextFrame = 0x1,
JFROpCodeBinaryFrame = 0x2,
// 3-7 are reserved.
JFROpCodeConnectionClose = 0x8,
JFROpCodePing = 0x9,
JFROpCodePong = 0xA,
// B-F are reserved.
};

/** close code from websocket frame spec. See RFC-6455 https://tools.ietf.org/html/rfc6455#page-64 */
typedef NS_ENUM(NSUInteger, JFRResponseCloseCode) {
JFRCloseCodeNormal = 1000,
JFRCloseCodeGoingAway = 1001,
JFRCloseCodeProtocolError = 1002,
JFRCloseCodeProtocolUnhandledType = 1003,
// 1004 reserved.
JFRCloseCodeNoStatusReceived = 1005,
//1006 reserved.
JFRCloseCodeEncoding = 1007,
JFRCloseCodePolicyViolated = 1008,
JFRCloseCodeMessageTooBig = 1009
};

/** Private class to hold the responses in our read stack to properly process messages */
@interface JFRResponse : NSObject

@property(nonatomic) BOOL isFinished;
@property(nonatomic) JFRResponseOpCode opCode;
@property(nonatomic) NSInteger bytesRemaining;
@property(nonatomic) NSInteger frameCount;
@property(nonatomic) NSMutableData *buffer;

@end
21 changes: 21 additions & 0 deletions Private/JFRResponse.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
// JFRResponse.m
// SimpleTest
//
// Created by Adam Kaplan on 4/13/15.
// Copyright (c) 2015 Vluxe. All rights reserved.
//

#import "JFRResponse.h"

const uint8_t JFRResponseFinMask = 0x80;
const uint8_t JFRResponseOpCodeMask = 0x0F;
const uint8_t JFRResponseRSVMask = 0x70;
const uint8_t JFRResponseMaskMask = 0x80;
const uint8_t JFRResponsePayloadLenMask = 0x7F;

const size_t JFRResponseMaxFrameSize = 32;

@implementation JFRResponse

@end
16 changes: 16 additions & 0 deletions SimpleTest/SimpleTest.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
6B22F5D61A9D94C80052A037 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6B22F5D51A9D94C80052A037 /* Images.xcassets */; };
6B22F5D91A9D94C80052A037 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6B22F5D71A9D94C80052A037 /* LaunchScreen.xib */; };
6B22F5F11A9D967D0052A037 /* JFRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B22F5F01A9D967D0052A037 /* JFRWebSocket.m */; };
FC1A3BB71ADC404600F0BDBD /* JFRResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = FC1A3BB61ADC404600F0BDBD /* JFRResponse.m */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
Expand All @@ -29,6 +30,8 @@
6B22F5D81A9D94C80052A037 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
6B22F5EF1A9D967D0052A037 /* JFRWebSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JFRWebSocket.h; path = ../../JFRWebSocket.h; sourceTree = "<group>"; };
6B22F5F01A9D967D0052A037 /* JFRWebSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = JFRWebSocket.m; path = ../../JFRWebSocket.m; sourceTree = "<group>"; };
FC1A3BB51ADC404600F0BDBD /* JFRResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JFRResponse.h; sourceTree = "<group>"; };
FC1A3BB61ADC404600F0BDBD /* JFRResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JFRResponse.m; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -61,6 +64,7 @@
6B22F5C71A9D94C80052A037 /* SimpleTest */ = {
isa = PBXGroup;
children = (
FC1A3BB41ADC404600F0BDBD /* Private */,
6B22F5EF1A9D967D0052A037 /* JFRWebSocket.h */,
6B22F5F01A9D967D0052A037 /* JFRWebSocket.m */,
6B22F5CC1A9D94C80052A037 /* AppDelegate.h */,
Expand All @@ -84,6 +88,16 @@
name = "Supporting Files";
sourceTree = "<group>";
};
FC1A3BB41ADC404600F0BDBD /* Private */ = {
isa = PBXGroup;
children = (
FC1A3BB51ADC404600F0BDBD /* JFRResponse.h */,
FC1A3BB61ADC404600F0BDBD /* JFRResponse.m */,
);
name = Private;
path = ../../Private;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
Expand Down Expand Up @@ -155,6 +169,7 @@
buildActionMask = 2147483647;
files = (
6B22F5D11A9D94C80052A037 /* ViewController.m in Sources */,
FC1A3BB71ADC404600F0BDBD /* JFRResponse.m in Sources */,
6B22F5CE1A9D94C80052A037 /* AppDelegate.m in Sources */,
6B22F5F11A9D967D0052A037 /* JFRWebSocket.m in Sources */,
6B22F5CB1A9D94C80052A037 /* main.m in Sources */,
Expand Down Expand Up @@ -300,6 +315,7 @@
6B22F5EA1A9D94C80052A037 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
Expand Down
9 changes: 7 additions & 2 deletions jetfire.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ Pod::Spec.new do |s|
s.source = { :git => "https://github.com/acmacalister/jetfire.git", :tag => "#{s.version}" }
s.ios.deployment_target = '6.0'
s.osx.deployment_target = '10.7'
s.source_files = '*.{h,m}'
s.source_files = 'JFRWebSocket.{h,m}'
s.requires_arc = true
end

s.subspec 'Core' do |ss|
ss.source_files = 'Private/*.{h,m}'
end
s.default_subspec = 'Core'
end