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

Add max message size #759

Merged
merged 8 commits into from
Aug 8, 2018
Merged
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
7 changes: 7 additions & 0 deletions Source/ARTBaseMessage.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//

#import <Foundation/Foundation.h>
#import <Ably/ARTTypes.h>

NS_ASSUME_NONNULL_BEGIN

Expand All @@ -29,8 +30,14 @@ NS_ASSUME_NONNULL_BEGIN

@property (strong, nonatomic, nullable) id data;

/// The event name, if available
@property (nullable, readwrite, strong, nonatomic) NSString *name;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why name is moved. Is the idea to include name in PresenceMessage ? That shouldn't happen.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@paddybyers ARTBaseMessage is a basic superclass. Given the way classes are structured now, this change allows to limit the number of implementations of the method that calculates if a bunch of messages exceeds maxMessageSize. Yes, technically that means that an ARTPresenceMessagecan now have a name property.

If you like, I can address this in a separate PR.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So is it not easy to do:

ARTMessage.messageSize() {
  return super.messageSize() + name.length;
}

?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's one way to do it. I was caught by other aspects of this PR and I didn't think of it :)
Issue here: #762

@property (nullable, nonatomic) id<ARTJsonCompatible> extras;

- (NSString *)description;

- (NSInteger)messageSize;

@end

NS_ASSUME_NONNULL_END
25 changes: 25 additions & 0 deletions Source/ARTBaseMessage.m
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,29 @@ - (NSString *)description {
return description;
}

- (NSInteger)messageSize {
// TO3l8*
NSInteger finalResult = 0;
finalResult += [self.name lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
finalResult += [[self.extras toJSONString] lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
finalResult += [self.clientId lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
if (self.data) {
if ([self.data isKindOfClass:[NSString class]]) {
finalResult += [self.data lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
}
else if ([self.data isKindOfClass:[NSData class]]) {
finalResult += [self.data length];
} else {
NSError *error = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:self.data
options:NSJSONWritingPrettyPrinted
error:&error];
if (!error) {
finalResult += [jsonData length];
}
}
}
return finalResult;
}

@end
2 changes: 2 additions & 0 deletions Source/ARTChannel.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
@class ARTRest;
@class ARTChannelOptions;
@class ARTMessage;
@class ARTBaseMessage;
@class ARTPaginatedResult<ItemType>;
@class ARTDataQuery;

Expand Down Expand Up @@ -43,6 +44,7 @@ NS_ASSUME_NONNULL_BEGIN

- (void)history:(void(^)(ARTPaginatedResult<ARTMessage *> *_Nullable result, ARTErrorInfo *_Nullable error))callback;

- (BOOL)exceedMaxSize:(NSArray<ARTBaseMessage *> *)messages;
@end

NS_ASSUME_NONNULL_END
43 changes: 43 additions & 0 deletions Source/ARTChannel.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#import "ARTBaseMessage+Private.h"
#import "ARTDataQuery.h"
#import "ARTRest+Private.h"
#import "ARTDefault.h"

@implementation ARTChannel {
__weak ARTRest *_rest;
Expand Down Expand Up @@ -73,6 +74,17 @@ - (void)publish:(art_nullable NSString *)name data:(art_nullable id)data extras:
if (callback) callback([ARTErrorInfo createFromNSError:error]);
return;
}

// Checked after encoding, so that the client can receive callback with encoding errors
if ([self exceedMaxSize:@[message]]) {
ARTErrorInfo *sizeError = [ARTErrorInfo createWithCode:40009
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a common error between all libs?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should add those error codes in an enum or at least have it in a global&static var, even though, I know there are a bunch of hard coded codes in the source. Maybe raising an issue is appropriated. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sense. Issue here: #760

message:@"maximum message length exceeded"];
if (callback) {
callback(sizeError);
}
return;
}

[self internalPostMessages:messagesWithDataEncoded callback:callback];
}

Expand All @@ -97,6 +109,17 @@ - (void)publish:(NSString *)name data:(id)data clientId:(NSString *)clientId ext
if (callback) callback([ARTErrorInfo createFromNSError:error]);
return;
}

// Checked after encoding, so that the client can receive callback with encoding errors
if ([self exceedMaxSize:@[message]]) {
ARTErrorInfo *sizeError = [ARTErrorInfo createWithCode:40009
message:@"maximum message length exceeded"];
if (callback) {
callback(sizeError);
}
return;
}

[self internalPostMessages:messagesWithDataEncoded callback:callback];
}

