Use websocket for sends.

pull/1/head
Matthew Chen 7 years ago
parent 5f1682deab
commit 8a76e778b5

@ -976,130 +976,69 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
messages:deviceMessages messages:deviceMessages
relay:recipient.relay relay:recipient.relay
timeStamp:message.timestamp]; timeStamp:message.timestamp];
if (YES) { if (TSSocketManager.sharedManager.canMakeRequests) {
[TSSocketManager.sharedManager makeRequest:request [TSSocketManager.sharedManager makeRequest:request
success:^{ success:^(id _Nullable responseObject) {
DDLogInfo(@"%@ Message send succeeded.", self.logTag); [self messageSendDidSucceed:message
if (isLocalNumber && deviceMessages.count == 0) { recipient:recipient
DDLogInfo( isLocalNumber:isLocalNumber
@"%@ Sent a message with no device messages; clearing 'mayHaveLinkedDevices'.", self.logTag); deviceMessages:deviceMessages
// In order to avoid skipping necessary sync messages, the default value success:successHandler];
// for mayHaveLinkedDevices is YES. Once we've successfully sent a
// sync message with no device messages (e.g. the service has confirmed
// that we have no linked devices), we can set mayHaveLinkedDevices to NO
// to avoid unnecessary message sends for sync messages until we learn
// of a linked device (e.g. through the device linking UI or by receiving
// a sync message, etc.).
[OWSDeviceManager.sharedManager clearMayHaveLinkedDevicesIfNotSet];
}
dispatch_async([OWSDispatch sendingQueue], ^{
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[recipient saveWithTransaction:transaction];
[message updateWithSentRecipient:recipient.uniqueId transaction:transaction];
}];
[self handleMessageSentLocally:message];
successHandler();
});
} }
failure:^(NSInteger statusCode, NSError *error) { failure:^(NSInteger statusCode, NSError *error) {
DDLogError(@"%@ Message send failed.", self.logTag); [self messageSendDidFail:message
DDLogInfo(@"%@ sending to recipient: %@, failed with error.", self.logTag, recipient.uniqueId);
[DDLog flushLog];
// NSHTTPURLResponse *response = (NSHTTPURLResponse
// *)task.response; long statuscode = response.statusCode;
NSData *responseData = error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey];
void (^retrySend)(void) = ^void() {
if (remainingAttempts <= 0) {
// Since we've already repeatedly failed to send to the messaging API,
// it's unlikely that repeating the whole process will succeed.
[error setIsRetryable:NO];
return failureHandler(error);
}
dispatch_async([OWSDispatch sendingQueue], ^{
DDLogDebug(@"%@ Retrying: %@", self.logTag, message.debugDescription);
[self sendMessageToService:message
recipient:recipient recipient:recipient
thread:thread thread:thread
attempts:remainingAttempts isLocalNumber:isLocalNumber
deviceMessages:deviceMessages
remainingAttempts:remainingAttempts
statusCode:statusCode
error:error
success:successHandler success:successHandler
failure:failureHandler]; failure:failureHandler];
}); }];
}; } else {
[self.networkManager makeRequest:request
switch (statusCode) { success:^(NSURLSessionDataTask *task, id responseObject) {
case 401: { [self messageSendDidSucceed:message
DDLogWarn( recipient:recipient
@"%@ Unable to send due to invalid credentials. Did the user's client get de-authed by " isLocalNumber:isLocalNumber
@"registering elsewhere?", deviceMessages:deviceMessages
self.logTag); success:successHandler];
NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeSignalServiceFailure,
NSLocalizedString(@"ERROR_DESCRIPTION_SENDING_UNAUTHORIZED",
@"Error message when attempting to send message"));
// No need to retry if we've been de-authed.
[error setIsRetryable:NO];
return failureHandler(error);
}
case 404: {
DDLogWarn(@"%@ Unregistered recipient: %@", self.logTag, recipient.uniqueId);
[self unregisteredRecipient:recipient message:message thread:thread];
NSError *error = OWSErrorMakeNoSuchSignalRecipientError();
// No need to retry if the recipient is not registered.
[error setIsRetryable:NO];
// If one member of a group deletes their account,
// the group should ignore errors when trying to send
// messages to this ex-member.
[error setShouldBeIgnoredForGroups:YES];
return failureHandler(error);
} }
case 409: { failure:^(NSURLSessionDataTask *task, NSError *error) {
// Mismatched devices NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response;
DDLogWarn(@"%@ Mismatch Devices for recipient: %@", self.logTag, recipient.uniqueId); NSInteger statusCode = response.statusCode;
NSError *error; [self messageSendDidFail:message
NSDictionary *serializedResponse = recipient:recipient
[NSJSONSerialization JSONObjectWithData:responseData options:0 error:&error]; thread:thread
if (error) { isLocalNumber:isLocalNumber
OWSProdError([OWSAnalyticsEvents messageSenderErrorCouldNotParseMismatchedDevicesJson]); deviceMessages:deviceMessages
[error setIsRetryable:YES]; remainingAttempts:remainingAttempts
return failureHandler(error); statusCode:statusCode
error:error
success:successHandler
failure:failureHandler];
}];
} }
}
[self handleMismatchedDevices:serializedResponse recipient:recipient completion:retrySend]; - (void)messageSendDidSucceed:(TSOutgoingMessage *)message
break; recipient:(SignalRecipient *)recipient
} isLocalNumber:(BOOL)isLocalNumber
case 410: { deviceMessages:(NSArray<NSDictionary *> *)deviceMessages
// Stale devices success:(void (^)(void))successHandler
DDLogWarn(@"%@ Stale devices for recipient: %@", self.logTag, recipient.uniqueId); {
OWSAssert(message);
OWSAssert(recipient);
OWSAssert(deviceMessages);
OWSAssert(successHandler);
if (!responseData) { DDLogInfo(@"%@ Message send succeeded.", self.logTag);
DDLogWarn(@"Stale devices but server didn't specify devices in response.");
NSError *error = OWSErrorMakeUnableToProcessServerResponseError();
[error setIsRetryable:YES];
return failureHandler(error);
}
[self handleStaleDevicesWithResponse:responseData
recipientId:recipient.uniqueId
completion:retrySend];
break;
}
default:
retrySend();
break;
}
}];
} else
[self.networkManager makeRequest:request
success:^(NSURLSessionDataTask *task, id responseObject) {
if (isLocalNumber && deviceMessages.count == 0) { if (isLocalNumber && deviceMessages.count == 0) {
DDLogInfo( DDLogInfo(@"%@ Sent a message with no device messages; clearing 'mayHaveLinkedDevices'.", self.logTag);
@"%@ Sent a message with no device messages; clearing 'mayHaveLinkedDevices'.", self.logTag);
// In order to avoid skipping necessary sync messages, the default value // In order to avoid skipping necessary sync messages, the default value
// for mayHaveLinkedDevices is YES. Once we've successfully sent a // for mayHaveLinkedDevices is YES. Once we've successfully sent a
// sync message with no device messages (e.g. the service has confirmed // sync message with no device messages (e.g. the service has confirmed
@ -1119,13 +1058,30 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
[self handleMessageSentLocally:message]; [self handleMessageSentLocally:message];
successHandler(); successHandler();
}); });
} }
failure:^(NSURLSessionDataTask *task, NSError *error) {
- (void)messageSendDidFail:(TSOutgoingMessage *)message
recipient:(SignalRecipient *)recipient
thread:(TSThread *)thread
isLocalNumber:(BOOL)isLocalNumber
deviceMessages:(NSArray<NSDictionary *> *)deviceMessages
remainingAttempts:(int)remainingAttempts
statusCode:(NSInteger)statusCode
error:(NSError *)error
success:(void (^)(void))successHandler
failure:(RetryableFailureHandler)failureHandler
{
OWSAssert(message);
OWSAssert(recipient);
OWSAssert(thread);
OWSAssert(deviceMessages);
OWSAssert(error);
OWSAssert(successHandler);
OWSAssert(failureHandler);
DDLogInfo(@"%@ sending to recipient: %@, failed with error.", self.logTag, recipient.uniqueId); DDLogInfo(@"%@ sending to recipient: %@, failed with error.", self.logTag, recipient.uniqueId);
[DDLog flushLog]; [DDLog flushLog];
NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response;
long statuscode = response.statusCode;
NSData *responseData = error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey]; NSData *responseData = error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey];
void (^retrySend)(void) = ^void() { void (^retrySend)(void) = ^void() {
@ -1147,15 +1103,14 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
}); });
}; };
switch (statuscode) { switch (statusCode) {
case 401: { case 401: {
DDLogWarn( DDLogWarn(@"%@ Unable to send due to invalid credentials. Did the user's client get de-authed by "
@"%@ Unable to send due to invalid credentials. Did the user's client get de-authed by "
@"registering elsewhere?", @"registering elsewhere?",
self.logTag); self.logTag);
NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeSignalServiceFailure, NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeSignalServiceFailure,
NSLocalizedString(@"ERROR_DESCRIPTION_SENDING_UNAUTHORIZED", NSLocalizedString(
@"Error message when attempting to send message")); @"ERROR_DESCRIPTION_SENDING_UNAUTHORIZED", @"Error message when attempting to send message"));
// No need to retry if we've been de-authed. // No need to retry if we've been de-authed.
[error setIsRetryable:NO]; [error setIsRetryable:NO];
return failureHandler(error); return failureHandler(error);
@ -1200,16 +1155,13 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
return failureHandler(error); return failureHandler(error);
} }
[self handleStaleDevicesWithResponse:responseData [self handleStaleDevicesWithResponse:responseData recipientId:recipient.uniqueId completion:retrySend];
recipientId:recipient.uniqueId
completion:retrySend];
break; break;
} }
default: default:
retrySend(); retrySend();
break; break;
} }
}];
} }
- (void)handleMismatchedDevices:(NSDictionary *)dictionary - (void)handleMismatchedDevices:(NSDictionary *)dictionary

