美文网首页即时通讯
工欲利其事必先利其器-SocketRocket (一)

工欲利其事必先利其器-SocketRocket (一)

作者: Dombo_Y | 来源:发表于2022-12-15 17:13 被阅读0次

SocketRocket: https://github.com/facebookincubator/SocketRocket.git

SocketRocket 是 meta(原facebook)开源的框架,基于webSocket 进行二次开发的OC库。
先看库中涵盖的文件列表,我按照Category、Tools、Interface 三个类型进行文件划分

WechatIMG101.jpeg

依次对文件进行注名:

NSRunLoop+SRWebSocket.h

@interface NSRunLoop (SRWebSocket)

/**
 Default run loop that will be used to schedule all instances of `SRWebSocket`.

 @return An instance of `NSRunLoop`.
 */
+ (NSRunLoop *)SR_networkRunLoop;

@end

NSRunLoop+SRWebSocket.m

#import "NSRunLoop+SRWebSocket.h"
#import "NSRunLoop+SRWebSocketPrivate.h"

#import "SRRunLoopThread.h"

// Required for object file to always be linked.
void import_NSRunLoop_SRWebSocket() { }

@implementation NSRunLoop (SRWebSocket)

+ (NSRunLoop *)SR_networkRunLoop
{
    return [SRRunLoopThread sharedThread].runLoop;
}

@end

NSRunLoop+SRWebSocketPrivate.h

#import <SocketRocket/NSRunLoop+SRWebSocket.h>
// Empty function that force links the object file for the category.
extern void import_NSRunLoop_SRWebSocket(void);

NSURLRequest+SRWebSocket.h



#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface NSURLRequest (SRWebSocket)

/**
 An array of pinned `SecCertificateRef` SSL certificates that `SRWebSocket` will use for validation.
 */
@property (nullable, nonatomic, copy, readonly) NSArray *SR_SSLPinnedCertificates
    DEPRECATED_MSG_ATTRIBUTE("Using pinned certificates is neither secure nor supported in SocketRocket, "
                             "and leads to security issues. Please use a proper, trust chain validated certificate.");

@end

@interface NSMutableURLRequest (SRWebSocket)

/**
 An array of pinned `SecCertificateRef` SSL certificates that `SRWebSocket` will use for validation.
 */
@property (nullable, nonatomic, copy) NSArray *SR_SSLPinnedCertificates
    DEPRECATED_MSG_ATTRIBUTE("Using pinned certificates is neither secure nor supported in SocketRocket, "
                             "and leads to security issues. Please use a proper, trust chain validated certificate.");

@end

NS_ASSUME_NONNULL_END

SRConstants.h

#import <Foundation/Foundation.h>

typedef NS_ENUM(uint8_t, SROpCode)
{
    SROpCodeTextFrame = 0x1, // text类型的占1字节
    SROpCodeBinaryFrame = 0x2, // 二进制类型的2字节
    // 3-7 reserved.
    SROpCodeConnectionClose = 0x8, // 连接关闭类型 8字节
    SROpCodePing = 0x9, // ping 类型 9 字节
    SROpCodePong = 0xA, // pong 类型 10字节
    // B-F reserved.
};

/**
 Default buffer size that is used for reading/writing to streams.
 */
extern size_t SRDefaultBufferSize(void);   // Default  4096字节

SRConstants.m

#import "SRConstants.h"

size_t SRDefaultBufferSize(void) {
    static size_t size;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        size = getpagesize(); // 4096
    });
    return size;
}

SRError.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

extern NSError *SRErrorWithDomainCodeDescription(NSString *domain, NSInteger code, NSString *description);
extern NSError *SRErrorWithCodeDescription(NSInteger code, NSString *description);
extern NSError *SRErrorWithCodeDescriptionUnderlyingError(NSInteger code, NSString *description, NSError *underlyingError);

extern NSError *SRHTTPErrorWithCodeDescription(NSInteger httpCode, NSInteger errorCode, NSString *description);

