FConnection.m 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. /*
  2. * Copyright 2017 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import <FirebaseCore/FIRLogger.h>
  17. #import "FConnection.h"
  18. #import "FConstants.h"
  19. typedef enum {
  20. REALTIME_STATE_CONNECTING = 0,
  21. REALTIME_STATE_CONNECTED = 1,
  22. REALTIME_STATE_DISCONNECTED = 2,
  23. } FConnectionState;
  24. @interface FConnection () {
  25. FConnectionState state;
  26. }
  27. @property (nonatomic, strong) FWebSocketConnection* conn;
  28. @property (nonatomic, strong) FRepoInfo* repoInfo;
  29. @end
  30. #pragma mark -
  31. #pragma mark FConnection implementation
  32. @implementation FConnection
  33. @synthesize delegate;
  34. @synthesize conn;
  35. @synthesize repoInfo;
  36. #pragma mark -
  37. #pragma mark Initializers
  38. - (id)initWith:(FRepoInfo *)aRepoInfo andDispatchQueue:(dispatch_queue_t)queue lastSessionID:(NSString *)lastSessionID{
  39. self = [super init];
  40. if (self) {
  41. state = REALTIME_STATE_CONNECTING;
  42. self.repoInfo = aRepoInfo;
  43. self.conn = [[FWebSocketConnection alloc] initWith:self.repoInfo andQueue:queue lastSessionID:lastSessionID];
  44. self.conn.delegate = self;
  45. }
  46. return self;
  47. }
  48. #pragma mark -
  49. #pragma mark Public method implementation
  50. - (void)open {
  51. FFLog(@"I-RDB082001", @"Calling open in FConnection");
  52. [self.conn open];
  53. }
  54. - (void) closeWithReason:(FDisconnectReason)reason {
  55. if (state != REALTIME_STATE_DISCONNECTED) {
  56. FFLog(@"I-RDB082002", @"Closing realtime connection.");
  57. state = REALTIME_STATE_DISCONNECTED;
  58. if (self.conn) {
  59. FFLog(@"I-RDB082003", @"Calling close again.");
  60. [self.conn close];
  61. self.conn = nil;
  62. }
  63. [self.delegate onDisconnect:self withReason:reason];
  64. }
  65. }
  66. - (void) close {
  67. [self closeWithReason:DISCONNECT_REASON_OTHER];
  68. }
  69. - (void) sendRequest:(NSDictionary *)dataMsg sensitive:(BOOL)sensitive {
  70. // since this came from the persistent connection, wrap it in a data message envelope
  71. NSDictionary* msg = @{
  72. kFWPRequestType: kFWPRequestTypeData,
  73. kFWPRequestDataPayload: dataMsg
  74. };
  75. [self sendData:msg sensitive:sensitive];
  76. }
  77. #pragma mark -
  78. #pragma mark Helpers
  79. - (void) sendData:(NSDictionary *)data sensitive:(BOOL)sensitive {
  80. if (state != REALTIME_STATE_CONNECTED) {
  81. @throw [[NSException alloc] initWithName:@"InvalidConnectionState" reason:@"Tried to send data on an unconnected FConnection" userInfo:nil];
  82. } else {
  83. if (sensitive) {
  84. FFLog(@"I-RDB082004", @"Sending data (contents hidden)");
  85. } else {
  86. FFLog(@"I-RDB082005", @"Sending: %@", data);
  87. }
  88. [self.conn send:data];
  89. }
  90. }
  91. #pragma mark -
  92. #pragma mark FWebSocketConnectinDelegate implementation
  93. // Corresponds to onConnectionLost in JS
  94. - (void)onDisconnect:(FWebSocketConnection *)fwebSocket wasEverConnected:(BOOL)everConnected {
  95. self.conn = nil;
  96. if (!everConnected && state == REALTIME_STATE_CONNECTING) {
  97. FFLog(@"I-RDB082006", @"Realtime connection failed.");
  98. // Since we failed to connect at all, clear any cached entry for this namespace in case the machine went away
  99. [self.repoInfo clearInternalHostCache];
  100. } else if (state == REALTIME_STATE_CONNECTED) {
  101. FFLog(@"I-RDB082007", @"Realtime connection lost.");
  102. }
  103. [self close];
  104. }
  105. // Corresponds to onMessageReceived in JS
  106. - (void)onMessage:(FWebSocketConnection *)fwebSocket withMessage:(NSDictionary *)message {
  107. NSString* rawMessageType = [message objectForKey:kFWPAsyncServerEnvelopeType];
  108. if(rawMessageType != nil) {
  109. if([rawMessageType isEqualToString:kFWPAsyncServerDataMessage]) {
  110. [self onDataMessage:[message objectForKey:kFWPAsyncServerEnvelopeData]];
  111. }
  112. else if ([rawMessageType isEqualToString:kFWPAsyncServerControlMessage]) {
  113. [self onControl:[message objectForKey:kFWPAsyncServerEnvelopeData]];
  114. }
  115. else {
  116. FFLog(@"I-RDB082008", @"Unrecognized server packet type: %@", rawMessageType);
  117. }
  118. }
  119. else {
  120. FFLog(@"I-RDB082009", @"Unrecognized raw server packet received: %@", message);
  121. }
  122. }
  123. - (void) onDataMessage:(NSDictionary *)message {
  124. // we don't do anything with data messages, just kick them up a level
  125. FFLog(@"I-RDB082010", @"Got data message: %@", message);
  126. [self.delegate onDataMessage:self withMessage:message];
  127. }
  128. - (void) onControl:(NSDictionary *)message {
  129. FFLog(@"I-RDB082011", @"Got control message: %@", message);
  130. NSString* type = [message objectForKey:kFWPAsyncServerControlMessageType];
  131. if([type isEqualToString:kFWPAsyncServerControlMessageShutdown]) {
  132. NSString* reason = [message objectForKey:kFWPAsyncServerControlMessageData];
  133. [self onConnectionShutdownWithReason:reason];
  134. }
  135. else if ([type isEqualToString:kFWPAsyncServerControlMessageReset]) {
  136. NSString* host = [message objectForKey:kFWPAsyncServerControlMessageData];
  137. [self onReset:host];
  138. }
  139. else if ([type isEqualToString:kFWPAsyncServerHello]) {
  140. NSDictionary* handshakeData = [message objectForKey:kFWPAsyncServerControlMessageData];
  141. [self onHandshake:handshakeData];
  142. }
  143. else {
  144. FFLog(@"I-RDB082012", @"Unknown control message returned from server: %@", message);
  145. }
  146. }
  147. - (void) onConnectionShutdownWithReason:(NSString *)reason {
  148. FFLog(@"I-RDB082013", @"Connection shutdown command received. Shutting down...");
  149. [self.delegate onKill:self withReason:reason];
  150. [self close];
  151. }
  152. - (void) onHandshake:(NSDictionary *)handshake {
  153. NSNumber* timestamp = [handshake objectForKey:kFWPAsyncServerHelloTimestamp];
  154. // NSString* version = [handshake objectForKey:kFWPAsyncServerHelloVersion];
  155. NSString* host = [handshake objectForKey:kFWPAsyncServerHelloConnectedHost];
  156. NSString* sessionID = [handshake objectForKey:kFWPAsyncServerHelloSession];
  157. self.repoInfo.internalHost = host;
  158. if (state == REALTIME_STATE_CONNECTING) {
  159. [self.conn start];
  160. [self onConnection:self.conn readyAtTime:timestamp sessionID:sessionID];
  161. }
  162. }
  163. - (void) onConnection:(FWebSocketConnection *)conn readyAtTime:(NSNumber *)timestamp sessionID:(NSString *)sessionID {
  164. FFLog(@"I-RDB082014", @"Realtime connection established");
  165. state = REALTIME_STATE_CONNECTED;
  166. [self.delegate onReady:self atTime:timestamp sessionID:sessionID];
  167. }
  168. - (void) onReset:(NSString *)host {
  169. FFLog(@"I-RDB082015", @"Got a reset; killing connection to: %@; Updating internalHost to: %@", repoInfo.internalHost, host);
  170. self.repoInfo.internalHost = host;
  171. // Explicitly close the connection with SERVER_RESET so calling code knows to reconnect immediately.
  172. [self closeWithReason:DISCONNECT_REASON_SERVER_RESET];
  173. }
  174. @end