@ -4,6 +4,8 @@
#import <SocketRocket/SRWebSocket.h> #import <SocketRocket/SRWebSocket.h>
NS_ASSUME_NONNULL_BEGIN
static void *SocketManagerStateObservationContext = &SocketManagerStateObservationContext; static void *SocketManagerStateObservationContext = &SocketManagerStateObservationContext;
extern NSString *const kNSNotification_SocketManagerStateDidChange; extern NSString *const kNSNotification_SocketManagerStateDidChange;
@ -14,7 +16,8 @@ typedef NS_ENUM(NSUInteger, SocketManagerState) {
SocketManagerStateOpen, SocketManagerStateOpen,
}; };
typedef void (^TSSocketMessageSuccess)(void); typedef void (^TSSocketMessageSuccess)(id _Nullable responseObject);
// statusCode is zero by default, if request never made or failed.
typedef void (^TSSocketMessageFailure)(NSInteger statusCode, NSError *error); typedef void (^TSSocketMessageFailure)(NSInteger statusCode, NSError *error);
@class TSRequest; @class TSRequest;
@ -22,6 +25,7 @@ typedef void (^TSSocketMessageFailure)(NSInteger statusCode, NSError *error);
@interface TSSocketManager : NSObject <SRWebSocketDelegate> @interface TSSocketManager : NSObject <SRWebSocketDelegate>
@property (nonatomic, readonly) SocketManagerState state; @property (nonatomic, readonly) SocketManagerState state;
@property (atomic, readonly) BOOL canMakeRequests;
+ (instancetype)sharedManager; + (instancetype)sharedManager;
@ -45,3 +49,5 @@ typedef void (^TSSocketMessageFailure)(NSInteger statusCode, NSError *error);
failure:(TSSocketMessageFailure)failure; failure:(TSSocketMessageFailure)failure;
@end @end
NS_ASSUME_NONNULL_END