NS_ASSUME_NONNULL_END

SRError.m

#import "SRError.h"

#import "SRWebSocket.h"

NS_ASSUME_NONNULL_BEGIN

NSError *SRErrorWithDomainCodeDescription(NSString *domain, NSInteger code, NSString *description)
{
    return [NSError errorWithDomain:domain code:code userInfo:@{ NSLocalizedDescriptionKey: description }];
}

NSError *SRErrorWithCodeDescription(NSInteger code, NSString *description)
{
    return SRErrorWithDomainCodeDescription(SRWebSocketErrorDomain, code, description);
}

NSError *SRErrorWithCodeDescriptionUnderlyingError(NSInteger code, NSString *description, NSError *underlyingError)
{
    return [NSError errorWithDomain:SRWebSocketErrorDomain
                               code:code
                           userInfo:@{ NSLocalizedDescriptionKey: description,
                                       NSUnderlyingErrorKey: underlyingError }];
}

NSError *SRHTTPErrorWithCodeDescription(NSInteger httpCode, NSInteger errorCode, NSString *description)
{
    return [NSError errorWithDomain:SRWebSocketErrorDomain
                               code:errorCode
                           userInfo:@{ NSLocalizedDescriptionKey: description,
                                       SRHTTPResponseErrorKey: @(httpCode) }];
}

NS_ASSUME_NONNULL_END

SRHash.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

extern NSData *SRSHA1HashFromString(NSString *string);
extern NSData *SRSHA1HashFromBytes(const char *bytes, size_t length);

extern NSString *SRBase64EncodedStringFromData(NSData *data);

NS_ASSUME_NONNULL_END

SRHash.m

#import "SRHash.h"

#import <CommonCrypto/CommonDigest.h>

NS_ASSUME_NONNULL_BEGIN

