prototype of ping pong websocket (ai generated)
This commit is contained in:
@@ -89,6 +89,7 @@
|
|||||||
CD936A32289B353F0093A1AC /* MBIMErrorResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = CD936A31289B353F0093A1AC /* MBIMErrorResponse.m */; };
|
CD936A32289B353F0093A1AC /* MBIMErrorResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = CD936A31289B353F0093A1AC /* MBIMErrorResponse.m */; };
|
||||||
CD936A35289B47D60093A1AC /* MBIMVersionOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = CD936A34289B47D50093A1AC /* MBIMVersionOperation.m */; };
|
CD936A35289B47D60093A1AC /* MBIMVersionOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = CD936A34289B47D50093A1AC /* MBIMVersionOperation.m */; };
|
||||||
CD936A39289B49FC0093A1AC /* MBIMStatusOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = CD936A38289B49FC0093A1AC /* MBIMStatusOperation.m */; };
|
CD936A39289B49FC0093A1AC /* MBIMStatusOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = CD936A38289B49FC0093A1AC /* MBIMStatusOperation.m */; };
|
||||||
|
CDA64B472DFCBF3000E9B07E /* MBIMPingPongWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = CDA64B462DFCBF3000E9B07E /* MBIMPingPongWebSocket.m */; };
|
||||||
CDDCF78D283F398C0087ABDF /* MBIMDeleteConversationOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = CDDCF78C283F398C0087ABDF /* MBIMDeleteConversationOperation.m */; };
|
CDDCF78D283F398C0087ABDF /* MBIMDeleteConversationOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = CDDCF78C283F398C0087ABDF /* MBIMDeleteConversationOperation.m */; };
|
||||||
CDE4556421A3578A0041F5DD /* IMChat+Encoded.m in Sources */ = {isa = PBXBuildFile; fileRef = CDE4556321A3578A0041F5DD /* IMChat+Encoded.m */; };
|
CDE4556421A3578A0041F5DD /* IMChat+Encoded.m in Sources */ = {isa = PBXBuildFile; fileRef = CDE4556321A3578A0041F5DD /* IMChat+Encoded.m */; };
|
||||||
CDE455A121A365AD0041F5DD /* MBIMMarkOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = CDE455A021A365AD0041F5DD /* MBIMMarkOperation.m */; };
|
CDE455A121A365AD0041F5DD /* MBIMMarkOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = CDE455A021A365AD0041F5DD /* MBIMMarkOperation.m */; };
|
||||||
@@ -260,6 +261,8 @@
|
|||||||
CD936A34289B47D50093A1AC /* MBIMVersionOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MBIMVersionOperation.m; sourceTree = "<group>"; };
|
CD936A34289B47D50093A1AC /* MBIMVersionOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MBIMVersionOperation.m; sourceTree = "<group>"; };
|
||||||
CD936A37289B49FC0093A1AC /* MBIMStatusOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MBIMStatusOperation.h; sourceTree = "<group>"; };
|
CD936A37289B49FC0093A1AC /* MBIMStatusOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MBIMStatusOperation.h; sourceTree = "<group>"; };
|
||||||
CD936A38289B49FC0093A1AC /* MBIMStatusOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MBIMStatusOperation.m; sourceTree = "<group>"; };
|
CD936A38289B49FC0093A1AC /* MBIMStatusOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MBIMStatusOperation.m; sourceTree = "<group>"; };
|
||||||
|
CDA64B452DFCBF3000E9B07E /* MBIMPingPongWebSocket.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MBIMPingPongWebSocket.h; sourceTree = "<group>"; };
|
||||||
|
CDA64B462DFCBF3000E9B07E /* MBIMPingPongWebSocket.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MBIMPingPongWebSocket.m; sourceTree = "<group>"; };
|
||||||
CDDCF78B283F398C0087ABDF /* MBIMDeleteConversationOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MBIMDeleteConversationOperation.h; sourceTree = "<group>"; };
|
CDDCF78B283F398C0087ABDF /* MBIMDeleteConversationOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MBIMDeleteConversationOperation.h; sourceTree = "<group>"; };
|
||||||
CDDCF78C283F398C0087ABDF /* MBIMDeleteConversationOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MBIMDeleteConversationOperation.m; sourceTree = "<group>"; };
|
CDDCF78C283F398C0087ABDF /* MBIMDeleteConversationOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MBIMDeleteConversationOperation.m; sourceTree = "<group>"; };
|
||||||
CDE4556221A3578A0041F5DD /* IMChat+Encoded.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "IMChat+Encoded.h"; sourceTree = "<group>"; };
|
CDE4556221A3578A0041F5DD /* IMChat+Encoded.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "IMChat+Encoded.h"; sourceTree = "<group>"; };
|
||||||
@@ -376,6 +379,8 @@
|
|||||||
CD14F1AC219FFAE100E7DD22 /* MBIMConcurrentHTTPServer.m */,
|
CD14F1AC219FFAE100E7DD22 /* MBIMConcurrentHTTPServer.m */,
|
||||||
1ACFCFE2219EB45300E2C237 /* MBIMHTTPConnection.h */,
|
1ACFCFE2219EB45300E2C237 /* MBIMHTTPConnection.h */,
|
||||||
1ACFCFE3219EB45300E2C237 /* MBIMHTTPConnection.m */,
|
1ACFCFE3219EB45300E2C237 /* MBIMHTTPConnection.m */,
|
||||||
|
CDA64B452DFCBF3000E9B07E /* MBIMPingPongWebSocket.h */,
|
||||||
|
CDA64B462DFCBF3000E9B07E /* MBIMPingPongWebSocket.m */,
|
||||||
);
|
);
|
||||||
path = Bridge;
|
path = Bridge;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -877,6 +882,7 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
CDA64B472DFCBF3000E9B07E /* MBIMPingPongWebSocket.m in Sources */,
|
||||||
CD14F1AD219FFAE100E7DD22 /* MBIMConcurrentHTTPServer.m in Sources */,
|
CD14F1AD219FFAE100E7DD22 /* MBIMConcurrentHTTPServer.m in Sources */,
|
||||||
1AA43E91219EBC2C00EDF1A7 /* MBIMHTTPConnection.m in Sources */,
|
1AA43E91219EBC2C00EDF1A7 /* MBIMHTTPConnection.m in Sources */,
|
||||||
CDF62339219A8A5600690038 /* MBIMBridge.h in Sources */,
|
CDF62339219A8A5600690038 /* MBIMBridge.h in Sources */,
|
||||||
|
|||||||
17
kordophone/Bridge/MBIMPingPongWebSocket.h
Normal file
17
kordophone/Bridge/MBIMPingPongWebSocket.h
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
//
|
||||||
|
// MBIMPingPongWebSocket.h
|
||||||
|
// kordophoned
|
||||||
|
//
|
||||||
|
// Created by James Magahern on 6/13/25.
|
||||||
|
// Copyright © 2025 James Magahern. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <CocoaHTTPServer/WebSocket.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface MBIMPingPongWebSocket : WebSocket
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
242
kordophone/Bridge/MBIMPingPongWebSocket.m
Normal file
242
kordophone/Bridge/MBIMPingPongWebSocket.m
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
//
|
||||||
|
// MBIMPingPongWebSocket.m
|
||||||
|
// kordophoned
|
||||||
|
//
|
||||||
|
// Created by James Magahern on 6/13/25.
|
||||||
|
// Copyright © 2025 James Magahern. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MBIMPingPongWebSocket.h"
|
||||||
|
#import "GCDAsyncSocket.h"
|
||||||
|
|
||||||
|
// WebSocket opcodes (from the base class)
|
||||||
|
#define TIMEOUT_NONE -1
|
||||||
|
#define TIMEOUT_REQUEST_BODY 10
|
||||||
|
|
||||||
|
#define TAG_HTTP_REQUEST_BODY 100
|
||||||
|
#define TAG_HTTP_RESPONSE_HEADERS 200
|
||||||
|
#define TAG_HTTP_RESPONSE_BODY 201
|
||||||
|
|
||||||
|
#define TAG_PREFIX 300
|
||||||
|
#define TAG_MSG_PLUS_SUFFIX 301
|
||||||
|
#define TAG_MSG_WITH_LENGTH 302
|
||||||
|
#define TAG_MSG_MASKING_KEY 303
|
||||||
|
#define TAG_PAYLOAD_PREFIX 304
|
||||||
|
#define TAG_PAYLOAD_LENGTH 305
|
||||||
|
#define TAG_PAYLOAD_LENGTH16 306
|
||||||
|
#define TAG_PAYLOAD_LENGTH64 307
|
||||||
|
|
||||||
|
#define WS_OP_CONTINUATION_FRAME 0
|
||||||
|
#define WS_OP_TEXT_FRAME 1
|
||||||
|
#define WS_OP_BINARY_FRAME 2
|
||||||
|
#define WS_OP_CONNECTION_CLOSE 8
|
||||||
|
#define WS_OP_PING 9
|
||||||
|
#define WS_OP_PONG 10
|
||||||
|
|
||||||
|
// Additional tags for ping/pong handling
|
||||||
|
#define TAG_PING_PAYLOAD 400
|
||||||
|
#define TAG_PONG_SENT 401
|
||||||
|
|
||||||
|
@interface WebSocket ()
|
||||||
|
- (void)socket:(GCDAsyncSocket *)asyncSocket didReadData:(NSData *)data withTag:(long)tag;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation MBIMPingPongWebSocket
|
||||||
|
{
|
||||||
|
NSData *pendingPingPayload;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Ping/Pong Frame Construction
|
||||||
|
|
||||||
|
- (NSData *)createPongFrameWithPayload:(NSData *)pingPayload {
|
||||||
|
NSMutableData *frame = [NSMutableData data];
|
||||||
|
|
||||||
|
// First byte: FIN (1) + RSV (000) + Opcode (1010 = 0xA for Pong)
|
||||||
|
uint8_t firstByte = 0x8A; // 10001010 in binary
|
||||||
|
[frame appendBytes:&firstByte length:1];
|
||||||
|
|
||||||
|
// Second byte: MASK (0) + Payload Length
|
||||||
|
NSUInteger payloadLength = [pingPayload length];
|
||||||
|
|
||||||
|
if (payloadLength <= 125) {
|
||||||
|
// Short payload length (0-125 bytes)
|
||||||
|
uint8_t secondByte = (uint8_t)payloadLength; // MASK bit is 0 for server-to-client
|
||||||
|
[frame appendBytes:&secondByte length:1];
|
||||||
|
}
|
||||||
|
else if (payloadLength <= 65535) {
|
||||||
|
// Medium payload length (126-65535 bytes)
|
||||||
|
uint8_t secondByte = 126;
|
||||||
|
[frame appendBytes:&secondByte length:1];
|
||||||
|
|
||||||
|
// Add 16-bit length in network byte order (big-endian)
|
||||||
|
uint16_t extendedLength = htons((uint16_t)payloadLength);
|
||||||
|
[frame appendBytes:&extendedLength length:2];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Large payload length (65536+ bytes) - rarely needed for pings
|
||||||
|
uint8_t secondByte = 127;
|
||||||
|
[frame appendBytes:&secondByte length:1];
|
||||||
|
|
||||||
|
// Add 64-bit length in network byte order (big-endian)
|
||||||
|
uint64_t extendedLength = OSSwapHostToBigInt64(payloadLength);
|
||||||
|
[frame appendBytes:&extendedLength length:8];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append the payload (copied exactly from ping)
|
||||||
|
if (payloadLength > 0) {
|
||||||
|
[frame appendData:pingPayload];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [frame copy];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)sendPongWithPayload:(NSData *)payload socket:(GCDAsyncSocket *)asyncSocket {
|
||||||
|
NSData *pongFrame = [self createPongFrameWithPayload:payload];
|
||||||
|
|
||||||
|
// Send the pong frame directly through the socket
|
||||||
|
[asyncSocket writeData:pongFrame withTimeout:TIMEOUT_NONE tag:TAG_PONG_SENT];
|
||||||
|
|
||||||
|
NSLog(@"Sent pong frame with payload length: %lu", (unsigned long)[payload length]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Override AsyncSocket Delegate
|
||||||
|
|
||||||
|
- (void)socket:(GCDAsyncSocket *)asyncSocket didReadData:(NSData *)data withTag:(long)tag {
|
||||||
|
NSUInteger nextOpCode = [[self valueForKey:@"nextOpCode"] unsignedIntValue]; // sheesh.
|
||||||
|
|
||||||
|
// Handle our custom ping/pong tags
|
||||||
|
if (tag == TAG_PING_PAYLOAD) {
|
||||||
|
// We've read the ping payload, now send the pong response
|
||||||
|
[self sendPongWithPayload:data socket:asyncSocket];
|
||||||
|
|
||||||
|
// Continue reading the next frame
|
||||||
|
[asyncSocket readDataToLength:1 withTimeout:TIMEOUT_NONE tag:TAG_PAYLOAD_PREFIX];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tag == TAG_PONG_SENT) {
|
||||||
|
// Pong was sent successfully, continue normal operation
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For TAG_PAYLOAD_PREFIX, we need to intercept and handle ping frames
|
||||||
|
if (tag == TAG_PAYLOAD_PREFIX) {
|
||||||
|
UInt8 *pFrame = (UInt8 *)[data bytes];
|
||||||
|
UInt8 frame = *pFrame;
|
||||||
|
|
||||||
|
// Check if this is a valid WebSocket frame
|
||||||
|
if (![self isValidWebSocketFrame:frame]) {
|
||||||
|
[self didClose];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSUInteger opcode = frame & 0x0F;
|
||||||
|
|
||||||
|
// Handle ping frames specially
|
||||||
|
if (opcode == WS_OP_PING) {
|
||||||
|
// Read the payload length byte next
|
||||||
|
[asyncSocket readDataToLength:1 withTimeout:TIMEOUT_NONE tag:TAG_PAYLOAD_LENGTH];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle pong frames (just log and continue)
|
||||||
|
if (opcode == WS_OP_PONG) {
|
||||||
|
NSLog(@"Received pong frame");
|
||||||
|
// Read the payload length byte next, but we'll ignore pong payloads
|
||||||
|
[asyncSocket readDataToLength:1 withTimeout:TIMEOUT_NONE tag:TAG_PAYLOAD_LENGTH];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For TAG_PAYLOAD_LENGTH, check if we're handling a ping
|
||||||
|
if (tag == TAG_PAYLOAD_LENGTH) {
|
||||||
|
UInt8 frame = *(UInt8 *)[data bytes];
|
||||||
|
BOOL masked = (frame & 0x80) != 0;
|
||||||
|
NSUInteger length = frame & 0x7F;
|
||||||
|
|
||||||
|
// If we were processing a ping frame, handle the payload reading
|
||||||
|
if (nextOpCode == WS_OP_PING) {
|
||||||
|
if (length <= 125) {
|
||||||
|
if (masked) {
|
||||||
|
// Read masking key first, then payload
|
||||||
|
[asyncSocket readDataToLength:4 withTimeout:TIMEOUT_NONE tag:TAG_MSG_MASKING_KEY];
|
||||||
|
}
|
||||||
|
// Read ping payload
|
||||||
|
[asyncSocket readDataToLength:length withTimeout:TIMEOUT_NONE tag:TAG_PING_PAYLOAD];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (length == 126) {
|
||||||
|
[asyncSocket readDataToLength:2 withTimeout:TIMEOUT_NONE tag:TAG_PAYLOAD_LENGTH16];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
[asyncSocket readDataToLength:8 withTimeout:TIMEOUT_NONE tag:TAG_PAYLOAD_LENGTH64];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we were processing a pong frame, just skip it
|
||||||
|
if (nextOpCode == WS_OP_PONG) {
|
||||||
|
if (length <= 125) {
|
||||||
|
if (masked) {
|
||||||
|
[asyncSocket readDataToLength:4 withTimeout:TIMEOUT_NONE tag:TAG_MSG_MASKING_KEY];
|
||||||
|
}
|
||||||
|
// Skip pong payload and continue to next frame
|
||||||
|
[asyncSocket readDataToLength:length withTimeout:TIMEOUT_NONE tag:TAG_PAYLOAD_PREFIX];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (length == 126) {
|
||||||
|
[asyncSocket readDataToLength:2 withTimeout:TIMEOUT_NONE tag:TAG_PAYLOAD_LENGTH16];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
[asyncSocket readDataToLength:8 withTimeout:TIMEOUT_NONE tag:TAG_PAYLOAD_LENGTH64];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle masking key reading for ping frames
|
||||||
|
if (tag == TAG_MSG_MASKING_KEY && nextOpCode == WS_OP_PING) {
|
||||||
|
// Store the masking key and read the actual payload
|
||||||
|
pendingPingPayload = [data copy];
|
||||||
|
// The payload length was already determined, read it
|
||||||
|
// Note: This is a simplified version - you'd need to track the actual length
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For all other cases, call the parent implementation
|
||||||
|
[super socket:asyncSocket didReadData:data withTag:tag];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Helper Methods
|
||||||
|
|
||||||
|
// Expose the isValidWebSocketFrame method since it's private in the parent
|
||||||
|
- (BOOL)isValidWebSocketFrame:(UInt8)frame {
|
||||||
|
NSUInteger rsv = frame & 0x70;
|
||||||
|
NSUInteger opcode = frame & 0x0F;
|
||||||
|
if (rsv || (3 <= opcode && opcode <= 7) || (0xB <= opcode && opcode <= 0xF)) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override didReceiveMessage to add logging
|
||||||
|
- (void)didReceiveMessage:(NSString *)msg {
|
||||||
|
NSLog(@"Received message: %@", msg);
|
||||||
|
[super didReceiveMessage:msg];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override didOpen to add logging
|
||||||
|
- (void)didOpen {
|
||||||
|
NSLog(@"WebSocket opened with ping/pong support");
|
||||||
|
[super didOpen];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)didClose {
|
||||||
|
NSLog(@"WebSocket closed");
|
||||||
|
[super didClose];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
@@ -11,6 +11,7 @@
|
|||||||
#import "IMChat+Encoded.h"
|
#import "IMChat+Encoded.h"
|
||||||
#import "MBIMHTTPConnection.h"
|
#import "MBIMHTTPConnection.h"
|
||||||
#import "MBIMURLUtilities.h"
|
#import "MBIMURLUtilities.h"
|
||||||
|
#import "MBIMPingPongWebSocket.h"
|
||||||
|
|
||||||
#import <CocoaHTTPServer/GCDAsyncSocket.h>
|
#import <CocoaHTTPServer/GCDAsyncSocket.h>
|
||||||
#import <CocoaHTTPServer/HTTPMessage.h>
|
#import <CocoaHTTPServer/HTTPMessage.h>
|
||||||
@@ -105,7 +106,7 @@ static const NSUInteger kUpdateItemsCullingLength = 100;
|
|||||||
|
|
||||||
- (WebSocket *)vendUpdateWebSocketConsumerForRequest:(HTTPMessage *)request socket:(GCDAsyncSocket *)gcdSocket
|
- (WebSocket *)vendUpdateWebSocketConsumerForRequest:(HTTPMessage *)request socket:(GCDAsyncSocket *)gcdSocket
|
||||||
{
|
{
|
||||||
WebSocket *socket = [[WebSocket alloc] initWithRequest:request socket:gcdSocket];
|
WebSocket *socket = [[MBIMPingPongWebSocket alloc] initWithRequest:request socket:gcdSocket];
|
||||||
socket.delegate = self;
|
socket.delegate = self;
|
||||||
|
|
||||||
MBIMUpdateConsumer consumer = ^(NSArray<MBIMUpdateItem *> *updates) {
|
MBIMUpdateConsumer consumer = ^(NSArray<MBIMUpdateItem *> *updates) {
|
||||||
|
|||||||
Reference in New Issue
Block a user