@ -25,6 +25,8 @@
#import "Threading.h" #import "Threading.h"
#import "WebSocketResources.pb.h" #import "WebSocketResources.pb.h"
NS_ASSUME_NONNULL_BEGIN
static const CGFloat kSocketHeartbeatPeriodSeconds = 30.f; static const CGFloat kSocketHeartbeatPeriodSeconds = 30.f;
static const CGFloat kSocketReconnectDelaySeconds = 5.f; static const CGFloat kSocketReconnectDelaySeconds = 5.f;
@ -51,7 +53,7 @@ NSString *const kNSNotification_SocketManagerStateDidChange = @"kNSNotification_
@implementation TSSocketMessage @implementation TSSocketMessage
- (void)didSucceed - (void)didSucceedWithResponseObject:(id _Nullable)responseObject
{ {
@synchronized(self) @synchronized(self)
{ {
@ -64,7 +66,7 @@ NSString *const kNSNotification_SocketManagerStateDidChange = @"kNSNotification_
OWSAssert(self.success); OWSAssert(self.success);
OWSAssert(self.failure); OWSAssert(self.failure);
self.success(); self.success(responseObject);
self.success = nil; self.success = nil;
self.failure = nil; self.failure = nil;
@ -156,10 +158,11 @@ NSString *const kNSNotification_SocketManagerStateDidChange = @"kNSNotification_
@property (nonatomic) BOOL hasObservedNotifications; @property (nonatomic) BOOL hasObservedNotifications;
// These two properties should only be accessed while synchronized on the socket manager. // This property should only be accessed while synchronized on the socket manager.
@property (nonatomic) UInt64 requestIdCounter;
@property (nonatomic, readonly) NSMutableDictionary<NSNumber *, TSSocketMessage *> *socketMessageMap; @property (nonatomic, readonly) NSMutableDictionary<NSNumber *, TSSocketMessage *> *socketMessageMap;
@property (atomic) BOOL canMakeRequests;
@end @end
#pragma mark - #pragma mark -
@ -179,8 +182,6 @@ NSString *const kNSNotification_SocketManagerStateDidChange = @"kNSNotification_
_signalService = [OWSSignalService sharedInstance]; _signalService = [OWSSignalService sharedInstance];
_messageReceiver = [OWSMessageReceiver sharedInstance]; _messageReceiver = [OWSMessageReceiver sharedInstance];
_state = SocketManagerStateClosed; _state = SocketManagerStateClosed;
// Ensure that all request ids are non-zero.
_requestIdCounter = 1;
_socketMessageMap = [NSMutableDictionary new]; _socketMessageMap = [NSMutableDictionary new];
OWSSingletonAssert(); OWSSingletonAssert();
@ -366,12 +367,14 @@ NSString *const kNSNotification_SocketManagerStateDidChange = @"kNSNotification_
// the socket failed immediately for some reason), so we update the state // the socket failed immediately for some reason), so we update the state
// _before_ calling it, not after. // _before_ calling it, not after.
_state = state; _state = state;
_canMakeRequests = state == SocketManagerStateOpen;
[socket open]; [socket open];
return; return;
} }
} }
_state = state; _state = state;
_canMakeRequests = state == SocketManagerStateOpen;
[self notifyStatusChange]; [self notifyStatusChange];
} }
@ -410,18 +413,6 @@ NSString *const kNSNotification_SocketManagerStateDidChange = @"kNSNotification_
- (void)makeRequest:(TSRequest *)request success:(TSSocketMessageSuccess)success failure:(TSSocketMessageFailure)failure - (void)makeRequest:(TSRequest *)request success:(TSSocketMessageSuccess)success failure:(TSSocketMessageFailure)failure
{ {
DispatchMainThreadSafe(^{
[self makeRequestOnMain:request success:success failure:failure];
});
}
// TODO: Currently we schedule requests on main since we want to consult the state property.
// There are alternatives.
- (void)makeRequestOnMain:(TSRequest *)request
success:(TSSocketMessageSuccess)success
failure:(TSSocketMessageFailure)failure
{
OWSAssertIsOnMainThread();
OWSAssert(request); OWSAssert(request);
OWSAssert(request.HTTPMethod.length > 0); OWSAssert(request.HTTPMethod.length > 0);
OWSAssert(success); OWSAssert(success);
@ -432,7 +423,8 @@ NSString *const kNSNotification_SocketManagerStateDidChange = @"kNSNotification_
socketMessage.failure = failure; socketMessage.failure = failure;
@synchronized(self) @synchronized(self)
{ {
socketMessage.requestId = self.requestIdCounter++; // TODO: Should we use another random number generator?
socketMessage.requestId = arc4random();
self.socketMessageMap[@(socketMessage.requestId)] = socketMessage; self.socketMessageMap[@(socketMessage.requestId)] = socketMessage;
} }
@ -444,9 +436,7 @@ NSString *const kNSNotification_SocketManagerStateDidChange = @"kNSNotification_
NSError *error; NSError *error;
jsonData = [NSJSONSerialization jsonData = [NSJSONSerialization
dataWithJSONObject:request.parameters dataWithJSONObject:request.parameters
// TODO:
options:(NSJSONWritingOptions)0 options:(NSJSONWritingOptions)0
// options:NSJSONWritingPrettyPrinted
error:&error]; error:&error];
if (!jsonData || error) { if (!jsonData || error) {
OWSProdLogAndFail(@"%@ could not serialize request JSON: %@", self.logTag, error); OWSProdLogAndFail(@"%@ could not serialize request JSON: %@", self.logTag, error);
@ -456,22 +446,18 @@ NSString *const kNSNotification_SocketManagerStateDidChange = @"kNSNotification_
} }
} }
if (self.state != SocketManagerStateOpen) {
DDLogError(@"%@ makeRequest: socket not open.", self.logTag);
// TODO:
[socketMessage didFailBeforeSending];
return;
}
WebSocketResourcesWebSocketRequestMessageBuilder *requestBuilder = WebSocketResourcesWebSocketRequestMessageBuilder *requestBuilder =
[WebSocketResourcesWebSocketRequestMessageBuilder new]; [WebSocketResourcesWebSocketRequestMessageBuilder new];
requestBuilder.id = socketMessage.requestId; requestBuilder.id = socketMessage.requestId;
[requestBuilder setVerb:request.HTTPMethod]; [requestBuilder setVerb:request.HTTPMethod];
[requestBuilder setPath:requestPath]; [requestBuilder setPath:requestPath];
if (jsonData) {
// TODO: Do we need body & headers for requests with no parameters?
[requestBuilder setBody:jsonData]; [requestBuilder setBody:jsonData];
[requestBuilder setHeadersArray:@[ [requestBuilder setHeadersArray:@[
@"content-type:application/json", @"content-type:application/json",
]]; ]];
}
WebSocketResourcesWebSocketMessageBuilder *messageBuilder = [WebSocketResourcesWebSocketMessageBuilder new]; WebSocketResourcesWebSocketMessageBuilder *messageBuilder = [WebSocketResourcesWebSocketMessageBuilder new];
[messageBuilder setType:WebSocketResourcesWebSocketMessageTypeRequest]; [messageBuilder setType:WebSocketResourcesWebSocketMessageTypeRequest];
@ -480,18 +466,20 @@ NSString *const kNSNotification_SocketManagerStateDidChange = @"kNSNotification_
NSData *messageData = [messageBuilder build].data; NSData *messageData = [messageBuilder build].data;
if (!messageData) { if (!messageData) {
OWSProdLogAndFail(@"%@ could not serialize message.", self.logTag); OWSProdLogAndFail(@"%@ could not serialize message.", self.logTag);
// TODO: [socketMessage didFailBeforeSending];
return;
}
if (!self.canMakeRequests) {
DDLogError(@"%@ makeRequest: socket not open.", self.logTag);
[socketMessage didFailBeforeSending]; [socketMessage didFailBeforeSending];
return; return;
} }
NSError *error; NSError *error;
BOOL wasScheduled = BOOL wasScheduled = [self.websocket sendDataNoCopy:messageData error:&error];
// TODO: Consider using sendDataNoCopy.
[self.websocket sendData:messageData error:&error];
if (!wasScheduled || error) { if (!wasScheduled || error) {
OWSProdLogAndFail(@"%@ could not serialize request JSON: %@", self.logTag, error); OWSProdLogAndFail(@"%@ could not serialize request JSON: %@", self.logTag, error);
// TODO:
[socketMessage didFailBeforeSending]; [socketMessage didFailBeforeSending];
return; return;
} }
@ -501,9 +489,99 @@ NSString *const kNSNotification_SocketManagerStateDidChange = @"kNSNotification_
request.HTTPMethod, request.HTTPMethod,
requestPath, requestPath,
jsonData.length); jsonData.length);
// [socketMessage didSucceed];
// Ensure requests "timeout" after 30 seconds.
__weak TSSocketMessage *weakSocketMessage = socketMessage;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(30 * NSEC_PER_SEC)),
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^{
[weakSocketMessage didFailBeforeSending];
});
}
- (void)processWebSocketResponseMessage:(WebSocketResourcesWebSocketResponseMessage *)message
{
OWSAssertIsOnMainThread();
OWSAssert(message);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self processWebSocketResponseMessageAsync:message];
});
}
- (void)processWebSocketResponseMessageAsync:(WebSocketResourcesWebSocketResponseMessage *)message
{
OWSAssert(message);
DDLogInfo(@"%@ received WebSocket response.", self.logTag);
if (![message hasId]) {
DDLogError(@"%@ received incomplete WebSocket response.", self.logTag);
return;
}
UInt64 requestId = message.id;
UInt32 responseStatus = 0;
if (message.hasStatus) {
responseStatus = message.status;
}
NSString *_Nullable responseMessage;
if (message.hasMessage) {
responseMessage = message.message;
}
NSData *_Nullable responseBody;
if (message.hasBody) {
responseBody = message.body;
}
NSArray<NSString *> *_Nullable responseHeaders = message.headers;
id responseObject = responseBody;
if (responseBody) {
NSError *error;
id _Nullable responseJson =
[NSJSONSerialization JSONObjectWithData:responseBody options:(NSJSONReadingOptions)0 error:&error];
if (!responseJson || error) {
DDLogError(@"%@ could not parse WebSocket response JSON: %@.", self.logTag, error);
// TODO: Should we require JSON parsing to succeed?
} else {
responseObject = responseJson;
}
}
TSSocketMessage *_Nullable socketMessage;
@synchronized(self)
{
socketMessage = self.socketMessageMap[@(requestId)];
[self.socketMessageMap removeObjectForKey:@(requestId)];
}
if (!socketMessage) {
DDLogError(@"%@ received response to unknown request.", self.logTag);
} else {
BOOL didSucceed = 200 <= responseStatus && responseStatus <= 299;
if (didSucceed) {
[socketMessage didSucceedWithResponseObject:responseObject];
} else {
NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeMessageResponseFailed,
NSLocalizedString(
@"ERROR_DESCRIPTION_RESPONSE_FAILED", @"Error indicating that a socket response failed."));
[socketMessage didFailWithStatusCode:(NSInteger)responseStatus error:error];
}
}
DDLogVerbose(@"%@ received WebSocket response: %llu, %zd, %@, %zd, %@, %d, %@.",
self.logTag,
(unsigned long long)requestId,
(NSInteger)responseStatus,
responseMessage,
responseBody.length,
responseHeaders,
socketMessage != nil,
responseObject);
} }
// didSucceedWithResponseObject:(id _Nullable)responseObject
#pragma mark - Delegate methods #pragma mark - Delegate methods
- (void)webSocketDidOpen:(SRWebSocket *)webSocket { - (void)webSocketDidOpen:(SRWebSocket *)webSocket {
@ -538,13 +616,18 @@ NSString *const kNSNotification_SocketManagerStateDidChange = @"kNSNotification_
return; return;
} }
WebSocketResourcesWebSocketMessage *wsMessage = [WebSocketResourcesWebSocketMessage parseFromData:data]; WebSocketResourcesWebSocketMessage *wsMessage;
@try {
wsMessage = [WebSocketResourcesWebSocketMessage parseFromData:data];
} @catch (NSException *exception) {
OWSProdLogAndFail(@"%@ Received an invalid message: %@", self.logTag, exception.debugDescription);
// TODO: Add analytics.
return;
}
if (wsMessage.type == WebSocketResourcesWebSocketMessageTypeRequest) { if (wsMessage.type == WebSocketResourcesWebSocketMessageTypeRequest) {
DDLogVerbose(@"%@ webSocket:didReceiveMessage: request.", self.logTag);
[self processWebSocketRequestMessage:wsMessage.request]; [self processWebSocketRequestMessage:wsMessage.request];
} else if (wsMessage.type == WebSocketResourcesWebSocketMessageTypeResponse) { } else if (wsMessage.type == WebSocketResourcesWebSocketMessageTypeResponse) {
DDLogVerbose(@"%@ webSocket:didReceiveMessage: response.", self.logTag);
[self processWebSocketResponseMessage:wsMessage.response]; [self processWebSocketResponseMessage:wsMessage.response];
} else { } else {
DDLogWarn(@"%@ webSocket:didReceiveMessage: unknown.", self.logTag); DDLogWarn(@"%@ webSocket:didReceiveMessage: unknown.", self.logTag);
@ -609,64 +692,6 @@ NSString *const kNSNotification_SocketManagerStateDidChange = @"kNSNotification_
} }
} }
- (void)processWebSocketResponseMessage:(WebSocketResourcesWebSocketResponseMessage *)message
{
OWSAssertIsOnMainThread();
OWSAssert(message);
DDLogInfo(@"%@ received WebSocket response.", self.logTag);
if (![message hasId]) {
DDLogError(@"%@ received incomplete WebSocket response.", self.logTag);
return;
}
UInt64 requestId = message.id;
UInt32 responseStatus = 0;
if (message.hasStatus) {
responseStatus = message.status;
}
NSString *_Nullable responseMessage;
if (message.hasMessage) {
responseMessage = message.message;
}
NSData *_Nullable responseBody;
if (message.hasBody) {
responseBody = message.body;
}
NSArray<NSString *> *_Nullable responseHeaders = message.headers;
TSSocketMessage *_Nullable socketMessage;
@synchronized(self)
{
socketMessage = self.socketMessageMap[@(requestId)];
[self.socketMessageMap removeObjectForKey:@(requestId)];
if (!socketMessage) {
DDLogError(@"%@ received response to unknown request.", self.logTag);
} else {
BOOL didSucceed = 200 <= responseStatus && responseStatus <= 299;
if (didSucceed) {
[socketMessage didSucceed];
} else {
NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeMessageResponseFailed,
NSLocalizedString(
@"ERROR_DESCRIPTION_RESPONSE_FAILED", @"Error indicating that a socket response failed."));
[socketMessage didFailWithStatusCode:(NSInteger)responseStatus error:error];
}
}
}
DDLogVerbose(@"%@ received WebSocket response: %llu, %zd, %@, %zd, %@, %d.",
self.logTag,
(unsigned long long)requestId,
(NSInteger)responseStatus,
responseMessage,
responseBody.length,
responseHeaders,
socketMessage != nil);
}
- (void)sendWebSocketResourcesWebSocketMessageAcknowledgement:(WebSocketResourcesWebSocketRequestMessage *)request - (void)sendWebSocketResourcesWebSocketMessageAcknowledgement:(WebSocketResourcesWebSocketRequestMessage *)request
{ {
OWSAssertIsOnMainThread(); OWSAssertIsOnMainThread();
@ -706,8 +731,9 @@ NSString *const kNSNotification_SocketManagerStateDidChange = @"kNSNotification_
- (void)webSocket:(SRWebSocket *)webSocket - (void)webSocket:(SRWebSocket *)webSocket
didCloseWithCode:(NSInteger)code didCloseWithCode:(NSInteger)code
reason:(NSString *)reason reason:(nullable NSString *)reason
wasClean:(BOOL)wasClean { wasClean:(BOOL)wasClean
{
OWSAssertIsOnMainThread(); OWSAssertIsOnMainThread();
OWSAssert(webSocket); OWSAssert(webSocket);
if (webSocket != self.websocket) { if (webSocket != self.websocket) {
@ -960,3 +986,5 @@ NSString *const kNSNotification_SocketManagerStateDidChange = @"kNSNotification_
} }
@end @end
NS_ASSUME_NONNULL_END

@ -59,7 +59,7 @@ NSError *OWSErrorMakeMessageSendDisabledDueToPreKeyUpdateFailuresError()
NSError *OWSErrorMakeMessageSendFailedToBlockListError() NSError *OWSErrorMakeMessageSendFailedToBlockListError()
{ {
return OWSErrorWithCodeDescription(OWSErrorCodeMessageRequestFailedToBlockList, return OWSErrorWithCodeDescription(OWSErrorCodeMessageSendFailedToBlockList,
NSLocalizedString(@"ERROR_DESCRIPTION_MESSAGE_SEND_FAILED_DUE_TO_BLOCK_LIST", NSLocalizedString(@"ERROR_DESCRIPTION_MESSAGE_SEND_FAILED_DUE_TO_BLOCK_LIST",
@"Error message indicating that message send failed due to block list")); @"Error message indicating that message send failed due to block list"));
} }

Loading…
Cancel
Save