NSData *SRSHA1HashFromString(NSString *string)
{
    size_t length = [string lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
    return SRSHA1HashFromBytes(string.UTF8String, length);
}

NSData *SRSHA1HashFromBytes(const char *bytes, size_t length)
{
    uint8_t outputLength = CC_SHA1_DIGEST_LENGTH;
    unsigned char output[outputLength];
    CC_SHA1(bytes, (CC_LONG)length, output);

    return [NSData dataWithBytes:output length:outputLength];
}

NSString *SRBase64EncodedStringFromData(NSData *data)
{
    if ([data respondsToSelector:@selector(base64EncodedStringWithOptions:)]) {
        return [data base64EncodedStringWithOptions:0];
    }

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
    return [data base64Encoding];
#pragma clang diagnostic pop
}

NS_ASSUME_NONNULL_END

配置 HeaderConfig
SRHTTPConnectMessage.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

extern CFHTTPMessageRef SRHTTPConnectMessageCreate(NSURLRequest *request,
                                                   NSString *securityKey,
                                                   uint8_t webSocketProtocolVersion,
                                                   NSArray<NSHTTPCookie *> *_Nullable cookies,
                                                   NSArray<NSString *> *_Nullable requestedProtocols);

NS_ASSUME_NONNULL_END

SRHTTPConnectMessage.m

#import "SRHTTPConnectMessage.h"

#import "SRURLUtilities.h"

NS_ASSUME_NONNULL_BEGIN

static NSString *_SRHTTPConnectMessageHost(NSURL *url)
{
    NSString *host = url.host;
    if (url.port) {
        host = [host stringByAppendingFormat:@":%@", url.port];
    }
    return host;
}

CFHTTPMessageRef SRHTTPConnectMessageCreate(NSURLRequest *request,
                                            NSString *securityKey,
                                            uint8_t webSocketProtocolVersion,
                                            NSArray<NSHTTPCookie *> *_Nullable cookies,
                                            NSArray<NSString *> *_Nullable requestedProtocols)
{
    NSURL *url = request.URL;

    CFHTTPMessageRef message = CFHTTPMessageCreateRequest(NULL, CFSTR("GET"), (__bridge CFURLRef)url, kCFHTTPVersion1_1); //  浏览器请求配置  Get、URL 、 HTTP/1.1

    // Set host first so it defaults
    CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Host"), (__bridge CFStringRef)_SRHTTPConnectMessageHost(url));  // 设置 Host

    NSMutableData *keyBytes = [[NSMutableData alloc] initWithLength:16];
    int result = SecRandomCopyBytes(kSecRandomDefault, keyBytes.length, keyBytes.mutableBytes);
    if (result != 0) {  //  这个是啥用啊????
        //TODO: (nlutsenko) Check if there was an error.
    }

    // Apply cookies if any have been provided
    if (cookies) {   // 如果有cookies 就 用 cookies 中的值
        NSDictionary<NSString *, NSString *> *messageCookies = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
        [messageCookies enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSString * _Nonnull obj, BOOL * _Nonnull stop) {
            if (key.length && obj.length) {
                CFHTTPMessageSetHeaderFieldValue(message, (__bridge CFStringRef)key, (__bridge CFStringRef)obj);
            }
        }];
// NSHTTPCookie 代表一个HTTP cookie,包含不限于 version、name、value、domain、path、isSessionOnly
    }

    // set header for http basic auth
    NSString *basicAuthorizationString = SRBasicAuthorizationHeaderFromURL(url); // user、password
    if (basicAuthorizationString) { //  把验证信息 加入  header
        CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Authorization"), (__bridge CFStringRef)basicAuthorizationString);
    }

    CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Upgrade"), CFSTR("websocket"));
    CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Connection"), CFSTR("Upgrade"));
    CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Sec-WebSocket-Key"), (__bridge CFStringRef)securityKey);
    CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Sec-WebSocket-Version"), (__bridge CFStringRef)@(webSocketProtocolVersion).stringValue);

    CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Origin"), (__bridge CFStringRef)SRURLOrigin(url));

    if (requestedProtocols.count) {
        CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Sec-WebSocket-Protocol"),
                                         (__bridge CFStringRef)[requestedProtocols componentsJoinedByString:@", "]);
    }

    [request.allHTTPHeaderFields enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
        CFHTTPMessageSetHeaderFieldValue(message, (__bridge CFStringRef)key, (__bridge CFStringRef)obj);
    }];

    return message;
}

NS_ASSUME_NONNULL_END

这里涉及到 HTTP的表头模型:


92ee980341a27493.png

用来处理消息的对象
SRIOConsumer.h

#import <Foundation/Foundation.h>

@class SRWebSocket; // TODO: (nlutsenko) Remove dependency on SRWebSocket here.

// Returns number of bytes consumed. Returning 0 means you didn't match.
// Sends bytes to callback handler;
typedef size_t (^stream_scanner)(NSData *collected_data);
typedef void (^data_callback)(SRWebSocket *webSocket,  NSData *data);

@interface SRIOConsumer : NSObject {
    stream_scanner _scanner;
    data_callback _handler;
    size_t _bytesNeeded;
    BOOL _readToCurrentFrame;
    BOOL _unmaskBytes;
}
@property (nonatomic, copy, readonly) stream_scanner consumer;
@property (nonatomic, copy, readonly) data_callback handler;
@property (nonatomic, assign) size_t bytesNeeded;
@property (nonatomic, assign, readonly) BOOL readToCurrentFrame;
@property (nonatomic, assign, readonly) BOOL unmaskBytes;

- (void)resetWithScanner:(stream_scanner)scanner
                 handler:(data_callback)handler
             bytesNeeded:(size_t)bytesNeeded
      readToCurrentFrame:(BOOL)readToCurrentFrame
             unmaskBytes:(BOOL)unmaskBytes;

@end

SRIOConsumer.m

#import "SRIOConsumer.h"

@implementation SRIOConsumer

@synthesize bytesNeeded = _bytesNeeded;
@synthesize consumer = _scanner;
@synthesize handler = _handler;
@synthesize readToCurrentFrame = _readToCurrentFrame;
@synthesize unmaskBytes = _unmaskBytes;

