Archived
Push from command line
This commit is contained in:
@@ -0,0 +1,444 @@
|
||||
/*
|
||||
* Copyright 2017 Google
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import "FIRMessagingSecureSocket.h"
|
||||
|
||||
#import <Protobuf/GPBMessage.h>
|
||||
#import <Protobuf/GPBCodedOutputStream.h>
|
||||
#import <Protobuf/GPBUtilities.h>
|
||||
|
||||
#import "FIRMessagingCodedInputStream.h"
|
||||
#import "FIRMessagingDefines.h"
|
||||
#import "FIRMessagingLogger.h"
|
||||
#import "FIRMessagingPacketQueue.h"
|
||||
|
||||
static const NSUInteger kMaxBufferLength = 1024 * 1024; // 1M
|
||||
static const NSUInteger kBufferLengthIncrement = 16 * 1024; // 16k
|
||||
static const uint8_t kVersion = 40;
|
||||
static const uint8_t kInvalidTag = -1;
|
||||
|
||||
typedef NS_ENUM(NSUInteger, FIRMessagingSecureSocketReadResult) {
|
||||
kFIRMessagingSecureSocketReadResultNone,
|
||||
kFIRMessagingSecureSocketReadResultIncomplete,
|
||||
kFIRMessagingSecureSocketReadResultCorrupt,
|
||||
kFIRMessagingSecureSocketReadResultSuccess
|
||||
};
|
||||
|
||||
static int32_t LogicalRightShift32(int32_t value, int32_t spaces) {
|
||||
return (int32_t)((uint32_t)(value) >> spaces);
|
||||
}
|
||||
|
||||
static NSUInteger SerializedSize(int32_t value) {
|
||||
NSUInteger bytes = 0;
|
||||
while (YES) {
|
||||
if ((value & ~0x7F) == 0) {
|
||||
bytes += sizeof(uint8_t);
|
||||
return bytes;
|
||||
} else {
|
||||
bytes += sizeof(uint8_t);
|
||||
value = LogicalRightShift32(value, 7);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@interface FIRMessagingSecureSocket() <NSStreamDelegate>
|
||||
|
||||
@property(nonatomic, readwrite, assign) FIRMessagingSecureSocketState state;
|
||||
@property(nonatomic, readwrite, strong) NSInputStream *inStream;
|
||||
@property(nonatomic, readwrite, strong) NSOutputStream *outStream;
|
||||
|
||||
@property(nonatomic, readwrite, strong) NSMutableData *inputBuffer;
|
||||
@property(nonatomic, readwrite, assign) NSUInteger inputBufferLength;
|
||||
@property(nonatomic, readwrite, strong) NSMutableData *outputBuffer;
|
||||
@property(nonatomic, readwrite, assign) NSUInteger outputBufferLength;
|
||||
|
||||
@property(nonatomic, readwrite, strong) FIRMessagingPacketQueue *packetQueue;
|
||||
@property(nonatomic, readwrite, assign) BOOL isVersionSent;
|
||||
@property(nonatomic, readwrite, assign) BOOL isVersionReceived;
|
||||
@property(nonatomic, readwrite, assign) BOOL isInStreamOpen;
|
||||
@property(nonatomic, readwrite, assign) BOOL isOutStreamOpen;
|
||||
|
||||
@property(nonatomic, readwrite, strong) NSRunLoop *runLoop;
|
||||
@property(nonatomic, readwrite, strong) NSString *currentRmqIdBeingSent;
|
||||
@property(nonatomic, readwrite, assign) int8_t currentProtoTypeBeingSent;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FIRMessagingSecureSocket
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_state = kFIRMessagingSecureSocketNotOpen;
|
||||
_inputBuffer = [NSMutableData dataWithLength:kBufferLengthIncrement];
|
||||
_packetQueue = [[FIRMessagingPacketQueue alloc] init];
|
||||
_currentProtoTypeBeingSent = kInvalidTag;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self disconnect];
|
||||
}
|
||||
|
||||
- (void)connectToHost:(NSString *)host
|
||||
port:(NSUInteger)port
|
||||
onRunLoop:(NSRunLoop *)runLoop {
|
||||
_FIRMessagingDevAssert(host != nil, @"Invalid host");
|
||||
_FIRMessagingDevAssert(runLoop != nil, @"Invalid runloop");
|
||||
_FIRMessagingDevAssert(self.state == kFIRMessagingSecureSocketNotOpen, @"Socket is already connected");
|
||||
|
||||
if (!host || self.state != kFIRMessagingSecureSocketNotOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket000,
|
||||
@"Opening secure socket to FIRMessaging service");
|
||||
self.state = kFIRMessagingSecureSocketOpening;
|
||||
self.runLoop = runLoop;
|
||||
CFReadStreamRef inputStreamRef;
|
||||
CFWriteStreamRef outputStreamRef;
|
||||
CFStreamCreatePairWithSocketToHost(NULL,
|
||||
(__bridge CFStringRef)host,
|
||||
(int)port,
|
||||
&inputStreamRef,
|
||||
&outputStreamRef);
|
||||
self.inStream = CFBridgingRelease(inputStreamRef);
|
||||
self.outStream = CFBridgingRelease(outputStreamRef);
|
||||
if (!self.inStream || !self.outStream) {
|
||||
FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket001,
|
||||
@"Failed to initialize socket.");
|
||||
return;
|
||||
}
|
||||
|
||||
self.isInStreamOpen = NO;
|
||||
self.isOutStreamOpen = NO;
|
||||
|
||||
BOOL isVOIPSocket = NO;
|
||||
|
||||
[self openStream:self.outStream isVOIPStream:isVOIPSocket];
|
||||
[self openStream:self.inStream isVOIPStream:isVOIPSocket];
|
||||
}
|
||||
|
||||
- (void)disconnect {
|
||||
if (self.state == kFIRMessagingSecureSocketClosing) {
|
||||
return;
|
||||
}
|
||||
if (!self.inStream && !self.outStream) {
|
||||
FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket002,
|
||||
@"The socket is not open or already closed.");
|
||||
_FIRMessagingDevAssert(self.state == kFIRMessagingSecureSocketClosed || self.state == kFIRMessagingSecureSocketNotOpen,
|
||||
@"Socket is already disconnected.");
|
||||
return;
|
||||
}
|
||||
|
||||
self.state = kFIRMessagingSecureSocketClosing;
|
||||
if (self.inStream) {
|
||||
[self closeStream:self.inStream];
|
||||
self.inStream = nil;
|
||||
}
|
||||
if (self.outStream) {
|
||||
[self closeStream:self.outStream];
|
||||
self.outStream = nil;
|
||||
}
|
||||
self.state = kFIRMessagingSecureSocketClosed;
|
||||
[self.delegate didDisconnectWithSecureSocket:self];
|
||||
}
|
||||
|
||||
- (void)sendData:(NSData *)data withTag:(int8_t)tag rmqId:(NSString *)rmqId {
|
||||
[self.packetQueue push:[FIRMessagingPacket packetWithTag:tag rmqId:rmqId data:data]];
|
||||
if ([self.outStream hasSpaceAvailable]) {
|
||||
[self performWrite];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - NSStreamDelegate
|
||||
|
||||
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
|
||||
switch (eventCode) {
|
||||
case NSStreamEventHasBytesAvailable:
|
||||
if (self.state != kFIRMessagingSecureSocketOpen) {
|
||||
FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket003,
|
||||
@"Try to read from socket that is not opened");
|
||||
return;
|
||||
}
|
||||
_FIRMessagingDevAssert(stream == self.inStream, @"Incorrect stream");
|
||||
if (![self performRead]) {
|
||||
FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket004,
|
||||
@"Error occurred when reading incoming stream");
|
||||
[self disconnect];
|
||||
}
|
||||
break;
|
||||
case NSStreamEventEndEncountered:
|
||||
FIRMessagingLoggerDebug(
|
||||
kFIRMessagingMessageCodeSecureSocket005, @"%@ end encountered",
|
||||
stream == self.inStream
|
||||
? @"Input stream"
|
||||
: (stream == self.outStream ? @"Output stream" : @"Unknown stream"));
|
||||
[self disconnect];
|
||||
break;
|
||||
case NSStreamEventOpenCompleted:
|
||||
if (stream == self.inStream) {
|
||||
self.isInStreamOpen = YES;
|
||||
} else if (stream == self.outStream) {
|
||||
self.isOutStreamOpen = YES;
|
||||
}
|
||||
if (self.isInStreamOpen && self.isOutStreamOpen) {
|
||||
FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket006,
|
||||
@"Secure socket to FIRMessaging service opened");
|
||||
self.state = kFIRMessagingSecureSocketOpen;
|
||||
[self.delegate secureSocketDidConnect:self];
|
||||
}
|
||||
break;
|
||||
case NSStreamEventErrorOccurred: {
|
||||
FIRMessagingLoggerDebug(
|
||||
kFIRMessagingMessageCodeSecureSocket007, @"%@ error occurred",
|
||||
stream == self.inStream
|
||||
? @"Input stream"
|
||||
: (stream == self.outStream ? @"Output stream" : @"Unknown stream"));
|
||||
[self disconnect];
|
||||
break;
|
||||
}
|
||||
case NSStreamEventHasSpaceAvailable:
|
||||
if (self.state != kFIRMessagingSecureSocketOpen) {
|
||||
FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket008,
|
||||
@"Try to write to socket that is not opened");
|
||||
return;
|
||||
}
|
||||
_FIRMessagingDevAssert(stream == self.outStream, @"Incorrect stream");
|
||||
[self performWrite];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (void)openStream:(NSStream *)stream isVOIPStream:(BOOL)isVOIPStream {
|
||||
_FIRMessagingDevAssert(stream != nil, @"Invalid stream");
|
||||
_FIRMessagingDevAssert(self.runLoop != nil, @"Invalid runloop");
|
||||
|
||||
if (stream) {
|
||||
_FIRMessagingDevAssert([stream streamStatus] == NSStreamStatusNotOpen, @"Stream already open");
|
||||
if ([stream streamStatus] != NSStreamStatusNotOpen) {
|
||||
FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket009,
|
||||
@"stream should not be open.");
|
||||
return;
|
||||
}
|
||||
[stream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL
|
||||
forKey:NSStreamSocketSecurityLevelKey];
|
||||
if (isVOIPStream) {
|
||||
[stream setProperty:NSStreamNetworkServiceTypeVoIP
|
||||
forKey:NSStreamNetworkServiceType];
|
||||
}
|
||||
stream.delegate = self;
|
||||
[stream scheduleInRunLoop:self.runLoop forMode:NSDefaultRunLoopMode];
|
||||
[stream open];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)closeStream:(NSStream *)stream {
|
||||
_FIRMessagingDevAssert(stream != nil, @"Invalid stream");
|
||||
_FIRMessagingDevAssert(self.runLoop != nil, @"Invalid runloop");
|
||||
|
||||
if (stream) {
|
||||
[stream close];
|
||||
[stream removeFromRunLoop:self.runLoop forMode:NSDefaultRunLoopMode];
|
||||
stream.delegate = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)performRead {
|
||||
_FIRMessagingDevAssert(self.state == kFIRMessagingSecureSocketOpen, @"Socket should be open");
|
||||
|
||||
if (!self.isVersionReceived) {
|
||||
self.isVersionReceived = YES;
|
||||
uint8_t versionByte = 0;
|
||||
NSInteger bytesRead = [self.inStream read:&versionByte maxLength:sizeof(uint8_t)];
|
||||
if (bytesRead != sizeof(uint8_t) || kVersion != versionByte) {
|
||||
FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket010,
|
||||
@"Version do not match. Received %d, Expecting %d", versionByte,
|
||||
kVersion);
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
while (YES) {
|
||||
BOOL isInputBufferValid = [self.inputBuffer length] > 0;
|
||||
_FIRMessagingDevAssert(isInputBufferValid,
|
||||
@"Invalid input buffer size %lu. Used bytes length %lu, buffer content: %@",
|
||||
_FIRMessaging_UL([self.inputBuffer length]),
|
||||
_FIRMessaging_UL(self.inputBufferLength),
|
||||
self.inputBuffer);
|
||||
if (!isInputBufferValid) {
|
||||
FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket011,
|
||||
@"Input buffer is not valid.");
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (![self.inStream hasBytesAvailable]) {
|
||||
break;
|
||||
}
|
||||
|
||||
// try to read more data
|
||||
uint8_t *unusedBufferPtr = (uint8_t *)self.inputBuffer.mutableBytes + self.inputBufferLength;
|
||||
NSUInteger unusedBufferLength = [self.inputBuffer length] - self.inputBufferLength;
|
||||
NSInteger bytesRead = [self.inStream read:unusedBufferPtr maxLength:unusedBufferLength];
|
||||
if (bytesRead <= 0) {
|
||||
FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket012,
|
||||
@"Failed to read input stream. Bytes read %ld, Used buffer size %lu, "
|
||||
@"Unused buffer size %lu",
|
||||
_FIRMessaging_UL(bytesRead), _FIRMessaging_UL(self.inputBufferLength),
|
||||
_FIRMessaging_UL(unusedBufferLength));
|
||||
break;
|
||||
}
|
||||
// did successfully read some more data
|
||||
self.inputBufferLength += (NSUInteger)bytesRead;
|
||||
|
||||
if ([self.inputBuffer length] <= self.inputBufferLength) {
|
||||
// shouldn't be reading more than 1MB of data in one go
|
||||
if ([self.inputBuffer length] + kBufferLengthIncrement > kMaxBufferLength) {
|
||||
FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket013,
|
||||
@"Input buffer exceed 1M, disconnect socket");
|
||||
return NO;
|
||||
}
|
||||
FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket014,
|
||||
@"Input buffer limit exceeded. Used input buffer size %lu, "
|
||||
@"Total input buffer size %lu. No unused buffer left. "
|
||||
@"Increase buffer size.",
|
||||
_FIRMessaging_UL(self.inputBufferLength),
|
||||
_FIRMessaging_UL([self.inputBuffer length]));
|
||||
[self.inputBuffer increaseLengthBy:kBufferLengthIncrement];
|
||||
_FIRMessagingDevAssert([self.inputBuffer length] > self.inputBufferLength, @"Invalid buffer size");
|
||||
}
|
||||
|
||||
while (self.inputBufferLength > 0 && [self.inputBuffer length] > 0) {
|
||||
_FIRMessagingDevAssert([self.inputBuffer length] >= self.inputBufferLength,
|
||||
@"Buffer longer than length");
|
||||
NSRange inputRange = NSMakeRange(0, self.inputBufferLength);
|
||||
size_t protoBytes = 0;
|
||||
// read the actual proto data coming in
|
||||
FIRMessagingSecureSocketReadResult readResult =
|
||||
[self processCurrentInputBuffer:[self.inputBuffer subdataWithRange:inputRange]
|
||||
outOffset:&protoBytes];
|
||||
// Corrupt data encountered, stop processing.
|
||||
if (readResult == kFIRMessagingSecureSocketReadResultCorrupt) {
|
||||
return NO;
|
||||
// Incomplete data, keep trying to read by loading more from the stream.
|
||||
} else if (readResult == kFIRMessagingSecureSocketReadResultIncomplete) {
|
||||
break;
|
||||
}
|
||||
_FIRMessagingDevAssert(self.inputBufferLength >= protoBytes, @"More bytes than buffer can handle");
|
||||
// we have read (0, protoBytes) of data in the inputBuffer
|
||||
if (protoBytes == self.inputBufferLength) {
|
||||
// did completely read the buffer data can be reset for further processing
|
||||
self.inputBufferLength = 0;
|
||||
} else {
|
||||
// delete processed bytes while maintaining the buffer size.
|
||||
NSUInteger prevLength __unused = [self.inputBuffer length];
|
||||
// delete the processed bytes
|
||||
[self.inputBuffer replaceBytesInRange:NSMakeRange(0, protoBytes) withBytes:NULL length:0];
|
||||
// reallocate more data
|
||||
[self.inputBuffer increaseLengthBy:protoBytes];
|
||||
_FIRMessagingDevAssert([self.inputBuffer length] == prevLength,
|
||||
@"Invalid input buffer size %lu. Used bytes length %lu, "
|
||||
@"buffer content: %@",
|
||||
_FIRMessaging_UL([self.inputBuffer length]),
|
||||
_FIRMessaging_UL(self.inputBufferLength),
|
||||
self.inputBuffer);
|
||||
self.inputBufferLength -= protoBytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (FIRMessagingSecureSocketReadResult)processCurrentInputBuffer:(NSData *)readData
|
||||
outOffset:(size_t *)outOffset {
|
||||
*outOffset = 0;
|
||||
|
||||
FIRMessagingCodedInputStream *input = [[FIRMessagingCodedInputStream alloc] initWithData:readData];
|
||||
int8_t rawTag;
|
||||
if (![input readTag:&rawTag]) {
|
||||
return kFIRMessagingSecureSocketReadResultIncomplete;
|
||||
}
|
||||
int32_t length;
|
||||
if (![input readLength:&length]) {
|
||||
return kFIRMessagingSecureSocketReadResultIncomplete;
|
||||
}
|
||||
// NOTE tag can be zero for |HeartbeatPing|, and length can be zero for |Close| proto
|
||||
_FIRMessagingDevAssert(rawTag >= 0 && length >= 0, @"Invalid tag or length");
|
||||
if (rawTag < 0 || length < 0) {
|
||||
FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket015, @"Buffer data corrupted.");
|
||||
return kFIRMessagingSecureSocketReadResultCorrupt;
|
||||
}
|
||||
NSData *data = [input readDataWithLength:(uint32_t)length];
|
||||
if (data == nil) {
|
||||
FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket016,
|
||||
@"Incomplete data, buffered data length %ld, expected length %d",
|
||||
_FIRMessaging_UL(self.inputBufferLength), length);
|
||||
return kFIRMessagingSecureSocketReadResultIncomplete;
|
||||
}
|
||||
[self.delegate secureSocket:self didReceiveData:data withTag:rawTag];
|
||||
*outOffset = input.offset;
|
||||
return kFIRMessagingSecureSocketReadResultSuccess;
|
||||
}
|
||||
|
||||
- (void)performWrite {
|
||||
_FIRMessagingDevAssert(self.state == kFIRMessagingSecureSocketOpen, @"Invalid socket state");
|
||||
|
||||
if (!self.isVersionSent) {
|
||||
self.isVersionSent = YES;
|
||||
uint8_t versionByte = kVersion;
|
||||
[self.outStream write:&versionByte maxLength:sizeof(uint8_t)];
|
||||
}
|
||||
|
||||
while (!self.packetQueue.isEmpty && self.outStream.hasSpaceAvailable) {
|
||||
if (self.outputBuffer.length == 0) {
|
||||
// serialize new packets only when the output buffer is flushed.
|
||||
FIRMessagingPacket *packet = [self.packetQueue pop];
|
||||
self.currentRmqIdBeingSent = packet.rmqId;
|
||||
self.currentProtoTypeBeingSent = packet.tag;
|
||||
NSUInteger length = SerializedSize(packet.tag) +
|
||||
SerializedSize((int)packet.data.length) + packet.data.length;
|
||||
self.outputBuffer = [NSMutableData dataWithLength:length];
|
||||
GPBCodedOutputStream *output = [GPBCodedOutputStream streamWithData:self.outputBuffer];
|
||||
[output writeRawVarint32:packet.tag];
|
||||
[output writeBytesNoTag:packet.data];
|
||||
self.outputBufferLength = 0;
|
||||
}
|
||||
|
||||
// flush the output buffer.
|
||||
NSInteger written = [self.outStream write:self.outputBuffer.bytes + self.outputBufferLength
|
||||
maxLength:self.outputBuffer.length - self.outputBufferLength];
|
||||
if (written <= 0) {
|
||||
continue;
|
||||
}
|
||||
self.outputBufferLength += (NSUInteger)written;
|
||||
if (self.outputBufferLength >= self.outputBuffer.length) {
|
||||
self.outputBufferLength = 0;
|
||||
self.outputBuffer = nil;
|
||||
[self.delegate secureSocket:self
|
||||
didSendProtoWithTag:self.currentProtoTypeBeingSent
|
||||
rmqId:self.currentRmqIdBeingSent];
|
||||
self.currentRmqIdBeingSent = nil;
|
||||
self.currentProtoTypeBeingSent = kInvalidTag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user