Expand All @@ -106,6 +129,7 @@ - (void)publish:(NSArray<ARTMessage *> *)messages {

- (void)publish:(__GENERIC(NSArray, ARTMessage *) *)messages callback:(art_nullable void (^)(ARTErrorInfo *__art_nullable error))callback {
NSError *error = nil;

NSMutableArray<ARTMessage *> *messagesWithDataEncoded = [NSMutableArray new];
for (ARTMessage *message in messages) {
[messagesWithDataEncoded addObject:[self encodeMessageIfNeeded:message error:&error]];
Expand All @@ -114,9 +138,28 @@ - (void)publish:(__GENERIC(NSArray, ARTMessage *) *)messages callback:(art_nulla
callback([ARTErrorInfo createFromNSError:error]);
return;
}

// Checked after encoding, so that the client can receive callback with encoding errors
if ([self exceedMaxSize:messages]) {
ARTErrorInfo *sizeError = [ARTErrorInfo createWithCode:40009
message:@"maximum message length exceeded"];
if (callback) {
callback(sizeError);
}
return;
}

[self internalPostMessages:messagesWithDataEncoded callback:callback];
}

- (BOOL)exceedMaxSize:(NSArray<ARTBaseMessage *> *)messages {
NSInteger size = 0;
for (ARTMessage *message in messages) {
size += [message messageSize];
}
return size > [ARTDefault maxMessageSize];
}

- (ARTMessage *)encodeMessageIfNeeded:(ARTMessage *)message error:(NSError **)error {
if (!self.dataEncoder) {
return message;
Expand Down
1 change: 1 addition & 0 deletions Source/ARTConnection+Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)setId:(NSString *_Nullable)newId;
- (void)setKey:(NSString *_Nullable)key;
- (void)setSerial:(int64_t)serial;
- (void)setMaxMessageSize:(NSInteger)maxMessageSize;
- (void)setState:(ARTRealtimeConnectionState)state;
- (void)setErrorReason:(ARTErrorInfo *_Nullable)errorReason;

Expand Down
1 change: 1 addition & 0 deletions Source/ARTConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nullable, readonly, strong, nonatomic) NSString *key;
@property (nullable, readonly, getter=getRecoveryKey) NSString *recoveryKey;
@property (readonly, assign, nonatomic) int64_t serial;
@property (readonly, assign, nonatomic) NSInteger maxMessageSize;
@property (readonly, assign, nonatomic) ARTRealtimeConnectionState state;
@property (nullable, readonly, strong, nonatomic) ARTErrorInfo *errorReason;

Expand Down
7 changes: 7 additions & 0 deletions Source/ARTConnection.m
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ @implementation ARTConnection {
_Nonnull dispatch_queue_t _queue;
NSString *_id;
NSString *_key;
NSInteger _maxMessageSize;
int64_t _serial;
ARTRealtimeConnectionState _state;
ARTErrorInfo *_errorReason;
Expand Down Expand Up @@ -135,6 +136,12 @@ - (void)setSerial:(int64_t)serial {
} ART_TRY_OR_MOVE_TO_FAILED_END
}

- (void)setMaxMessageSize:(NSInteger)maxMessageSize {
ART_TRY_OR_MOVE_TO_FAILED_START(_realtime) {
_maxMessageSize = maxMessageSize;
} ART_TRY_OR_MOVE_TO_FAILED_END
}

- (void)setState:(ARTRealtimeConnectionState)state {
ART_TRY_OR_MOVE_TO_FAILED_START(_realtime) {
_state = state;
Expand Down
4 changes: 0 additions & 4 deletions Source/ARTMessage.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@ NS_ASSUME_NONNULL_BEGIN

@interface ARTMessage : ARTBaseMessage

/// The event name, if available
@property (nullable, readwrite, strong, nonatomic) NSString *name;
@property (nullable, nonatomic) id<ARTJsonCompatible> extras;

- (instancetype)initWithName:(nullable NSString *)name data:(id)data;
- (instancetype)initWithName:(nullable NSString *)name data:(id)data clientId:(NSString *)clientId;

Expand Down
6 changes: 3 additions & 3 deletions Source/ARTMessage.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ @implementation ARTMessage

- (instancetype)initWithName:(NSString *)name data:(id)data {
if (self = [self init]) {
_name = [name copy];
self.name = [name copy];
if (data) {
self.data = data;
self.encoding = @"";
Expand Down Expand Up @@ -42,8 +42,8 @@ - (NSString *)description {

- (id)copyWithZone:(NSZone *)zone {
ARTMessage *message = [super copyWithZone:zone];
message->_name = self.name;
message->_extras = self.extras;
message.name = self.name;
message.extras = self.extras;
return message;
}

Expand Down
1 change: 1 addition & 0 deletions Source/ARTRealtime.m
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,7 @@ - (void)onConnected:(ARTProtocolMessage *)message {
case ARTRealtimeConnecting:
[self.connection setId:message.connectionId];
[self.connection setKey:message.connectionKey];
[self.connection setMaxMessageSize:message.connectionDetails.maxMessageSize];
if (!_resuming) {
[self.connection setSerial:message.connectionSerial];
[self.logger debug:@"RT:%p msgSerial of connection \"%@\" has been reset", self, self.connection.id_nosync];
Expand Down
22 changes: 22 additions & 0 deletions Source/ARTRealtimeChannel.m
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,16 @@ - (void)publishPresence:(ARTPresenceMessage *)msg callback:(art_nullable void (^
if (cb) cb([ARTErrorInfo createWithCode:ARTStateNoClientId message:@"attempted to publish presence message without clientId"]);
return;
}

if ([self exceedMaxSize:@[msg]]) {
if (cb) {
ARTErrorInfo *sizeError = [ARTErrorInfo createWithCode:40009
message:@"maximum message length exceeded"];
cb(sizeError);
}
return;
}

_lastPresenceAction = msg.action;

if (msg.data && self.dataEncoder) {
Expand Down Expand Up @@ -1096,6 +1106,18 @@ - (void)map:(ARTPresenceMap *)map shouldReenterLocalMember:(ARTPresenceMessage *
} ART_TRY_OR_MOVE_TO_FAILED_END
}

- (BOOL)exceedMaxSize:(NSArray<ARTBaseMessage *> *)messages {
NSInteger size = 0;
for (ARTMessage *message in messages) {
size += [message messageSize];
}
NSInteger maxSize = [ARTDefault maxMessageSize];
if (self.realtime.connection.maxMessageSize) {
maxSize = self.realtime.connection.maxMessageSize;
}
return size > maxSize;
}

@end

#pragma mark - ARTEvent
Expand Down
1 change: 1 addition & 0 deletions Source/ARTTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ NSString *generateNonce(void);

@protocol ARTJsonCompatible <NSObject>
- (NSDictionary *_Nullable)toJSON:(NSError *_Nullable *_Nullable)error;
- (NSString *_Nullable)toJSONString;
@end

@interface NSString (ARTEventIdentification) <ARTEventIdentification>
Expand Down
14 changes: 14 additions & 0 deletions Source/ARTTypes.m
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,10 @@ - (NSDictionary *)toJSON:(NSError *_Nullable *_Nullable)error {
return (NSDictionary *)json;
}

- (NSString *)toJSONString {
return self;
}

@end

@implementation NSDictionary (ARTJsonCompatible)
Expand All @@ -186,6 +190,16 @@ - (NSDictionary *)toJSON:(NSError *_Nullable *_Nullable)error {
return self;
}

- (NSString *)toJSONString {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why this was added. We already serialised JSON message payloads didn't we?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We needed a way to have a JSON-stringified representation of a dictionary, which is not a built-in function.

NSError *err = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:self options:0 error:&err];
if (err) {
return nil;
}
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
return jsonString;
}

@end

@implementation NSURL (ARTLog)
Expand Down
Loading