- (void)resetWithScanner:(stream_scanner)scanner
                 handler:(data_callback)handler
             bytesNeeded:(size_t)bytesNeeded
      readToCurrentFrame:(BOOL)readToCurrentFrame
             unmaskBytes:(BOOL)unmaskBytes
{
    _scanner = [scanner copy];
    _handler = [handler copy];
    _bytesNeeded = bytesNeeded;
    _readToCurrentFrame = readToCurrentFrame;
    _unmaskBytes = unmaskBytes;
    assert(_scanner || _bytesNeeded);
}

@end


用来处理消息的对象
SRIOConsumerPool.h

#import <Foundation/Foundation.h>

#import "SRIOConsumer.h" // TODO: (nlutsenko) Convert to @class and constants file for block types

// This class is not thread-safe, and is expected to always be run on the same queue.
@interface SRIOConsumerPool : NSObject

- (instancetype)initWithBufferCapacity:(NSUInteger)poolSize;

- (SRIOConsumer *)consumerWithScanner:(stream_scanner)scanner
                              handler:(data_callback)handler
                          bytesNeeded:(size_t)bytesNeeded
                   readToCurrentFrame:(BOOL)readToCurrentFrame
                          unmaskBytes:(BOOL)unmaskBytes;
- (void)returnConsumer:(SRIOConsumer *)consumer;

@end 

SRIOConsumerPool.m

#import "SRIOConsumerPool.h"

@implementation SRIOConsumerPool {
    NSUInteger _poolSize;
    NSMutableArray<SRIOConsumer *> *_bufferedConsumers;
}

- (instancetype)initWithBufferCapacity:(NSUInteger)poolSize;
{
    self = [super init];
    if (self) {
        _poolSize = poolSize;
        _bufferedConsumers = [NSMutableArray arrayWithCapacity:poolSize];
    }
    return self;
}

- (instancetype)init
{
    return [self initWithBufferCapacity:8];
}

// 获取 一个 IOConsumer 对象
- (SRIOConsumer *)consumerWithScanner:(stream_scanner)scanner
                              handler:(data_callback)handler
                          bytesNeeded:(size_t)bytesNeeded
                   readToCurrentFrame:(BOOL)readToCurrentFrame
                          unmaskBytes:(BOOL)unmaskBytes
{
    SRIOConsumer *consumer = nil;
    if (_bufferedConsumers.count) {
        consumer = [_bufferedConsumers lastObject];
        [_bufferedConsumers removeLastObject];
    } else {
        consumer = [[SRIOConsumer alloc] init];
    }

    [consumer resetWithScanner:scanner
                       handler:handler
                   bytesNeeded:bytesNeeded
            readToCurrentFrame:readToCurrentFrame
                   unmaskBytes:unmaskBytes]; 
    return consumer;
}

- (void)returnConsumer:(SRIOConsumer *)consumer;
{
    if (_bufferedConsumers.count < _poolSize) {
        [_bufferedConsumers addObject:consumer];
    }
} 
@end

SRLog.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

// Uncomment this line to enable debug logging
//#define SR_DEBUG_LOG_ENABLED

extern void SRErrorLog(NSString *format, ...);
extern void SRDebugLog(NSString *format, ...);

NS_ASSUME_NONNULL_END

SRLog.m

#import "SRLog.h"

NS_ASSUME_NONNULL_BEGIN

extern void SRErrorLog(NSString *format, ...)
{
    __block va_list arg_list;
    va_start (arg_list, format);

    NSString *formattedString = [[NSString alloc] initWithFormat:format arguments:arg_list];

    va_end(arg_list);

    NSLog(@"[SocketRocket] %@", formattedString);
}

extern void SRDebugLog(NSString *format, ...)
{
#ifdef SR_DEBUG_LOG_ENABLED
    SRErrorLog(tag, format);
#endif
}

NS_ASSUME_NONNULL_END

SRMutex.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

typedef __attribute__((capability("mutex"))) pthread_mutex_t *SRMutex;

extern SRMutex SRMutexInitRecursive(void);
extern void SRMutexDestroy(SRMutex mutex);

extern void SRMutexLock(SRMutex mutex) __attribute__((acquire_capability(mutex)));
extern void SRMutexUnlock(SRMutex mutex) __attribute__((release_capability(mutex)));

NS_ASSUME_NONNULL_END

SRMutex.n

#import "SRMutex.h"

#import <pthread/pthread.h>

NS_ASSUME_NONNULL_BEGIN

SRMutex SRMutexInitRecursive(void)
{   //  初始化线程互斥
    pthread_mutex_t *mutex = malloc(sizeof(pthread_mutex_t)); 
    pthread_mutexattr_t attributes;

    pthread_mutexattr_init(&attributes);
    pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_RECURSIVE);
    pthread_mutex_init(mutex, &attributes);
    pthread_mutexattr_destroy(&attributes);

    return mutex;
}

void SRMutexDestroy(SRMutex mutex)
{
    pthread_mutex_destroy(mutex);
    free(mutex);
}

__attribute__((no_thread_safety_analysis))
void SRMutexLock(SRMutex mutex)
{
    pthread_mutex_lock(mutex);
}

__attribute__((no_thread_safety_analysis))
void SRMutexUnlock(SRMutex mutex)
{
    pthread_mutex_unlock(mutex);
}

NS_ASSUME_NONNULL_END


SRPinningSecurityPolicy.h

#import <Foundation/Foundation.h>

#import <SocketRocket/SRSecurityPolicy.h>

NS_ASSUME_NONNULL_BEGIN

/** 
 * NOTE: While publicly, SocketRocket does not support configuring the security policy with pinned certificates,
 * it is still possible to manually construct a security policy of this class. If you do this, note that you may
 * be open to MitM attacks, and we will not support any issues you may have. Dive at your own risk.
 */
@interface SRPinningSecurityPolicy : SRSecurityPolicy

- (instancetype)initWithCertificates:(NSArray *)pinnedCertificates;

@end

NS_ASSUME_NONNULL_END

SRPinningSecurityPolicy.m

#import "SRPinningSecurityPolicy.h"

#import <Foundation/Foundation.h>

#import "SRLog.h"

NS_ASSUME_NONNULL_BEGIN

@interface SRPinningSecurityPolicy ()

@property (nonatomic, copy, readonly) NSArray *pinnedCertificates;

@end

@implementation SRPinningSecurityPolicy

- (instancetype)initWithCertificates:(NSArray *)pinnedCertificates
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated"

    // Do not validate certificate chain since we're pinning to specific certificates.
    self = [super initWithCertificateChainValidationEnabled:NO];

#pragma clang diagnostic pop

    if (!self) { return self; }

    if (pinnedCertificates.count == 0) {
        @throw [NSException exceptionWithName:@"Creating security policy failed."
                                       reason:@"Must specify at least one certificate when creating a pinning policy."
                                     userInfo:nil];
    }
    _pinnedCertificates = [pinnedCertificates copy];

    return self;
}

- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain
{
    SRDebugLog(@"Pinned cert count: %d", self.pinnedCertificates.count);
    NSUInteger requiredCertCount = self.pinnedCertificates.count;

    NSUInteger validatedCertCount = 0;
    CFIndex serverCertCount = SecTrustGetCertificateCount(serverTrust);
    for (CFIndex i = 0; i < serverCertCount; i++) {
        SecCertificateRef cert = SecTrustGetCertificateAtIndex(serverTrust, i);
        NSData *data = CFBridgingRelease(SecCertificateCopyData(cert));
        for (id ref in self.pinnedCertificates) {
            SecCertificateRef trustedCert = (__bridge SecCertificateRef)ref;
            // TODO: (nlutsenko) Add caching, so we don't copy the data for every pinned cert all the time.
            NSData *trustedCertData = CFBridgingRelease(SecCertificateCopyData(trustedCert));
            if ([trustedCertData isEqualToData:data]) {
                validatedCertCount++;
                break;
            }
        }
    }
    return (requiredCertCount == validatedCertCount);
}

@end

NS_ASSUME_NONNULL_END

SRRunLoopThread.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface SRRunLoopThread : NSThread

@property (nonatomic, strong, readonly) NSRunLoop *runLoop;

+ (instancetype)sharedThread;

@end

NS_ASSUME_NONNULL_END

SRRunLoopThread.m

#import "SRRunLoopThread.h"

@interface SRRunLoopThread ()
{
    dispatch_group_t _waitGroup;
}

@property (nonatomic, strong, readwrite) NSRunLoop *runLoop;

@end

@implementation SRRunLoopThread

+ (instancetype)sharedThread
{
    static SRRunLoopThread *thread;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        thread = [[SRRunLoopThread alloc] init];
        thread.name = @"com.facebook.SocketRocket.NetworkThread";
        [thread start];
    });
    return thread;
}

- (instancetype)init
{
    self = [super init];
    if (self) {
        _waitGroup = dispatch_group_create();
        dispatch_group_enter(_waitGroup);
    }
    return self;
}

- (void)main
{
    @autoreleasepool {
        _runLoop = [NSRunLoop currentRunLoop];
        dispatch_group_leave(_waitGroup);

        // Add an empty run loop source to prevent runloop from spinning.
        CFRunLoopSourceContext sourceCtx = {
            .version = 0,
            .info = NULL,
            .retain = NULL,
            .release = NULL,
            .copyDescription = NULL,
            .equal = NULL,
            .hash = NULL,
            .schedule = NULL,
            .cancel = NULL,
            .perform = NULL
        };
        CFRunLoopSourceRef source = CFRunLoopSourceCreate(NULL, 0, &sourceCtx);
        CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
        CFRelease(source);

        while ([_runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) {

        }
        assert(NO);
    }
}

- (NSRunLoop *)runLoop;
{
    dispatch_group_wait(_waitGroup, DISPATCH_TIME_FOREVER);
    return _runLoop;
}

@end

SRSecurityPolicy.h

#import <Foundation/Foundation.h>
#import <Security/Security.h>

NS_ASSUME_NONNULL_BEGIN

@interface SRSecurityPolicy : NSObject

/**
 A default `SRSecurityPolicy` implementation specifies socket security and
 validates the certificate chain.

 Use a subclass of `SRSecurityPolicy` for more fine grained customization.
 */
+ (instancetype)defaultPolicy;

/**
 Specifies socket security and provider certificate pinning, disregarding certificate
 chain validation.

 @param pinnedCertificates Array of `SecCertificateRef` SSL certificates to use for validation.
 */
+ (instancetype)pinnningPolicyWithCertificates:(NSArray *)pinnedCertificates
    DEPRECATED_MSG_ATTRIBUTE("Using pinned certificates is neither secure nor supported in SocketRocket, "
                             "and leads to security issues. Please use a proper, trust chain validated certificate.");

/**
 Specifies socket security and optional certificate chain validation.

 @param enabled Whether or not to validate the SSL certificate chain. If you
 are considering using this method because your certificate was not issued by a
 recognized certificate authority, consider using `pinningPolicyWithCertificates` instead.
 */
- (instancetype)initWithCertificateChainValidationEnabled:(BOOL)enabled
    DEPRECATED_MSG_ATTRIBUTE("Disabling certificate chain validation is unsafe. "
                             "Please use a proper Certificate Authority to issue your TLS certificates.")
    NS_DESIGNATED_INITIALIZER;

/**
 Updates all the security options for input and output streams, for example you
 can set your socket security level here.

 @param stream Stream to update the options in.
 */
- (void)updateSecurityOptionsInStream:(NSStream *)stream;

/**
 Whether or not the specified server trust should be accepted, based on the security policy.

 This method should be used when responding to an authentication challenge from
 a server. In the default implemenation, no further validation is done here, but
 you're free to override it in a subclass. See `SRPinningSecurityPolicy.h` for
 an example.

 @param serverTrust The X.509 certificate trust of the server.
 @param domain The domain of serverTrust.

 @return Whether or not to trust the server.
 */
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain;

@end

NS_ASSUME_NONNULL_END

SRSecurityPolicy.m

#import "SRSecurityPolicy.h"
#import "SRPinningSecurityPolicy.h"

NS_ASSUME_NONNULL_BEGIN

@interface SRSecurityPolicy ()

@property (nonatomic, assign, readonly) BOOL certificateChainValidationEnabled;

@end

@implementation SRSecurityPolicy

+ (instancetype)defaultPolicy
{
    return [self new];
}

+ (instancetype)pinnningPolicyWithCertificates:(NSArray *)pinnedCertificates
{
    [NSException raise:NSInvalidArgumentException
                format:@"Using pinned certificates is neither secure nor supported in SocketRocket, "
                        "and leads to security issues. Please use a proper, trust chain validated certificate."];

    return nil;
}

- (instancetype)initWithCertificateChainValidationEnabled:(BOOL)enabled
{
    self = [super init];
    if (!self) { return self; }

    _certificateChainValidationEnabled = enabled;

    return self;
}

- (instancetype)init
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated"

    return [self initWithCertificateChainValidationEnabled:YES];

#pragma clang diagnostic pop
}

- (void)updateSecurityOptionsInStream:(NSStream *)stream
{
    // Enforce TLS 1.2
    [stream setProperty:(__bridge id)CFSTR("kCFStreamSocketSecurityLevelTLSv1_2") forKey:(__bridge id)kCFStreamPropertySocketSecurityLevel];

    // Validate certificate chain for this stream if enabled.
    NSDictionary<NSString *, id> *sslOptions = @{ (__bridge NSString *)kCFStreamSSLValidatesCertificateChain : @(self.certificateChainValidationEnabled) };
    [stream setProperty:sslOptions forKey:(__bridge NSString *)kCFStreamPropertySSLSettings];
}

- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain
{
    // No further evaluation happens in the default policy.
    return YES;
}

@end

NS_ASSUME_NONNULL_END

SRURLUtilities.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

// The origin isn't really applicable for a native application.
// So instead, just map ws -> http and wss -> https.
extern NSString *SRURLOrigin(NSURL *url);

extern BOOL SRURLRequiresSSL(NSURL *url);

// Extracts `user` and `password` from url (if available) into `Basic base64(user:password)`.
extern NSString *_Nullable SRBasicAuthorizationHeaderFromURL(NSURL *url);

// Returns a valid value for `NSStreamNetworkServiceType` or `nil`.
extern NSString *_Nullable SRStreamNetworkServiceTypeFromURLRequest(NSURLRequest *request);

NS_ASSUME_NONNULL_END

SRURLUtilities.m

#import "SRURLUtilities.h"

#import "SRHash.h"

NS_ASSUME_NONNULL_BEGIN

NSString *SRURLOrigin(NSURL *url)
{
    NSMutableString *origin = [NSMutableString string];

    NSString *scheme = url.scheme.lowercaseString;
    if ([scheme isEqualToString:@"wss"]) {
        scheme = @"https";
    } else if ([scheme isEqualToString:@"ws"]) {
        scheme = @"http";
    }
    [origin appendFormat:@"%@://%@", scheme, url.host];

    NSNumber *port = url.port;
    BOOL portIsDefault = (!port ||
                          ([scheme isEqualToString:@"http"] && port.integerValue == 80) ||
                          ([scheme isEqualToString:@"https"] && port.integerValue == 443));
    if (!portIsDefault) {
        [origin appendFormat:@":%@", port.stringValue];
    }
    return origin;
}

extern BOOL SRURLRequiresSSL(NSURL *url)
{
    NSString *scheme = url.scheme.lowercaseString;
    return ([scheme isEqualToString:@"wss"] || [scheme isEqualToString:@"https"]);
}

extern NSString *_Nullable SRBasicAuthorizationHeaderFromURL(NSURL *url)
{
    NSData *data = [[NSString stringWithFormat:@"%@:%@", url.user, url.password] dataUsingEncoding:NSUTF8StringEncoding];
    return [NSString stringWithFormat:@"Basic %@", SRBase64EncodedStringFromData(data)];
}

extern NSString *_Nullable SRStreamNetworkServiceTypeFromURLRequest(NSURLRequest *request)
{
    NSString *networkServiceType = nil;
    switch (request.networkServiceType) {
        case NSURLNetworkServiceTypeDefault:
        case NSURLNetworkServiceTypeResponsiveData:
        case NSURLNetworkServiceTypeAVStreaming:
        case NSURLNetworkServiceTypeResponsiveAV:
            break;
        case NSURLNetworkServiceTypeVoIP:
            networkServiceType = NSStreamNetworkServiceTypeVoIP;
            break;
        case NSURLNetworkServiceTypeVideo:
            networkServiceType = NSStreamNetworkServiceTypeVideo;
            break;
        case NSURLNetworkServiceTypeBackground:
            networkServiceType = NSStreamNetworkServiceTypeBackground;
            break;
        case NSURLNetworkServiceTypeVoice:
            networkServiceType = NSStreamNetworkServiceTypeVoice;
            break;
        case NSURLNetworkServiceTypeCallSignaling: {
            if (@available(iOS 10.0, tvOS 10.0, macOS 10.12, *)) {
                networkServiceType = NSStreamNetworkServiceTypeCallSignaling;
            }
        } break;
    }
    return networkServiceType;
} 
NS_ASSUME_NONNULL_END

以上 为 配置 与 工具类的 相关功能文件,接下来 SRWebSocket 重头戏 在下一章

相关文章

  • 工欲利其事必先利其器

    工欲利其事必先利其器,今天写简书非常的艰难,手机因为故障充不上电,充了一晚上还是2%,还时常自动就关机了。让人...

  • 工欲 善其事必先利其器

    按照常理说,我们脱下的裤头都会马上立即会去洗干净,可是有一段时间我脱下的裤头会有七八件,然后呢?剩下最后一件的时候...

  • 打造自己的Python编码环境

    打造自己的 Python 编码环境 - 作者:XYM | 工欲利其事必先利其器,好的编码环境可以提升我们的打码的幸...

  • 打造你的利器

    本文属于《JavaEE实战——从本地到云端》 打造你的利器 工欲善其事,必先利其器 工欲善其事,必先利其器是说:工...

  • 80-81

    80:工欲善其事,必先利其器 笔记:印象笔记,来记录资料和来管理资料。全平台软件 81:工欲善其事,必先利其器,工...

  • 初体验之人生第一支小楷笔

    欲善其事必先利其器,哈哈,其之妙不可言说

  • 善用工具

    每天学点时间管理 R:工欲利其事必先利其器! U:我非常喜欢这句话,也不断从这句话中得到美好的体验。 以前学化妆,...

  • Win下最爱效率神器:AutoHotKey | 晚晴幽草轩

    工欲善其事,必先利其器!工欲善其事,必先利其器!工欲善其事,必先利其器!重而说三。 @https://link.j...

  • 只要3步教你免费下载国外高质量照片素材

    工欲利其事必先利其器,做设计也不外乎如此。 设计师除了本身的设计修养(创意能力)外 收藏一些精品设计素材(PSD素...

  • 课题组常用工具推荐

    By 小穷博士 前言 公欲利其事,必先利其器。对于我们这个课题组,经常需要编程序进行软件开发,因此选择一些趁手的工...

网友评论

    本文标题:工欲利其事必先利其器-SocketRocket (一)

    本文链接:https://www.haomeiwen.com/subject/yrvrqdtx.html