├── CoreWebSocket ├── en.lproj │ └── InfoPlist.strings ├── CoreWebSocket-Prefix.pch ├── CoreWebSocket-Info.plist ├── cuEnc64.h ├── CoreWebSocket.h ├── WebSocketClient.h ├── main.c ├── WebSocket.h ├── CoreWebSocket.1 ├── WebSocketTypes.h ├── cuEnc64.c ├── WebSocketFrame.h ├── WebSocket.c ├── WebSocketFrame.c └── WebSocketClient.c ├── .gitignore ├── CoreWebSocket.xcodeproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── project.pbxproj ├── public └── test.html └── Readme.md /CoreWebSocket/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | build 3 | *.xcodeproj/project.xcworkspace/xcuserdata 4 | *.xcodeproj/xcuserdata 5 | *.xcodeproj/*.mode1v3 6 | *.xcodeproj/*.pbxuser 7 | passenger.* -------------------------------------------------------------------------------- /CoreWebSocket/CoreWebSocket-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'CoreWebSocket' target in the 'CoreWebSocket' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /CoreWebSocket.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CoreWebSocket/CoreWebSocket-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | com.github.mirek.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | FMWK 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | NSHumanReadableCopyright 26 | Copyright © 2011 Inteliv Ltd. All rights reserved. 27 | NSPrincipalClass 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /CoreWebSocket/cuEnc64.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1998-2003 Apple Computer, Inc. All Rights Reserved. 3 | * 4 | * The contents of this file constitute Original Code as defined in and are 5 | * subject to the Apple Public Source License Version 1.2 (the 'License'). 6 | * You may not use this file except in compliance with the License. Please 7 | * obtain a copy of the License at http://www.apple.com/publicsource and 8 | * read it before using this file. 9 | * 10 | * This Original Code and all software distributed under the License are 11 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 12 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 13 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 14 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 15 | * Please see the License for the specific language governing rights and 16 | * limitations under the License. 17 | * 18 | * cuEnc64.h - encode/decode in 64-char IA5 format, per RFC 1421 19 | */ 20 | 21 | #ifndef _CU_ENC64_H_ 22 | #define _CU_ENC64_H_ 23 | 24 | #ifdef __cplusplus 25 | extern "C" { 26 | #endif 27 | 28 | /* 29 | * Given input buffer inbuf, length inlen, decode from 64-char IA5 format to 30 | * binary. Result is malloced and returned; its length is returned in *outlen. 31 | * NULL return indicates corrupted input. 32 | */ 33 | unsigned char *cuEnc64(const unsigned char *inbuf, 34 | unsigned inlen, 35 | unsigned *outlen); // RETURNED 36 | 37 | /* 38 | * Enc64, with embedded newlines every lineLen in result. A newline is 39 | * the UNIX \n. Result is mallocd. 40 | */ 41 | unsigned char *cuEnc64WithLines(const unsigned char *inbuf, 42 | unsigned inlen, 43 | unsigned linelen, 44 | unsigned *outlen); // RETURNED 45 | 46 | #ifdef __cplusplus 47 | } 48 | #endif 49 | 50 | #endif /*_CU_ENC64_H_*/ 51 | -------------------------------------------------------------------------------- /CoreWebSocket/CoreWebSocket.h: -------------------------------------------------------------------------------- 1 | // 2 | // The MIT License 3 | // 4 | // Copyright (c) 2011 - 2013, Mirek Rusin 5 | // http://github.com/mirek/CoreWebSocket 6 | // 7 | // Permission to use, copy, modify, and/or distribute this software for any 8 | // purpose with or without fee is hereby granted, provided that the above 9 | // copyright notice and this permission notice appear in all copies. 10 | // 11 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | // 19 | 20 | #ifndef __CORE_WEB_SOCKET_CORE_WEB_SOCKET_H__ 21 | #define __CORE_WEB_SOCKET_CORE_WEB_SOCKET_H__ 1 22 | 23 | #define kWebSocketDefaultPort 80 24 | #define kWebSocketDefaultSecurePort 443 25 | 26 | #define kWebSocketScheme "ws" 27 | #define kWebSocketSecureScheme "wss" 28 | 29 | #include 30 | #include 31 | #include 32 | 33 | #import 34 | 35 | #if (TARGET_OS_IPHONE) 36 | #include 37 | #else 38 | #include 39 | //#include 40 | //#include 41 | #endif 42 | 43 | #include 44 | #include 45 | #include 46 | 47 | #include "CoreWebSocket/WebSocketTypes.h" 48 | #include "CoreWebSocket/WebSocket.h" 49 | #include "CoreWebSocket/WebSocketClient.h" 50 | #include "CoreWebSocket/cuEnc64.h" 51 | 52 | #endif 53 | 54 | -------------------------------------------------------------------------------- /public/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CoreWebSocket Example 5 | 6 | 7 | 8 |
9 | Position of light gray box is calculated on the server side in Objective-C and sent back to this HTML page. 10 |
11 | 12 |
13 |
14 | 15 | 50 | 51 | -------------------------------------------------------------------------------- /CoreWebSocket/WebSocketClient.h: -------------------------------------------------------------------------------- 1 | // 2 | // The MIT License 3 | // 4 | // Copyright (c) 2011 - 2013, Mirek Rusin 5 | // http://github.com/mirek/CoreWebSocket 6 | // 7 | // Permission to use, copy, modify, and/or distribute this software for any 8 | // purpose with or without fee is hereby granted, provided that the above 9 | // copyright notice and this permission notice appear in all copies. 10 | // 11 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | // 19 | 20 | #ifndef __CORE_WEB_SOCKET_WEB_SOCKET_CLIENT_H__ 21 | #define __CORE_WEB_SOCKET_WEB_SOCKET_CLIENT_H__ 1 22 | 23 | #include 24 | #include "WebSocket.h" 25 | #include "WebSocketFrame.h" 26 | #include "cuEnc64.h" 27 | 28 | #pragma mark Lifecycle 29 | 30 | WebSocketClientRef WebSocketClientCreate (WebSocketRef webSocket, CFSocketNativeHandle handle); 31 | void WebSocketClientRetain (WebSocketClientRef self); 32 | void WebSocketClientRelease (WebSocketClientRef self); 33 | 34 | #pragma mark Write 35 | 36 | CFIndex WebSocketClientWriteWithData (WebSocketClientRef self, CFDataRef value); 37 | CFIndex WebSocketClientWriteWithString (WebSocketClientRef self, CFStringRef value); 38 | CFIndex WebSocketClientWriteWithFormat (WebSocketClientRef self, CFStringRef fmt, ...) CF_FORMAT_FUNCTION(2,0); 39 | 40 | #pragma mark Handshake (internal) 41 | 42 | uint32_t __WebSocketGetMagicNumberWithKeyValueString (CFStringRef string); 43 | Boolean __WebSocketDataAppendMagickNumberWithKeyValueString (CFMutableDataRef data, CFStringRef string); 44 | CFDataRef __WebSocketCreateMD5Data (CFAllocatorRef allocator, CFDataRef value) CF_RETURNS_RETAINED; 45 | CFDataRef __WebSocketCreateSHA1DataWithData (CFAllocatorRef allocator, CFDataRef value) CF_RETURNS_RETAINED; 46 | CFDataRef __WebSocketCreateSHA1DataWithString (CFAllocatorRef allocator, CFStringRef value, CFStringEncoding encoding) CF_RETURNS_RETAINED; 47 | Boolean __WebSocketClientReadHandShake (WebSocketClientRef client); 48 | Boolean __WebSocketClientWriteWithHTTPMessage (WebSocketClientRef client, CFHTTPMessageRef message); 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /CoreWebSocket/main.c: -------------------------------------------------------------------------------- 1 | // 2 | // The MIT License 3 | // 4 | // Copyright (c) 2011 - 2013, Mirek Rusin 5 | // http://github.com/mirek/CoreWebSocket 6 | // 7 | // Permission to use, copy, modify, and/or distribute this software for any 8 | // purpose with or without fee is hereby granted, provided that the above 9 | // copyright notice and this permission notice appear in all copies. 10 | // 11 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | // 19 | 20 | #include 21 | #include 22 | 23 | #include "WebSocket.h" 24 | #include "WebSocketFrame.h" 25 | 26 | #define FPS 60 27 | 28 | CGPoint box1 = { 0, 0 }; 29 | CGPoint box2 = { 0, 0 }; 30 | 31 | void 32 | Callback (WebSocketRef self, WebSocketClientRef client, CFStringRef value) { 33 | if (value) { 34 | char *buffer = malloc(1024); 35 | if (CFStringGetCString(value, buffer, 1024, kCFStringEncodingUTF8)) { 36 | sscanf(buffer, "%lf %lf", &box1.x, &box1.y); 37 | } 38 | free(buffer); 39 | } 40 | } 41 | 42 | void 43 | TimerCallback (CFRunLoopTimerRef timer, void *info) { 44 | WebSocketRef self = (WebSocketRef) info; 45 | 46 | box2.x += (box1.x - box2.x) * 0.1; 47 | box2.y += (box1.y - box2.y) * 0.1; 48 | 49 | for (CFIndex i = 0; i < WebSocketGetClientCount(self); ++i) { 50 | WebSocketClientRef client = WebSocketGetClientAtIndex(self, i); 51 | WebSocketClientWriteWithFormat(client, CFSTR("[%lf, %lf]"), box2.x, box2.y); 52 | } 53 | } 54 | 55 | int 56 | main (int argc, const char *argv[]) { 57 | WebSocketRef webSocket = WebSocketCreateWithHostAndPort(NULL, kWebSocketHostAny, 6001, NULL); 58 | if (webSocket) { 59 | 60 | printf("Running on 0.0.0.0:6001... Open public/test.html file."); 61 | 62 | webSocket->callbacks.didClientReadCallback = Callback; 63 | 64 | // Send some data periodically to the web page. 65 | CFRunLoopTimerContext context = { 0, webSocket, NULL, NULL, NULL }; 66 | CFRunLoopTimerRef timer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent(), 1.0 / FPS, 0, 0, TimerCallback, &context); 67 | CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopCommonModes); 68 | 69 | CFRunLoopRun(); 70 | 71 | WebSocketRelease(webSocket); 72 | } 73 | return 0; 74 | } 75 | 76 | -------------------------------------------------------------------------------- /CoreWebSocket/WebSocket.h: -------------------------------------------------------------------------------- 1 | // 2 | // The MIT License 3 | // 4 | // Copyright (c) 2011 - 2013, Mirek Rusin 5 | // http://github.com/mirek/CoreWebSocket 6 | // 7 | // Permission to use, copy, modify, and/or distribute this software for any 8 | // purpose with or without fee is hereby granted, provided that the above 9 | // copyright notice and this permission notice appear in all copies. 10 | // 11 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | // 19 | 20 | #ifndef __CORE_WEB_SOCKET_WEB_SOCKET__ 21 | #define __CORE_WEB_SOCKET_WEB_SOCKET__ 1 22 | 23 | #include "CoreWebSocket/CoreWebSocket.h" 24 | 25 | #define __WebSocketMaxHeaderKeyLength 4096 26 | 27 | #pragma mark Lifecycle 28 | 29 | WebSocketRef WebSocketCreateWithHostAndPort (CFAllocatorRef allocator, CFStringRef host, UInt16 port, void *userInfo); 30 | WebSocketRef WebSocketCreate (CFAllocatorRef allocator, void *userInfo); 31 | 32 | void WebSocketDealloc (WebSocketRef self); 33 | void WebSocketRetain (WebSocketRef self); 34 | void WebSocketRelease (WebSocketRef self); 35 | 36 | CFStringRef WebSocketCopyHostString (WebSocketRef self) CF_RETURNS_RETAINED; 37 | UInt16 WebSocketGetPort (WebSocketRef self); 38 | 39 | void WebSocketConnectWithHostAndPort(WebSocketRef webSocket, CFStringRef hostOrAddress, UInt32 port); 40 | 41 | void WebSocketWriteWithString (WebSocketRef webSocket, CFStringRef value); 42 | CFIndex WebSocketWriteWithStringAndClientIndex (WebSocketRef webSocket, CFStringRef value, CFIndex index); 43 | 44 | WebSocketClientRef 45 | WebSocketGetClientAtIndex (WebSocketRef self, CFIndex index); 46 | 47 | CFIndex 48 | WebSocketGetClientCount (WebSocketRef self); 49 | 50 | #pragma mark Callbacks 51 | 52 | void WebSocketSetClientReadCallback (WebSocketRef self, WebSocketDidClientReadCallback callback); 53 | 54 | #pragma mark Internal, client management 55 | 56 | CFIndex __WebSocketAppendClient (WebSocketRef webSocket, WebSocketClientRef client); 57 | CFIndex __WebSocketRemoveClient (WebSocketRef webSocket, WebSocketClientRef client); 58 | 59 | #pragma mark Internal, socket callback 60 | 61 | void __WebSocketAcceptCallBack(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *info); 62 | 63 | #endif 64 | 65 | -------------------------------------------------------------------------------- /CoreWebSocket/CoreWebSocket.1: -------------------------------------------------------------------------------- 1 | .\"Modified from man(1) of FreeBSD, the NetBSD mdoc.template, and mdoc.samples. 2 | .\"See Also: 3 | .\"man mdoc.samples for a complete listing of options 4 | .\"man mdoc for the short list of editing options 5 | .\"/usr/share/misc/mdoc.template 6 | .Dd 07/03/2011 \" DATE 7 | .Dt WebSocketCore 1 \" Program name and manual section number 8 | .Os Darwin 9 | .Sh NAME \" Section Header - required - don't modify 10 | .Nm WebSocketCore, 11 | .\" The following lines are read in generating the apropos(man -k) database. Use only key 12 | .\" words here as the database is built based on the words here and in the .ND line. 13 | .Nm Other_name_for_same_program(), 14 | .Nm Yet another name for the same program. 15 | .\" Use .Nm macro to designate other names for the documented program. 16 | .Nd This line parsed for whatis database. 17 | .Sh SYNOPSIS \" Section Header - required - don't modify 18 | .Nm 19 | .Op Fl abcd \" [-abcd] 20 | .Op Fl a Ar path \" [-a path] 21 | .Op Ar file \" [file] 22 | .Op Ar \" [file ...] 23 | .Ar arg0 \" Underlined argument - use .Ar anywhere to underline 24 | arg2 ... \" Arguments 25 | .Sh DESCRIPTION \" Section Header - required - don't modify 26 | Use the .Nm macro to refer to your program throughout the man page like such: 27 | .Nm 28 | Underlining is accomplished with the .Ar macro like this: 29 | .Ar underlined text . 30 | .Pp \" Inserts a space 31 | A list of items with descriptions: 32 | .Bl -tag -width -indent \" Begins a tagged list 33 | .It item a \" Each item preceded by .It macro 34 | Description of item a 35 | .It item b 36 | Description of item b 37 | .El \" Ends the list 38 | .Pp 39 | A list of flags and their descriptions: 40 | .Bl -tag -width -indent \" Differs from above in tag removed 41 | .It Fl a \"-a flag as a list item 42 | Description of -a flag 43 | .It Fl b 44 | Description of -b flag 45 | .El \" Ends the list 46 | .Pp 47 | .\" .Sh ENVIRONMENT \" May not be needed 48 | .\" .Bl -tag -width "ENV_VAR_1" -indent \" ENV_VAR_1 is width of the string ENV_VAR_1 49 | .\" .It Ev ENV_VAR_1 50 | .\" Description of ENV_VAR_1 51 | .\" .It Ev ENV_VAR_2 52 | .\" Description of ENV_VAR_2 53 | .\" .El 54 | .Sh FILES \" File used or created by the topic of the man page 55 | .Bl -tag -width "/Users/joeuser/Library/really_long_file_name" -compact 56 | .It Pa /usr/share/file_name 57 | FILE_1 description 58 | .It Pa /Users/joeuser/Library/really_long_file_name 59 | FILE_2 description 60 | .El \" Ends the list 61 | .\" .Sh DIAGNOSTICS \" May not be needed 62 | .\" .Bl -diag 63 | .\" .It Diagnostic Tag 64 | .\" Diagnostic informtion here. 65 | .\" .It Diagnostic Tag 66 | .\" Diagnostic informtion here. 67 | .\" .El 68 | .Sh SEE ALSO 69 | .\" List links in ascending order by section, alphabetically within a section. 70 | .\" Please do not reference files that do not exist without filing a bug report 71 | .Xr a 1 , 72 | .Xr b 1 , 73 | .Xr c 1 , 74 | .Xr a 2 , 75 | .Xr b 2 , 76 | .Xr a 3 , 77 | .Xr b 3 78 | .\" .Sh BUGS \" Document known, unremedied bugs 79 | .\" .Sh HISTORY \" Document history if command behaves in a unique manner -------------------------------------------------------------------------------- /CoreWebSocket/WebSocketTypes.h: -------------------------------------------------------------------------------- 1 | // 2 | // The MIT License 3 | // 4 | // Copyright (c) 2011 - 2013, Mirek Rusin 5 | // http://github.com/mirek/CoreWebSocket 6 | // 7 | // Permission to use, copy, modify, and/or distribute this software for any 8 | // purpose with or without fee is hereby granted, provided that the above 9 | // copyright notice and this permission notice appear in all copies. 10 | // 11 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | // 19 | 20 | #ifndef __CORE_WEB_SOCKET_WEB_SOCKET_TYPES__ 21 | #define __CORE_WEB_SOCKET_WEB_SOCKET_TYPES__ 1 22 | 23 | #include 24 | #include "WebSocketFrame.h" 25 | 26 | #define WebSocketLog(fmt, ...) printf(fmt, __VA_ARGS__) 27 | 28 | #define kWebSocketHostAny CFSTR("0.0.0.0") 29 | #define kWebSocketHostLoopBack CFSTR("127.0.0.1") 30 | #define kWebSocketPortAny 0 31 | 32 | typedef struct WebSocket WebSocket; 33 | typedef WebSocket *WebSocketRef; 34 | 35 | typedef struct WebSocketClient WebSocketClient; 36 | typedef WebSocketClient *WebSocketClientRef; 37 | 38 | #pragma mark WebSocket Protocol 39 | 40 | typedef enum WebSocketProtocol WebSocketProtocol; 41 | 42 | enum WebSocketProtocol { 43 | kWebSocketProtocolUnknown = -1, 44 | kWebSocketProtocolDraftIETF_HYBI_00 = 0, 45 | kWebSocketProtocolDraftIETF_HYBI_06 = 6, 46 | kWebSocketProtocol_RFC6455_13 = 13 47 | }; 48 | 49 | #pragma mark WebSocket Callbacks 50 | 51 | typedef void (*WebSocketDidAddClientCallback) (WebSocketRef webSocket, WebSocketClientRef client); 52 | typedef void (*WebSocketWillRemoveClientCallback) (WebSocketRef webSocket, WebSocketClientRef client); 53 | typedef void (*WebSocketDidClientReadCallback) (WebSocketRef webSocket, WebSocketClientRef client, CFStringRef value); 54 | 55 | typedef struct WebSocketCallbacks WebSocketCallbacks; 56 | 57 | struct WebSocketCallbacks { 58 | WebSocketDidAddClientCallback didAddClientCallback; 59 | WebSocketWillRemoveClientCallback willRemoveClientCallback; 60 | WebSocketDidClientReadCallback didClientReadCallback; 61 | }; 62 | 63 | #pragma mark WebSocket Client 64 | 65 | enum WebSocketClientState { 66 | kWebSocketClientInitialized, 67 | kWebSocketClientReadStreamOpened, 68 | kWebSocketClientWriteStreamOpened, 69 | kWebSocketClientHandShakeError, 70 | kWebSocketClientHandShakeRead, 71 | kWebSocketClientHandShakeSent, 72 | kWebSocketClientReady 73 | }; 74 | 75 | struct WebSocketClient { 76 | CFAllocatorRef allocator; 77 | CFIndex retainCount; 78 | WebSocketRef webSocket; 79 | CFSocketNativeHandle handle; 80 | CFReadStreamRef read; 81 | CFWriteStreamRef write; 82 | 83 | CFMutableArrayRef writeQueue; 84 | 85 | CFHTTPMessageRef handShakeRequestHTTPMessage; 86 | WebSocketProtocol protocol; 87 | 88 | // Linked list of clients 89 | WebSocketClientRef previousClient; 90 | WebSocketClientRef nextClient; 91 | 92 | CFStreamClientContext context; 93 | 94 | Boolean didReadHandShake; 95 | Boolean didWriteHandShake; 96 | 97 | WebSocketFrameRef frame; 98 | }; 99 | 100 | struct WebSocket { 101 | CFAllocatorRef allocator; 102 | CFIndex retainCount; 103 | void *userInfo; 104 | 105 | struct sockaddr_in addr; 106 | CFSocketRef socket; 107 | CFReadStreamRef read; 108 | CFWriteStreamRef write; 109 | 110 | CFIndex clientsUsedLength; 111 | CFIndex clientsLength; 112 | WebSocketClientRef *clients; 113 | 114 | WebSocketCallbacks callbacks; 115 | }; 116 | 117 | #endif 118 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # CoreWebSocket - Core Foundation based WebSocket Library for iOS/OSX 2 | 3 | CoreWebSocket is an C language, Core Foundation based library for iOS and Mac OSX. It can be used from Objective-C. 4 | 5 | WebSocket enables low latency, bi-directional, full-duplex communication channel over TCP with web browser. It works 6 | with all modern web browsers including Safari, Chrome, Firefox and Opera. It works with iOS Safari as well. 7 | 8 | ## Installation 9 | 10 | To get the library using git in your project: 11 | 12 | git submodule add git://github.com/mirek/CoreWebSocket.git CoreWebSocket 13 | 14 | Then to add the library to your project 15 | 16 | 1. Add CoreWebSocket.xcodeproj to project 17 | 2. In to targets Build Phases 18 | 1. In Target Dependencies add 19 | + CoreWebSocket 20 | 2. In Link Binary With Libraries add 21 | + CFNetwork.framework 22 | + CoreServices.framework 23 | + libcrypto.dylib 24 | + CoreWebSocket.framework 25 | 3. Add Build Phase > Add Copy Files 26 | 1. Set Destination to be Frameworks 27 | 2. Add CoreWebSocket.framework 28 | 3. Clean 29 | 4. Build CoreWebSocket 30 | 5. Build your app 31 | 32 | # Usage 33 | 34 | Exmaple AppDelegate.m 35 | 36 | 37 | #import "AppDelegate.h" 38 | #include "CoreWebSocket/CoreWebSocket.h" 39 | 40 | @implementation AppDelegate 41 | 42 | void Callback(WebSocketRef self, WebSocketClientRef client, CFStringRef value) { 43 | if (value) { 44 | CFShow(value); 45 | } 46 | } 47 | 48 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification 49 | { 50 | WebSocketRef webSocket = WebSocketCreateWithHostAndPort(NULL, kWebSocketHostAny, 6001, NULL); 51 | if (webSocket) { 52 | webSocket->callbacks.didClientReadCallback = Callback; 53 | } 54 | } 55 | 56 | @end 57 | 58 | ## Web Browser Usage 59 | 60 | 61 | 62 | 63 | 64 | 112 | 113 | 114 | 115 | ## License 116 | 117 | Unless otherwise stated, the code is released under Open Source MIT License, (c) Copyright Mirek Rusin 118 | 119 | Parts of the source code (particularly Base64 encoding functions) have been copied from http://opensource.apple.com which are released 120 | under Apple Public Source License 2.0 http://www.opensource.apple.com/apsl. 121 | 122 | Portions of the copied source code could be modified. 123 | -------------------------------------------------------------------------------- /CoreWebSocket/cuEnc64.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1998-2003 Apple Computer, Inc. All Rights Reserved. 3 | * 4 | * The contents of this file constitute Original Code as defined in and are 5 | * subject to the Apple Public Source License Version 1.2 (the 'License'). 6 | * You may not use this file except in compliance with the License. Please 7 | * obtain a copy of the License at http://www.apple.com/publicsource and 8 | * read it before using this file. 9 | * 10 | * This Original Code and all software distributed under the License are 11 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 12 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 13 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 14 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 15 | * Please see the License for the specific language governing rights and 16 | * limitations under the License. 17 | * 18 | * cuEnc64.c - encode/decode in 64-char IA5 format, per RFC 1421 19 | */ 20 | 21 | #include "cuEnc64.h" 22 | 23 | #include 24 | 25 | /* 26 | * map a 6-bit binary value to a printable character. 27 | */ 28 | static const 29 | unsigned char bintoasc[] = 30 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 31 | 32 | /* 33 | * Map an 7-bit printable character to its corresponding binary value. 34 | * Any illegal characters return high bit set. 35 | */ 36 | static const 37 | unsigned char asctobin[] = 38 | { 39 | 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 40 | 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 41 | 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 42 | 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 43 | 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 44 | 0x80, 0x80, 0x80, 0x3e, 0x80, 0x80, 0x80, 0x3f, 45 | 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 46 | 0x3c, 0x3d, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 47 | 0x80, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 48 | 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 49 | 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 50 | 0x17, 0x18, 0x19, 0x80, 0x80, 0x80, 0x80, 0x80, 51 | 0x80, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 52 | 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 53 | 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 54 | 0x31, 0x32, 0x33, 0x80, 0x80, 0x80, 0x80, 0x80 55 | }; 56 | 57 | /* 58 | * map 6 bits to a printing char 59 | */ 60 | #define ENC(c) (bintoasc[((c) & 0x3f)]) 61 | 62 | #define PAD '=' 63 | 64 | /* 65 | * map one group of up to 3 bytes at inp to 4 bytes at outp. 66 | * Count is number of valid bytes in *inp; if less than 3, the 67 | * 1 or two extras must be zeros. 68 | */ 69 | static void encChunk(const unsigned char *inp, 70 | unsigned char *outp, 71 | int count) 72 | { 73 | unsigned char c1, c2, c3, c4; 74 | 75 | c1 = *inp >> 2; 76 | c2 = ((inp[0] << 4) & 0x30) | ((inp[1] >> 4) & 0xf); 77 | c3 = ((inp[1] << 2) & 0x3c) | ((inp[2] >> 6) & 0x3); 78 | c4 = inp[2] & 0x3f; 79 | *outp++ = ENC(c1); 80 | *outp++ = ENC(c2); 81 | if (count == 1) { 82 | *outp++ = PAD; 83 | *outp = PAD; 84 | } else { 85 | *outp++ = ENC(c3); 86 | if (count == 2) { 87 | *outp = PAD; 88 | } 89 | else { 90 | *outp = ENC(c4); 91 | } 92 | } 93 | } 94 | 95 | /* 96 | * Given input buffer inbuf, length inlen, encode to 64-char IA5 format. 97 | * Result is fmalloc'd and returned; it is terminated by Microsoft-style 98 | * newline and NULL. Its length (including the trailing newline and NULL) 99 | * is returned in *outlen. 100 | */ 101 | 102 | unsigned char *cuEnc64(const unsigned char *inbuf, 103 | unsigned inlen, 104 | unsigned *outlen) // RETURNED 105 | { 106 | return cuEnc64WithLines(inbuf, inlen, 0, outlen); 107 | } 108 | 109 | unsigned char *cuEnc64WithLines(const unsigned char *inbuf, 110 | unsigned inlen, 111 | unsigned linelen, 112 | unsigned *outlen) 113 | { 114 | unsigned outTextLen; 115 | unsigned len; // to malloc, liberal 116 | unsigned olen = 0; // actual output size 117 | unsigned char *outbuf; 118 | unsigned char endbuf[3]; 119 | unsigned i; 120 | unsigned char *outp; 121 | unsigned numLines; 122 | unsigned thisLine; 123 | 124 | outTextLen = ((inlen + 2) / 3) * 4; 125 | if(linelen) { 126 | /* 127 | * linelen must be 0 mod 4 for this to work; round up... 128 | */ 129 | if((linelen & 0x03) != 0) { 130 | linelen = (linelen + 3) & 0xfffffffc; 131 | } 132 | numLines = (outTextLen + linelen - 1)/ linelen; 133 | } 134 | else { 135 | numLines = 1; 136 | } 137 | 138 | /* 139 | * Total output size = encoded text size plus one newline per 140 | * line of output, plus trailing NULL. We always generate newlines 141 | * as \n; when decoding, we tolerate \r\n (Microsoft) or \n. 142 | */ 143 | len = outTextLen + (2 * numLines) + 1; 144 | outbuf = (unsigned char*)malloc(len); 145 | outp = outbuf; 146 | thisLine = 0; 147 | 148 | while(inlen) { 149 | if(inlen < 3) { 150 | for(i=0; i<3; i++) { 151 | if(i < inlen) { 152 | endbuf[i] = inbuf[i]; 153 | } 154 | else { 155 | endbuf[i] = 0; 156 | } 157 | } 158 | encChunk(endbuf, outp, inlen); 159 | inlen = 0; 160 | } 161 | else { 162 | encChunk(inbuf, outp, 3); 163 | inlen -= 3; 164 | inbuf += 3; 165 | } 166 | outp += 4; 167 | thisLine += 4; 168 | olen += 4; 169 | if((linelen != 0) && (thisLine >= linelen) && inlen) { 170 | /* 171 | * last trailing newline added below 172 | * Note we don't split 4-byte output chunks over newlines 173 | */ 174 | *outp++ = '\n'; 175 | olen++; 176 | thisLine = 0; 177 | } 178 | } 179 | *outp++ = '\n'; 180 | *outp = '\0'; 181 | olen += 2; 182 | *outlen = olen; 183 | return outbuf; 184 | } 185 | 186 | 187 | -------------------------------------------------------------------------------- /CoreWebSocket/WebSocketFrame.h: -------------------------------------------------------------------------------- 1 | // 2 | // The MIT License 3 | // 4 | // Copyright (c) 2011 - 2013, Mirek Rusin 5 | // http://github.com/mirek/CoreWebSocket 6 | // 7 | // Permission to use, copy, modify, and/or distribute this software for any 8 | // purpose with or without fee is hereby granted, provided that the above 9 | // copyright notice and this permission notice appear in all copies. 10 | // 11 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | // 19 | 20 | #ifndef __CORE_WEB_SOCKET_WEB_SOCKET_FRAME_H__ 21 | #define __CORE_WEB_SOCKET_WEB_SOCKET_FRAME_H__ 1 22 | 23 | #include 24 | 25 | // 4 bits opcode field. 26 | enum WebSocketFrameOpCode { 27 | kWebSocketFrameOpCodeContinuation = 0, // Denotes a continuation frame (or undefined op code) 28 | kWebSocketFrameOpCodeText = 1, // Denotes a text frame 29 | kWebSocketFrameOpCodeBinary = 2, // Denotes a binary frame 30 | kWebSocketFrameOpCodeClose = 8, // Denotes a connection close 31 | kWebSocketFrameOpCodePing = 9, // Denotes a ping 32 | kWebSocketFrameOpCodePong = 10 // Denotes a pong 33 | }; 34 | 35 | typedef enum WebSocketFrameOpCode WebSocketFrameOpCode; 36 | 37 | const char *__WebSocketFrameOpCodeCString[15]; 38 | 39 | enum WebSocketFrameState { 40 | kWebSocketFrameStateNone, 41 | kWebSocketFrameState2BytesHeader, 42 | kWebSocketFrameState16BitLength, 43 | kWebSocketFrameState64BitLength, 44 | kWebSocketFrameStateMaskingKey, 45 | kWebSocketFrameStatePayload, 46 | kWebSocketFrameStateReady, 47 | kWebSocketFrameStateError 48 | }; 49 | 50 | typedef enum WebSocketFrameState WebSocketFrameState; 51 | 52 | struct WebSocketFrame { 53 | CFAllocatorRef allocator; 54 | CFIndex retainCount; 55 | 56 | WebSocketFrameState state; 57 | 58 | Boolean isFin; 59 | Boolean isRsv1; 60 | Boolean isRsv2; 61 | Boolean isRsv3; 62 | WebSocketFrameOpCode opCode; 63 | Boolean isMasked; 64 | UInt8 maskingKey[4]; 65 | UInt8 payloadOffset; 66 | UInt64 payloadLength; 67 | 68 | // Frame data, including header and payload (ext and app data). 69 | CFMutableDataRef data; 70 | }; 71 | 72 | typedef struct WebSocketFrame * WebSocketFrameRef; 73 | 74 | /** 75 | * Create new frame object. Frame object created this way can be used to append 76 | * incomming bytes. After appending bytes you can call parse and check state 77 | * to see if the frame has been finished. 78 | */ 79 | WebSocketFrameRef 80 | WebSocketFrameCreate (CFAllocatorRef allocator) CF_RETURNS_RETAINED; 81 | 82 | /** 83 | * Create new frame with information and payload data. Frames created this way 84 | * can be used to send the data. 85 | * 86 | * If isMasked flag is TRUE and maskingKey is NULL, random masking key will be used. 87 | * 88 | * Payload can be NULL for control frames. 89 | */ 90 | WebSocketFrameRef 91 | WebSocketFrameCreateWithPayloadData (CFAllocatorRef allocator, WebSocketFrameOpCode opCode, Boolean isMasked, UInt8 *maskingKey, CFDataRef payload); 92 | 93 | /** 94 | * Create new frame with information and payload data. Frames created this way 95 | * can be used to send the data. 96 | * 97 | * If isMasked flag is TRUE and maskingKey is NULL, random masking key will be used. 98 | */ 99 | WebSocketFrameRef 100 | WebSocketFrameCreateWithPayloadString (CFAllocatorRef allocator, Boolean isMasked, UInt8 *maskingKey, CFStringRef payload); 101 | 102 | /** 103 | * Create new websocket frame object with specified payload capacity. 104 | */ 105 | WebSocketFrameRef 106 | WebSocketFrameCreateWithPayloadCapacity (CFAllocatorRef allocator, CFIndex payloadCapacity); 107 | 108 | /** 109 | * Deallocate websocket frame object. Direct use of this function is not recommended, 110 | * use WebSocketFrameRelease instead. 111 | */ 112 | void 113 | WebSocketFrameDealloc (WebSocketFrameRef self); 114 | 115 | /** 116 | * Increment retain count. 117 | */ 118 | WebSocketFrameRef 119 | WebSocketFrameRetain (WebSocketFrameRef self); 120 | 121 | /** 122 | * Release websocket frame object. If the retain count reaches zero, dealloc function will be 123 | * called and all reference to the object will be invalid. 124 | */ 125 | void 126 | WebSocketFrameRelease (WebSocketFrameRef self); 127 | 128 | /** 129 | * @return TRUE if the frame has FIN bit set, FALSE otherwise. 130 | */ 131 | Boolean 132 | WebSocketFrameGetIsFin (WebSocketFrameRef self); 133 | 134 | /** 135 | * 136 | */ 137 | WebSocketFrameOpCode 138 | WebSocketFrameGetOpCode (WebSocketFrameRef self); 139 | 140 | Boolean 141 | WebSocketFrameGetIsMasked (WebSocketFrameRef self); 142 | 143 | UInt64 144 | WebSocketFrameGetPayloadLength (WebSocketFrameRef self); 145 | 146 | WebSocketFrameState 147 | WebSocketFrameGetState (WebSocketFrameRef self); 148 | 149 | void 150 | WebSocketFrameAppend (WebSocketFrameRef self, const UInt8 *bytes, CFIndex length); 151 | 152 | void 153 | WebSocketFrameReset (WebSocketFrameRef self); 154 | 155 | WebSocketFrameState 156 | WebSocketFrameParse (WebSocketFrameRef self); 157 | 158 | void 159 | WebSocketFrameGetPayload (WebSocketFrameRef self, CFRange range, UInt8 *buffer, Boolean unmask); 160 | 161 | CFStringRef 162 | WebSocketFrameCopyPayloadString (WebSocketFrameRef self, CFStringEncoding encoding); 163 | 164 | const UInt8 * 165 | WebSocketFrameGetBytesPtr (WebSocketFrameRef self); 166 | 167 | #endif 168 | -------------------------------------------------------------------------------- /CoreWebSocket/WebSocket.c: -------------------------------------------------------------------------------- 1 | // 2 | // The MIT License 3 | // 4 | // Copyright (c) 2011 - 2013, Mirek Rusin 5 | // http://github.com/mirek/CoreWebSocket 6 | // 7 | // Permission to use, copy, modify, and/or distribute this software for any 8 | // purpose with or without fee is hereby granted, provided that the above 9 | // copyright notice and this permission notice appear in all copies. 10 | // 11 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | // 19 | 20 | #include "WebSocket.h" 21 | 22 | #ifndef likely 23 | #define likely(x) __builtin_expect((x),1) 24 | #endif 25 | 26 | #ifndef unlikely 27 | #define unlikely(x) __builtin_expect((x),0) 28 | #endif 29 | 30 | #define if_likely(x) if (likely(x)) 31 | #define if_self if (likely((self) != NULL)) 32 | #define if_self_and(x) if (likely(((self) != NULL) && (x))) 33 | 34 | #pragma mark Lifecycle 35 | 36 | WebSocketRef 37 | WebSocketCreateWithHostAndPort (CFAllocatorRef allocator, CFStringRef host, UInt16 port, void *userInfo) { 38 | WebSocketRef self = CFAllocatorAllocate(allocator, sizeof(WebSocket), 0); 39 | if (self) { 40 | self->allocator = allocator ? CFRetain(allocator) : NULL; 41 | self->retainCount = 1; 42 | self->userInfo = userInfo; 43 | 44 | self->clientsLength = 1024; 45 | self->clientsUsedLength = 0; 46 | if (NULL == (self->clients = CFAllocatorAllocate(allocator, self->clientsLength, 0))) { 47 | WebSocketRelease(self), self = NULL; 48 | goto fin; 49 | } 50 | 51 | // Callbacks 52 | self->callbacks.didAddClientCallback = NULL; 53 | self->callbacks.willRemoveClientCallback = NULL; 54 | self->callbacks.didClientReadCallback = NULL; 55 | 56 | // Setup the context; 57 | CFSocketContext context = { 58 | .copyDescription = NULL, 59 | .retain = NULL, 60 | .release = NULL, 61 | .version = 0, 62 | .info = self 63 | }; 64 | 65 | if (NULL == (self->socket = CFSocketCreate(self->allocator, PF_INET, SOCK_STREAM, IPPROTO_TCP, kCFSocketAcceptCallBack, __WebSocketAcceptCallBack, &context))) { 66 | WebSocketRelease(self), self = NULL; 67 | goto fin; 68 | } 69 | 70 | // Re-use local addresses, if they're still in TIME_WAIT 71 | int yes = 1; 72 | setsockopt(CFSocketGetNative(self->socket), SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes)); 73 | 74 | // Set the port and address we want to listen on 75 | memset(&self->addr, 0, sizeof(self->addr)); 76 | self->addr.sin_len = sizeof(self->addr); 77 | self->addr.sin_family = AF_INET; 78 | 79 | if (CFEqual(kWebSocketHostAny, host)) { 80 | 81 | // Host is set to "0.0.0.0", set it to INADDR_ANY 82 | self->addr.sin_addr.s_addr = htonl(INADDR_ANY); 83 | } else { 84 | 85 | // Set the host based on provided string. TODO: hostname resolution? 86 | CFIndex hostCStringLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength(host), kCFStringEncodingASCII) + 1; 87 | char *hostCString = CFAllocatorAllocate(self->allocator, hostCStringLength, 0); 88 | if (hostCString) { 89 | if (CFStringGetCString(host, hostCString, hostCStringLength, kCFStringEncodingASCII)) { 90 | inet_aton(hostCString, &self->addr.sin_addr); 91 | } else { 92 | 93 | // TODO: Couldn't get CString 94 | } 95 | CFAllocatorDeallocate(self->allocator, hostCString); 96 | } else { 97 | 98 | // TODO: Couldn't allocate buffer 99 | } 100 | } 101 | 102 | self->addr.sin_port = htons(port); 103 | 104 | CFDataRef address = CFDataCreate(self->allocator, (const void *) &self->addr, sizeof(self->addr)); 105 | if (address) { 106 | if (CFSocketSetAddress(self->socket, (CFDataRef) address) != kCFSocketSuccess) { 107 | WebSocketRelease(self), self = NULL; 108 | CFRelease(address); 109 | goto fin; 110 | } 111 | CFRelease(address); 112 | } 113 | 114 | // Create run loop source and add it to the current run loop 115 | CFRunLoopSourceRef source = CFSocketCreateRunLoopSource(self->allocator, self->socket, 0); 116 | CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes); 117 | CFRelease(source); 118 | } 119 | 120 | fin: 121 | 122 | return self; 123 | } 124 | 125 | WebSocketRef 126 | WebSocketCreate (CFAllocatorRef allocator, void *userInfo) { 127 | return WebSocketCreateWithHostAndPort(allocator, kWebSocketHostLoopBack, kWebSocketPortAny, userInfo); 128 | } 129 | 130 | void 131 | WebSocketRetain (WebSocketRef self) { 132 | if_self { 133 | ++self->retainCount; 134 | } 135 | } 136 | 137 | void 138 | WebSocketRelease (WebSocketRef self) { 139 | if_self { 140 | if (--self->retainCount == 0) { 141 | CFAllocatorRef allocator = self->allocator; 142 | 143 | if (self->clients) { 144 | while (--self->clientsUsedLength >= 0) { 145 | WebSocketClientRelease(self->clients[self->clientsUsedLength]); 146 | } 147 | CFAllocatorDeallocate(allocator, self->clients), self->clients = NULL; 148 | } 149 | 150 | if (self->socket) { 151 | CFSocketInvalidate(self->socket); 152 | CFRelease(self->socket), self->socket = NULL; 153 | } 154 | 155 | CFAllocatorDeallocate(allocator, self), self = NULL; 156 | 157 | if (allocator) { 158 | CFRelease(allocator), allocator = NULL; 159 | } 160 | } 161 | } 162 | } 163 | 164 | UInt16 165 | WebSocketGetPort (WebSocketRef self) { 166 | UInt16 result = UINT16_MAX; 167 | if_self_and (self->socket != NULL) { 168 | struct sockaddr_in sock; 169 | socklen_t sockLength = sizeof(struct sockaddr_in); 170 | if (getsockname(CFSocketGetNative(self->socket), (struct sockaddr *) &sock, &sockLength) != -1) { 171 | result = ntohs(sock.sin_port); 172 | } 173 | } 174 | return result; 175 | } 176 | 177 | // Send string frame to all connected clients 178 | void 179 | WebSocketWriteWithString (WebSocketRef self, CFStringRef value) { 180 | if_self_and (value != NULL) { 181 | for (CFIndex i = 0; i < self->clientsUsedLength; ++i) { 182 | WebSocketWriteWithStringAndClientIndex(self, value, i); 183 | } 184 | } 185 | } 186 | 187 | CFIndex 188 | WebSocketWriteWithStringAndClientIndex (WebSocketRef self, CFStringRef value, CFIndex index) { 189 | CFIndex result = -1; 190 | if_self_and (value != NULL && index >= 0 && index < self->clientsUsedLength) { 191 | result = WebSocketClientWriteWithString(self->clients[index], value); 192 | } 193 | return result; 194 | } 195 | 196 | WebSocketClientRef 197 | WebSocketGetClientAtIndex (WebSocketRef self, CFIndex index) { 198 | WebSocketClientRef result = NULL; 199 | if_self_and (index < self->clientsUsedLength) { 200 | result = self->clients[index]; 201 | } 202 | return result; 203 | } 204 | 205 | CFIndex 206 | WebSocketGetClientCount (WebSocketRef self) { 207 | CFIndex result = 0; 208 | if_self { 209 | result = self->clientsUsedLength; 210 | } 211 | return result; 212 | } 213 | 214 | #pragma mark Callbacks 215 | 216 | void 217 | WebSocketSetClientReadCallback (WebSocketRef self, WebSocketDidClientReadCallback callback) { 218 | if_self { 219 | self->callbacks.didClientReadCallback = callback; 220 | } 221 | } 222 | 223 | #pragma mark Internal, client management 224 | 225 | CFIndex 226 | __WebSocketAppendClient (WebSocketRef self, WebSocketClientRef client) { 227 | CFIndex result = -1; 228 | if_self_and (client != NULL) { 229 | WebSocketClientRetain(client), self->clients[self->clientsUsedLength++] = client; 230 | result = self->clientsUsedLength; 231 | if (self->callbacks.didAddClientCallback) { 232 | self->callbacks.didAddClientCallback(self, client); 233 | } 234 | } 235 | return result; 236 | } 237 | 238 | CFIndex 239 | __WebSocketRemoveClient (WebSocketRef self, WebSocketClientRef client) { 240 | CFIndex result = -1; 241 | if_self_and (client != NULL) { 242 | for (CFIndex i = 0; i < self->clientsUsedLength; ++i) { 243 | if_likely (self->clients[i] == client) { 244 | 245 | // Invoke callback before removing client if defined. 246 | if (self->callbacks.willRemoveClientCallback) { 247 | self->callbacks.willRemoveClientCallback(self, client); 248 | } 249 | 250 | // Swap last client with this one. 251 | self->clients[i] = self->clients[result = --self->clientsUsedLength]; 252 | WebSocketClientRelease(client); 253 | 254 | break; 255 | } 256 | } 257 | } 258 | return result; 259 | } 260 | 261 | #pragma mark Callbacks 262 | 263 | void __WebSocketAcceptCallBack (CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *sock, void *info) { 264 | WebSocketRef self = (WebSocketRef) info; 265 | if_self { 266 | WebSocketClientRef client = WebSocketClientCreate(self, *((CFSocketNativeHandle *) sock)); 267 | printf("adding %p client\n", client); 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /CoreWebSocket/WebSocketFrame.c: -------------------------------------------------------------------------------- 1 | // 2 | // The MIT License 3 | // 4 | // Copyright (c) 2011 - 2013, Mirek Rusin 5 | // http://github.com/mirek/CoreWebSocket 6 | // 7 | // Permission to use, copy, modify, and/or distribute this software for any 8 | // purpose with or without fee is hereby granted, provided that the above 9 | // copyright notice and this permission notice appear in all copies. 10 | // 11 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | // 19 | 20 | #include 21 | #include 22 | 23 | #include "WebSocketFrame.h" 24 | 25 | #define if_self if (self) 26 | #define if_self_and(x) if ((self) && (x)) 27 | 28 | #pragma mark Private declarations 29 | 30 | UInt8 __WebSocketFrameUInt8 (WebSocketFrameRef self, CFIndex offset); 31 | UInt16 __WebSocketFrameUInt16 (WebSocketFrameRef self, CFIndex offset); 32 | UInt32 __WebSocketFrameUInt32 (WebSocketFrameRef self, CFIndex offset); 33 | UInt64 __WebSocketFrameUInt64 (WebSocketFrameRef self, CFIndex offset); 34 | CFIndex __WebSocketFrameGetSizeOfPayloadLength (WebSocketFrameRef self); 35 | 36 | #define kWebSocketFrameDefaultReadStreamBufferLength (4 * 1024) 37 | 38 | WebSocketFrameRef 39 | WebSocketFrameCreate (CFAllocatorRef allocator) { 40 | WebSocketFrameRef self = CFAllocatorAllocate(allocator, sizeof(struct WebSocketFrame), 0); 41 | memset(self, 0, sizeof(struct WebSocketFrame)); 42 | if_self { 43 | self->retainCount = 1; 44 | self->allocator = allocator ? CFRetain(allocator) : NULL; 45 | self->data = CFDataCreateMutable(allocator, 0); 46 | } 47 | return self; 48 | } 49 | 50 | WebSocketFrameRef 51 | WebSocketFrameCreateWithPayloadData (CFAllocatorRef allocator, WebSocketFrameOpCode opCode, Boolean isMasked, UInt8 *maskingKey, CFDataRef payload) { 52 | WebSocketFrameRef self = WebSocketFrameCreate(allocator); 53 | if_self { 54 | UInt8 header[2 + 8]; 55 | memset(header, 0, sizeof(header)); 56 | 57 | Boolean isFin = TRUE; 58 | Boolean rsv1 = FALSE; 59 | Boolean rsv2 = FALSE; 60 | Boolean rsv3 = FALSE; 61 | 62 | header[0] |= ((isFin ? 1 : 0) << 7); 63 | header[0] |= ((rsv1 ? 1 : 0) << 6); 64 | header[0] |= ((rsv2 ? 1 : 0) << 5); 65 | header[0] |= ((rsv3 ? 1 : 0) << 4); 66 | header[0] |= (opCode & 0xf); 67 | 68 | header[1] |= ((isMasked ? 1 : 0) << 7); 69 | 70 | CFIndex length = payload != NULL ? CFDataGetLength(payload) : 0; 71 | if (length <= 125) { 72 | header[1] |= (UInt8) length; 73 | WebSocketFrameAppend(self, header, 2); 74 | } else { 75 | if (length < 65535) { 76 | header[1] |= 126; 77 | *((UInt16 *) &header[2]) = OSSwapBigToHostInt16(length); 78 | WebSocketFrameAppend(self, header, 2 + 2); 79 | } else { 80 | header[1] |= 127; 81 | *((UInt64 *) &header[2]) = OSSwapBigToHostInt64(length); 82 | WebSocketFrameAppend(self, header, 2 + 8); 83 | } 84 | } 85 | 86 | if (isMasked) { 87 | UInt8 autoMaskingKey[4] = { 0, 0, 0, 0 }; 88 | if (maskingKey == NULL) { 89 | * (u_int32_t *) autoMaskingKey = arc4random(); 90 | maskingKey = autoMaskingKey; 91 | } 92 | WebSocketFrameAppend(self, maskingKey, 4); 93 | } 94 | 95 | if (length > 0) { 96 | WebSocketFrameAppend(self, CFDataGetBytePtr(payload), length); 97 | } 98 | 99 | WebSocketFrameParse(self); 100 | } 101 | return self; 102 | } 103 | 104 | WebSocketFrameRef 105 | WebSocketFrameCreateWithPayloadString (CFAllocatorRef allocator, Boolean isMasked, UInt8 *maskingKey, CFStringRef payload) { 106 | WebSocketFrameRef self = NULL; 107 | CFDataRef data = CFStringCreateExternalRepresentation(allocator, payload, kCFStringEncodingUTF8, 0); 108 | if (data != NULL) { 109 | self = WebSocketFrameCreateWithPayloadData(allocator, kWebSocketFrameOpCodeText, isMasked, maskingKey, data); 110 | CFRelease(data); 111 | } 112 | return self; 113 | } 114 | 115 | void 116 | WebSocketFrameDealloc (WebSocketFrameRef self) { 117 | if_self { 118 | CFAllocatorRef allocator = self->allocator; 119 | if (self->data) { 120 | CFRelease(self->data), self->data = NULL; 121 | } 122 | CFAllocatorDeallocate(allocator, self), self = NULL; 123 | if (allocator != NULL) { 124 | CFRelease(allocator), allocator = NULL; 125 | } 126 | } 127 | } 128 | 129 | WebSocketFrameRef 130 | WebSocketFrameRetain (WebSocketFrameRef self) { 131 | if_self { 132 | ++self->retainCount; 133 | } 134 | return self; 135 | } 136 | 137 | void 138 | WebSocketFrameRelease (WebSocketFrameRef self) { 139 | if_self { 140 | if (--self->retainCount == 0) { 141 | WebSocketFrameDealloc(self); 142 | } 143 | } 144 | } 145 | 146 | Boolean 147 | WebSocketFrameGetIsFin (WebSocketFrameRef self) { 148 | Boolean result = FALSE; 149 | if_self_and (self->state == kWebSocketFrameStateReady) { 150 | result = self->isFin; 151 | } 152 | return result; 153 | } 154 | 155 | WebSocketFrameOpCode 156 | WebSocketFrameGetOpCode (WebSocketFrameRef self) { 157 | WebSocketFrameOpCode result = 0; 158 | if_self_and (self->state == kWebSocketFrameStateReady) { 159 | result = self->opCode; 160 | } 161 | return result; 162 | } 163 | 164 | Boolean 165 | WebSocketFrameGetIsMasked (WebSocketFrameRef self) { 166 | Boolean result = FALSE; 167 | if_self_and (self->state == kWebSocketFrameStateReady) { 168 | result = self->isMasked; 169 | } 170 | return result; 171 | } 172 | 173 | void 174 | WebSocketFrameAppend (WebSocketFrameRef self, const UInt8 *bytes, CFIndex length) { 175 | if_self { 176 | CFDataAppendBytes(self->data, bytes, length); 177 | } 178 | } 179 | 180 | UInt64 181 | WebSocketFrameGetPayloadLength (WebSocketFrameRef self) { 182 | UInt64 result = 0; 183 | if_self_and (self->state == kWebSocketFrameStateReady) { 184 | result = self->payloadLength; 185 | } 186 | return result; 187 | } 188 | 189 | WebSocketFrameState 190 | WebSocketFrameGetState (WebSocketFrameRef self) { 191 | WebSocketFrameState result = kWebSocketFrameStateNone; 192 | if_self { 193 | result = self->state; 194 | } 195 | return result; 196 | } 197 | 198 | void 199 | WebSocketFrameReset (WebSocketFrameRef self) { 200 | if_self { 201 | self->isFin = FALSE; 202 | self->isRsv1 = FALSE; 203 | self->isRsv2 = FALSE; 204 | self->isRsv3 = FALSE; 205 | self->isMasked = FALSE; 206 | self->maskingKey[0] = 0; 207 | self->maskingKey[1] = 0; 208 | self->maskingKey[2] = 0; 209 | self->maskingKey[3] = 0; 210 | self->opCode = 0; 211 | self->state = kWebSocketFrameStateNone; 212 | self->payloadOffset = 0; 213 | self->payloadLength = 0; 214 | CFDataSetLength(self->data, 0); 215 | } 216 | } 217 | 218 | #pragma mark Private definitions 219 | 220 | const char * 221 | __WebSocketFrameOpCodeCString[15] = { 222 | "kWebSocketFrameOpCodeContinuation", // 0 223 | "kWebSocketFrameOpCodeText", // 1 224 | "kWebSocketFrameOpCodeBinary", // 2 225 | "kWebSocketFrameOpCode3", // 3 226 | "kWebSocketFrameOpCode4", // 4 227 | "kWebSocketFrameOpCode5", // 5 228 | "kWebSocketFrameOpCode6", // 6 229 | "kWebSocketFrameOpCode7", // 7 230 | "kWebSocketFrameOpCodeClose", // 8 231 | "kWebSocketFrameOpCodePing", // 9 232 | "kWebSocketFrameOpCodePong", // 10 233 | "kWebSocketFrameOpCode11", // 11 234 | "kWebSocketFrameOpCode12", // 12 235 | "kWebSocketFrameOpCode13", // 13 236 | "kWebSocketFrameOpCode14" // 14 237 | }; 238 | 239 | UInt8 240 | __WebSocketFrameUInt8 (WebSocketFrameRef self, CFIndex offset) { 241 | UInt8 result = 0; 242 | if_self_and (offset + sizeof(result) - 1 < CFDataGetLength(self->data)) { 243 | CFDataGetBytes(self->data, CFRangeMake(offset, sizeof(result)), (UInt8 *) &result); 244 | } 245 | return result; 246 | } 247 | 248 | UInt16 249 | __WebSocketFrameUInt16 (WebSocketFrameRef self, CFIndex offset) { 250 | UInt16 result = 0; 251 | if_self_and (offset + sizeof(result) - 1 < CFDataGetLength(self->data)) { 252 | CFDataGetBytes(self->data, CFRangeMake(offset, sizeof(result)), (UInt8 *) &result); 253 | result = OSSwapBigToHostInt16(result); 254 | } 255 | return result; 256 | } 257 | 258 | UInt32 259 | __WebSocketFrameUInt32 (WebSocketFrameRef self, CFIndex offset) { 260 | UInt32 result = 0; 261 | if_self_and (offset + sizeof(result) - 1 < CFDataGetLength(self->data)) { 262 | CFDataGetBytes(self->data, CFRangeMake(offset, sizeof(result)), (UInt8 *) &result); 263 | result = OSSwapBigToHostInt32(result); 264 | } 265 | return result; 266 | } 267 | 268 | UInt64 269 | __WebSocketFrameUInt64 (WebSocketFrameRef self, CFIndex offset) { 270 | UInt64 result = 0; 271 | if_self_and (offset + sizeof(result) - 1 < CFDataGetLength(self->data)) { 272 | CFDataGetBytes(self->data, CFRangeMake(offset, sizeof(result)), (UInt8 *) &result); 273 | result = OSSwapBigToHostInt64(result); 274 | } 275 | return result; 276 | } 277 | 278 | WebSocketFrameState 279 | WebSocketFrameParse (WebSocketFrameRef self) { 280 | WebSocketFrameState result = kWebSocketFrameStateNone; 281 | if_self { 282 | 283 | self->isFin = FALSE; 284 | self->isRsv1 = FALSE; 285 | self->isRsv2 = FALSE; 286 | self->isRsv3 = FALSE; 287 | self->isMasked = FALSE; 288 | self->maskingKey[0] = 0; 289 | self->maskingKey[1] = 0; 290 | self->maskingKey[2] = 0; 291 | self->maskingKey[3] = 0; 292 | self->opCode = 0; 293 | self->state = kWebSocketFrameState2BytesHeader; 294 | self->payloadOffset = 0; 295 | self->payloadLength = 0; 296 | 297 | CFIndex n = CFDataGetLength(self->data); 298 | Boolean loop = TRUE; 299 | CFIndex i = 0; 300 | while (loop) { 301 | switch (self->state) { 302 | case kWebSocketFrameState2BytesHeader: 303 | if (i + 2 <= n) { 304 | UInt8 byte1 = __WebSocketFrameUInt8(self, i); 305 | UInt8 byte2 = __WebSocketFrameUInt8(self, i + 1); 306 | 307 | self->isFin = byte1 & (1 << 7) ? TRUE : FALSE; 308 | self->isRsv1 = byte1 & (1 << 6) ? TRUE : FALSE; 309 | self->isRsv2 = byte1 & (1 << 5) ? TRUE : FALSE; 310 | self->isRsv3 = byte1 & (1 << 4) ? TRUE : FALSE; 311 | self->opCode = (WebSocketFrameOpCode) (byte1 & 0xf); 312 | 313 | self->isMasked = byte2 & (1 << 7) ? TRUE : FALSE; 314 | self->payloadLength = (byte2 & 127); 315 | 316 | switch (self->payloadLength) { 317 | case 126: 318 | self->state = kWebSocketFrameState16BitLength; 319 | break; 320 | 321 | case 127: 322 | self->state = kWebSocketFrameState64BitLength; 323 | break; 324 | 325 | default: 326 | self->state = kWebSocketFrameStateMaskingKey; 327 | break; 328 | } 329 | 330 | self->payloadOffset += 2; 331 | i += 2; 332 | } else { 333 | self->state = kWebSocketFrameStateError; 334 | } 335 | break; 336 | 337 | case kWebSocketFrameState16BitLength: 338 | if (i + 2 <= n) { 339 | self->payloadLength = __WebSocketFrameUInt16(self, i); 340 | self->state = kWebSocketFrameStateMaskingKey; 341 | self->payloadOffset += 2; 342 | i += 2; 343 | } else { 344 | self->state = kWebSocketFrameStateError; 345 | } 346 | break; 347 | 348 | case kWebSocketFrameState64BitLength: 349 | if (i + 8 <= n) { 350 | self->payloadLength = __WebSocketFrameUInt64(self, i); 351 | self->state = kWebSocketFrameStateMaskingKey; 352 | self->payloadOffset += 8; 353 | i += 8; 354 | } else { 355 | self->state = kWebSocketFrameStateError; 356 | } 357 | break; 358 | 359 | case kWebSocketFrameStateMaskingKey: 360 | if (self->isMasked) { 361 | if (i + 4 <= n) { 362 | self->maskingKey[0] = __WebSocketFrameUInt8(self, i); 363 | self->maskingKey[1] = __WebSocketFrameUInt8(self, i + 1); 364 | self->maskingKey[2] = __WebSocketFrameUInt8(self, i + 2); 365 | self->maskingKey[3] = __WebSocketFrameUInt8(self, i + 3); 366 | self->state = kWebSocketFrameStatePayload; 367 | self->payloadOffset += 4; 368 | i += 4; 369 | } else { 370 | self->state = kWebSocketFrameStateError; 371 | } 372 | } else { 373 | self->state = kWebSocketFrameStatePayload; 374 | } 375 | break; 376 | 377 | case kWebSocketFrameStatePayload: 378 | if (i + WebSocketFrameGetPayloadLength(self)) { 379 | self->state = kWebSocketFrameStateReady; 380 | i += WebSocketFrameGetPayloadLength(self); 381 | } else { 382 | self->state = kWebSocketFrameStateError; 383 | } 384 | break; 385 | 386 | case kWebSocketFrameStateReady: 387 | loop = FALSE; 388 | break; 389 | 390 | case kWebSocketFrameStateError: 391 | loop = FALSE; 392 | break; 393 | 394 | case kWebSocketFrameStateNone: 395 | loop = FALSE; 396 | break; 397 | } 398 | } 399 | result = self->state; 400 | } 401 | return result; 402 | } 403 | 404 | void 405 | WebSocketFrameGetPayloadWithRange (WebSocketFrameRef self, CFRange range, UInt8 *buffer, Boolean unmask) { 406 | if_self_and (self->state == kWebSocketFrameStateReady && range.location >= 0 && (range.location + range.length) <= self->payloadLength) { 407 | CFIndex location = self->payloadOffset + range.location; 408 | if (unmask && self->isMasked) { 409 | const UInt8 *payload = CFDataGetBytePtr(self->data) + location; 410 | for (CFIndex i = 0; i < range.length; ++i) { 411 | *(buffer + i) = *(payload + i) ^ (self->maskingKey[(i + range.location) % 4]); 412 | } 413 | } else { 414 | CFDataGetBytes(self->data, CFRangeMake(location, range.length), buffer); 415 | } 416 | } 417 | } 418 | 419 | CFDataRef 420 | WebSocketFrameCopyPayloadDataWithRange (WebSocketFrameRef self, CFRange range, Boolean unmask) { 421 | CFMutableDataRef result = NULL; 422 | if_self_and (self->state == kWebSocketFrameStateReady && range.location >= 0 && (range.location + range.length) <= self->payloadLength) { 423 | result = CFDataCreateMutable(self->allocator, range.length); 424 | if (result != NULL) { 425 | CFDataSetLength(result, range.length); 426 | WebSocketFrameGetPayloadWithRange(self, range, CFDataGetMutableBytePtr(result), unmask); 427 | } 428 | } 429 | return result; 430 | } 431 | 432 | CFStringRef 433 | WebSocketFrameCopyPayloadStringWithRange (WebSocketFrameRef self, CFRange range, CFStringEncoding encoding) { 434 | CFStringRef result = NULL; 435 | if_self_and (self->state == kWebSocketFrameStateReady && range.location >= 0 && (range.location + range.length) <= self->payloadLength) { 436 | 437 | // TODO: Avoid this allocation. 438 | CFDataRef data = WebSocketFrameCopyPayloadDataWithRange(self, range, TRUE); 439 | if (data) { 440 | result = CFStringCreateFromExternalRepresentation(self->allocator, data, encoding); 441 | CFRelease(data); 442 | } 443 | } 444 | return result; 445 | } 446 | 447 | CFStringRef 448 | WebSocketFrameCopyPayloadString (WebSocketFrameRef self, CFStringEncoding encoding) { 449 | CFStringRef result = NULL; 450 | if_self_and (self->state == kWebSocketFrameStateReady) { 451 | result = WebSocketFrameCopyPayloadStringWithRange(self, CFRangeMake(0, self->payloadLength), encoding); 452 | } 453 | return result; 454 | } 455 | 456 | const UInt8 * 457 | WebSocketFrameGetBytesPtr (WebSocketFrameRef self) { 458 | const UInt8 *result = NULL; 459 | if_self { 460 | result = CFDataGetBytePtr(self->data); 461 | } 462 | return result; 463 | } 464 | 465 | -------------------------------------------------------------------------------- /CoreWebSocket.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 9194067A171F23B0009B64F8 /* WebSocketFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = EB93B6E316C195840004C53A /* WebSocketFrame.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | EB131D7D1327F5C900C8F095 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = EB131D7B1327F5C900C8F095 /* InfoPlist.strings */; }; 12 | EB131D8D1327F63700C8F095 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EB60778B1324E7FB00D98B17 /* CoreFoundation.framework */; }; 13 | EB131D8E1327F63900C8F095 /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EB6077961325111400D98B17 /* CoreServices.framework */; }; 14 | EB131D8F1327F63B00C8F095 /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = EB607798132514E800D98B17 /* libcrypto.dylib */; }; 15 | EB60778C1324E7FB00D98B17 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EB60778B1324E7FB00D98B17 /* CoreFoundation.framework */; }; 16 | EB6077971325111500D98B17 /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EB6077961325111400D98B17 /* CoreServices.framework */; }; 17 | EB607799132514E800D98B17 /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = EB607798132514E800D98B17 /* libcrypto.dylib */; }; 18 | EB8414131375A255002BF1E4 /* cuEnc64.c in Sources */ = {isa = PBXBuildFile; fileRef = EB84140C1375A255002BF1E4 /* cuEnc64.c */; }; 19 | EB8414141375A255002BF1E4 /* cuEnc64.c in Sources */ = {isa = PBXBuildFile; fileRef = EB84140C1375A255002BF1E4 /* cuEnc64.c */; }; 20 | EB8414151375A255002BF1E4 /* cuEnc64.c in Sources */ = {isa = PBXBuildFile; fileRef = EB84140C1375A255002BF1E4 /* cuEnc64.c */; }; 21 | EB8414161375A255002BF1E4 /* cuEnc64.h in Headers */ = {isa = PBXBuildFile; fileRef = EB84140D1375A255002BF1E4 /* cuEnc64.h */; settings = {ATTRIBUTES = (Public, ); }; }; 22 | EB8414171375A255002BF1E4 /* cuEnc64.h in Headers */ = {isa = PBXBuildFile; fileRef = EB84140D1375A255002BF1E4 /* cuEnc64.h */; }; 23 | EB8414181375A255002BF1E4 /* WebSocket.c in Sources */ = {isa = PBXBuildFile; fileRef = EB84140E1375A255002BF1E4 /* WebSocket.c */; }; 24 | EB8414191375A255002BF1E4 /* WebSocket.c in Sources */ = {isa = PBXBuildFile; fileRef = EB84140E1375A255002BF1E4 /* WebSocket.c */; }; 25 | EB84141A1375A255002BF1E4 /* WebSocket.c in Sources */ = {isa = PBXBuildFile; fileRef = EB84140E1375A255002BF1E4 /* WebSocket.c */; }; 26 | EB84141B1375A255002BF1E4 /* WebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = EB84140F1375A255002BF1E4 /* WebSocket.h */; settings = {ATTRIBUTES = (Public, ); }; }; 27 | EB84141C1375A255002BF1E4 /* WebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = EB84140F1375A255002BF1E4 /* WebSocket.h */; }; 28 | EB84141D1375A255002BF1E4 /* WebSocketClient.c in Sources */ = {isa = PBXBuildFile; fileRef = EB8414101375A255002BF1E4 /* WebSocketClient.c */; }; 29 | EB84141E1375A255002BF1E4 /* WebSocketClient.c in Sources */ = {isa = PBXBuildFile; fileRef = EB8414101375A255002BF1E4 /* WebSocketClient.c */; }; 30 | EB84141F1375A255002BF1E4 /* WebSocketClient.c in Sources */ = {isa = PBXBuildFile; fileRef = EB8414101375A255002BF1E4 /* WebSocketClient.c */; }; 31 | EB8414201375A255002BF1E4 /* WebSocketClient.h in Headers */ = {isa = PBXBuildFile; fileRef = EB8414111375A255002BF1E4 /* WebSocketClient.h */; settings = {ATTRIBUTES = (Public, ); }; }; 32 | EB8414211375A255002BF1E4 /* WebSocketClient.h in Headers */ = {isa = PBXBuildFile; fileRef = EB8414111375A255002BF1E4 /* WebSocketClient.h */; }; 33 | EB8414221375A255002BF1E4 /* WebSocketTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = EB8414121375A255002BF1E4 /* WebSocketTypes.h */; settings = {ATTRIBUTES = (Public, ); }; }; 34 | EB8414231375A255002BF1E4 /* WebSocketTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = EB8414121375A255002BF1E4 /* WebSocketTypes.h */; }; 35 | EB93B6E516C19F780004C53A /* WebSocketFrame.c in Sources */ = {isa = PBXBuildFile; fileRef = EB93B6E416C19F780004C53A /* WebSocketFrame.c */; }; 36 | EB93B6E616C19F780004C53A /* WebSocketFrame.c in Sources */ = {isa = PBXBuildFile; fileRef = EB93B6E416C19F780004C53A /* WebSocketFrame.c */; }; 37 | EB93B6E716C19F780004C53A /* WebSocketFrame.c in Sources */ = {isa = PBXBuildFile; fileRef = EB93B6E416C19F780004C53A /* WebSocketFrame.c */; }; 38 | EBB035A31327F70B005EF338 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EBB035A21327F70B005EF338 /* CoreFoundation.framework */; }; 39 | EBB035A51327F711005EF338 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EBB035A41327F711005EF338 /* CFNetwork.framework */; }; 40 | EBF1E9B71375A2F1009F3C50 /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = EBF1E9B61375A2F1009F3C50 /* main.c */; }; 41 | EBF1E9B91375A334009F3C50 /* CoreWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = EBF1E9B81375A334009F3C50 /* CoreWebSocket.h */; settings = {ATTRIBUTES = (Public, ); }; }; 42 | EBF1E9BA1375A334009F3C50 /* CoreWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = EBF1E9B81375A334009F3C50 /* CoreWebSocket.h */; }; 43 | /* End PBXBuildFile section */ 44 | 45 | /* Begin PBXCopyFilesBuildPhase section */ 46 | EB6077851324E7FB00D98B17 /* CopyFiles */ = { 47 | isa = PBXCopyFilesBuildPhase; 48 | buildActionMask = 2147483647; 49 | dstPath = /usr/share/man/man1/; 50 | dstSubfolderSpec = 0; 51 | files = ( 52 | ); 53 | runOnlyForDeploymentPostprocessing = 1; 54 | }; 55 | /* End PBXCopyFilesBuildPhase section */ 56 | 57 | /* Begin PBXFileReference section */ 58 | EB131D711327F5C900C8F095 /* CoreWebSocket.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CoreWebSocket.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 59 | EB131D7A1327F5C900C8F095 /* CoreWebSocket-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "CoreWebSocket-Info.plist"; sourceTree = ""; }; 60 | EB131D7C1327F5C900C8F095 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 61 | EB131D7E1327F5C900C8F095 /* CoreWebSocket-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CoreWebSocket-Prefix.pch"; sourceTree = ""; }; 62 | EB6077871324E7FB00D98B17 /* CoreWebSocketConsole */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = CoreWebSocketConsole; sourceTree = BUILT_PRODUCTS_DIR; }; 63 | EB60778B1324E7FB00D98B17 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; 64 | EB6077961325111400D98B17 /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = System/Library/Frameworks/CoreServices.framework; sourceTree = SDKROOT; }; 65 | EB607798132514E800D98B17 /* libcrypto.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.dylib; path = usr/lib/libcrypto.dylib; sourceTree = SDKROOT; }; 66 | EB84140C1375A255002BF1E4 /* cuEnc64.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cuEnc64.c; sourceTree = ""; }; 67 | EB84140D1375A255002BF1E4 /* cuEnc64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cuEnc64.h; sourceTree = ""; }; 68 | EB84140E1375A255002BF1E4 /* WebSocket.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = WebSocket.c; sourceTree = ""; }; 69 | EB84140F1375A255002BF1E4 /* WebSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebSocket.h; sourceTree = ""; }; 70 | EB8414101375A255002BF1E4 /* WebSocketClient.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = WebSocketClient.c; sourceTree = ""; }; 71 | EB8414111375A255002BF1E4 /* WebSocketClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebSocketClient.h; sourceTree = ""; }; 72 | EB8414121375A255002BF1E4 /* WebSocketTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebSocketTypes.h; sourceTree = ""; }; 73 | EB93B6E316C195840004C53A /* WebSocketFrame.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WebSocketFrame.h; sourceTree = ""; }; 74 | EB93B6E416C19F780004C53A /* WebSocketFrame.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = WebSocketFrame.c; sourceTree = ""; }; 75 | EBB035981327F6FA005EF338 /* libCoreWebSocket_iOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libCoreWebSocket_iOS.a; sourceTree = BUILT_PRODUCTS_DIR; }; 76 | EBB035A21327F70B005EF338 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/System/Library/Frameworks/CoreFoundation.framework; sourceTree = DEVELOPER_DIR; }; 77 | EBB035A41327F711005EF338 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/System/Library/Frameworks/CFNetwork.framework; sourceTree = DEVELOPER_DIR; }; 78 | EBF1E9B61375A2F1009F3C50 /* main.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = main.c; path = CoreWebSocket/main.c; sourceTree = ""; }; 79 | EBF1E9B81375A334009F3C50 /* CoreWebSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoreWebSocket.h; sourceTree = ""; }; 80 | /* End PBXFileReference section */ 81 | 82 | /* Begin PBXFrameworksBuildPhase section */ 83 | EB131D6D1327F5C900C8F095 /* Frameworks */ = { 84 | isa = PBXFrameworksBuildPhase; 85 | buildActionMask = 2147483647; 86 | files = ( 87 | EB131D8D1327F63700C8F095 /* CoreFoundation.framework in Frameworks */, 88 | EB131D8E1327F63900C8F095 /* CoreServices.framework in Frameworks */, 89 | EB131D8F1327F63B00C8F095 /* libcrypto.dylib in Frameworks */, 90 | ); 91 | runOnlyForDeploymentPostprocessing = 0; 92 | }; 93 | EB6077841324E7FB00D98B17 /* Frameworks */ = { 94 | isa = PBXFrameworksBuildPhase; 95 | buildActionMask = 2147483647; 96 | files = ( 97 | EB607799132514E800D98B17 /* libcrypto.dylib in Frameworks */, 98 | EB6077971325111500D98B17 /* CoreServices.framework in Frameworks */, 99 | EB60778C1324E7FB00D98B17 /* CoreFoundation.framework in Frameworks */, 100 | ); 101 | runOnlyForDeploymentPostprocessing = 0; 102 | }; 103 | EBB035951327F6FA005EF338 /* Frameworks */ = { 104 | isa = PBXFrameworksBuildPhase; 105 | buildActionMask = 2147483647; 106 | files = ( 107 | EBB035A51327F711005EF338 /* CFNetwork.framework in Frameworks */, 108 | EBB035A31327F70B005EF338 /* CoreFoundation.framework in Frameworks */, 109 | ); 110 | runOnlyForDeploymentPostprocessing = 0; 111 | }; 112 | /* End PBXFrameworksBuildPhase section */ 113 | 114 | /* Begin PBXGroup section */ 115 | EB131D781327F5C900C8F095 /* CoreWebSocket */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | EBF1E9B81375A334009F3C50 /* CoreWebSocket.h */, 119 | EB93B6E316C195840004C53A /* WebSocketFrame.h */, 120 | EB93B6E416C19F780004C53A /* WebSocketFrame.c */, 121 | EB8414121375A255002BF1E4 /* WebSocketTypes.h */, 122 | EB84140F1375A255002BF1E4 /* WebSocket.h */, 123 | EB84140E1375A255002BF1E4 /* WebSocket.c */, 124 | EB8414111375A255002BF1E4 /* WebSocketClient.h */, 125 | EB8414101375A255002BF1E4 /* WebSocketClient.c */, 126 | EB84140D1375A255002BF1E4 /* cuEnc64.h */, 127 | EB84140C1375A255002BF1E4 /* cuEnc64.c */, 128 | EB131D791327F5C900C8F095 /* Supporting Files */, 129 | ); 130 | path = CoreWebSocket; 131 | sourceTree = ""; 132 | }; 133 | EB131D791327F5C900C8F095 /* Supporting Files */ = { 134 | isa = PBXGroup; 135 | children = ( 136 | EB131D7A1327F5C900C8F095 /* CoreWebSocket-Info.plist */, 137 | EB131D7B1327F5C900C8F095 /* InfoPlist.strings */, 138 | EB131D7E1327F5C900C8F095 /* CoreWebSocket-Prefix.pch */, 139 | ); 140 | name = "Supporting Files"; 141 | sourceTree = ""; 142 | }; 143 | EB131D831327F5F300C8F095 /* iOS */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | EBB035A41327F711005EF338 /* CFNetwork.framework */, 147 | EBB035A21327F70B005EF338 /* CoreFoundation.framework */, 148 | ); 149 | name = iOS; 150 | sourceTree = ""; 151 | }; 152 | EB4CE584132683A70090FED3 /* OSX */ = { 153 | isa = PBXGroup; 154 | children = ( 155 | EB607798132514E800D98B17 /* libcrypto.dylib */, 156 | EB6077961325111400D98B17 /* CoreServices.framework */, 157 | EB60778B1324E7FB00D98B17 /* CoreFoundation.framework */, 158 | ); 159 | name = OSX; 160 | sourceTree = ""; 161 | }; 162 | EB60777C1324E7FB00D98B17 = { 163 | isa = PBXGroup; 164 | children = ( 165 | EBF1E9B41375A2E5009F3C50 /* Console */, 166 | EB131D781327F5C900C8F095 /* CoreWebSocket */, 167 | EB60778A1324E7FB00D98B17 /* Frameworks */, 168 | EB6077881324E7FB00D98B17 /* Products */, 169 | ); 170 | sourceTree = ""; 171 | }; 172 | EB6077881324E7FB00D98B17 /* Products */ = { 173 | isa = PBXGroup; 174 | children = ( 175 | EB6077871324E7FB00D98B17 /* CoreWebSocketConsole */, 176 | EB131D711327F5C900C8F095 /* CoreWebSocket.framework */, 177 | EBB035981327F6FA005EF338 /* libCoreWebSocket_iOS.a */, 178 | ); 179 | name = Products; 180 | sourceTree = ""; 181 | }; 182 | EB60778A1324E7FB00D98B17 /* Frameworks */ = { 183 | isa = PBXGroup; 184 | children = ( 185 | EB131D831327F5F300C8F095 /* iOS */, 186 | EB4CE584132683A70090FED3 /* OSX */, 187 | ); 188 | name = Frameworks; 189 | sourceTree = ""; 190 | }; 191 | EBF1E9B41375A2E5009F3C50 /* Console */ = { 192 | isa = PBXGroup; 193 | children = ( 194 | EBF1E9B61375A2F1009F3C50 /* main.c */, 195 | ); 196 | name = Console; 197 | sourceTree = ""; 198 | }; 199 | /* End PBXGroup section */ 200 | 201 | /* Begin PBXHeadersBuildPhase section */ 202 | EB131D6E1327F5C900C8F095 /* Headers */ = { 203 | isa = PBXHeadersBuildPhase; 204 | buildActionMask = 2147483647; 205 | files = ( 206 | EB8414161375A255002BF1E4 /* cuEnc64.h in Headers */, 207 | EB84141B1375A255002BF1E4 /* WebSocket.h in Headers */, 208 | EB8414201375A255002BF1E4 /* WebSocketClient.h in Headers */, 209 | EB8414221375A255002BF1E4 /* WebSocketTypes.h in Headers */, 210 | EBF1E9B91375A334009F3C50 /* CoreWebSocket.h in Headers */, 211 | 9194067A171F23B0009B64F8 /* WebSocketFrame.h in Headers */, 212 | ); 213 | runOnlyForDeploymentPostprocessing = 0; 214 | }; 215 | EBB035961327F6FA005EF338 /* Headers */ = { 216 | isa = PBXHeadersBuildPhase; 217 | buildActionMask = 2147483647; 218 | files = ( 219 | EB8414171375A255002BF1E4 /* cuEnc64.h in Headers */, 220 | EB84141C1375A255002BF1E4 /* WebSocket.h in Headers */, 221 | EB8414211375A255002BF1E4 /* WebSocketClient.h in Headers */, 222 | EB8414231375A255002BF1E4 /* WebSocketTypes.h in Headers */, 223 | EBF1E9BA1375A334009F3C50 /* CoreWebSocket.h in Headers */, 224 | ); 225 | runOnlyForDeploymentPostprocessing = 0; 226 | }; 227 | /* End PBXHeadersBuildPhase section */ 228 | 229 | /* Begin PBXNativeTarget section */ 230 | EB131D701327F5C900C8F095 /* CoreWebSocket */ = { 231 | isa = PBXNativeTarget; 232 | buildConfigurationList = EB131D811327F5C900C8F095 /* Build configuration list for PBXNativeTarget "CoreWebSocket" */; 233 | buildPhases = ( 234 | EB131D6C1327F5C900C8F095 /* Sources */, 235 | EB131D6D1327F5C900C8F095 /* Frameworks */, 236 | EB131D6E1327F5C900C8F095 /* Headers */, 237 | EB131D6F1327F5C900C8F095 /* Resources */, 238 | ); 239 | buildRules = ( 240 | ); 241 | dependencies = ( 242 | ); 243 | name = CoreWebSocket; 244 | productName = CoreWebSocket; 245 | productReference = EB131D711327F5C900C8F095 /* CoreWebSocket.framework */; 246 | productType = "com.apple.product-type.framework"; 247 | }; 248 | EB6077861324E7FB00D98B17 /* CoreWebSocketConsole */ = { 249 | isa = PBXNativeTarget; 250 | buildConfigurationList = EB6077931324E7FB00D98B17 /* Build configuration list for PBXNativeTarget "CoreWebSocketConsole" */; 251 | buildPhases = ( 252 | EB6077831324E7FB00D98B17 /* Sources */, 253 | EB6077841324E7FB00D98B17 /* Frameworks */, 254 | EB6077851324E7FB00D98B17 /* CopyFiles */, 255 | ); 256 | buildRules = ( 257 | ); 258 | dependencies = ( 259 | ); 260 | name = CoreWebSocketConsole; 261 | productName = WebSocketCore; 262 | productReference = EB6077871324E7FB00D98B17 /* CoreWebSocketConsole */; 263 | productType = "com.apple.product-type.tool"; 264 | }; 265 | EBB035971327F6FA005EF338 /* CoreWebSocket_iOS */ = { 266 | isa = PBXNativeTarget; 267 | buildConfigurationList = EBB035A01327F6FA005EF338 /* Build configuration list for PBXNativeTarget "CoreWebSocket_iOS" */; 268 | buildPhases = ( 269 | EBB035941327F6FA005EF338 /* Sources */, 270 | EBB035951327F6FA005EF338 /* Frameworks */, 271 | EBB035961327F6FA005EF338 /* Headers */, 272 | ); 273 | buildRules = ( 274 | ); 275 | dependencies = ( 276 | ); 277 | name = CoreWebSocket_iOS; 278 | productName = CoreWebSocket_iOS; 279 | productReference = EBB035981327F6FA005EF338 /* libCoreWebSocket_iOS.a */; 280 | productType = "com.apple.product-type.library.static"; 281 | }; 282 | /* End PBXNativeTarget section */ 283 | 284 | /* Begin PBXProject section */ 285 | EB60777E1324E7FB00D98B17 /* Project object */ = { 286 | isa = PBXProject; 287 | attributes = { 288 | LastUpgradeCheck = 0500; 289 | ORGANIZATIONNAME = "Inteliv Ltd"; 290 | }; 291 | buildConfigurationList = EB6077811324E7FB00D98B17 /* Build configuration list for PBXProject "CoreWebSocket" */; 292 | compatibilityVersion = "Xcode 3.2"; 293 | developmentRegion = English; 294 | hasScannedForEncodings = 0; 295 | knownRegions = ( 296 | en, 297 | ); 298 | mainGroup = EB60777C1324E7FB00D98B17; 299 | productRefGroup = EB6077881324E7FB00D98B17 /* Products */; 300 | projectDirPath = ""; 301 | projectRoot = ""; 302 | targets = ( 303 | EB6077861324E7FB00D98B17 /* CoreWebSocketConsole */, 304 | EB131D701327F5C900C8F095 /* CoreWebSocket */, 305 | EBB035971327F6FA005EF338 /* CoreWebSocket_iOS */, 306 | ); 307 | }; 308 | /* End PBXProject section */ 309 | 310 | /* Begin PBXResourcesBuildPhase section */ 311 | EB131D6F1327F5C900C8F095 /* Resources */ = { 312 | isa = PBXResourcesBuildPhase; 313 | buildActionMask = 2147483647; 314 | files = ( 315 | EB131D7D1327F5C900C8F095 /* InfoPlist.strings in Resources */, 316 | ); 317 | runOnlyForDeploymentPostprocessing = 0; 318 | }; 319 | /* End PBXResourcesBuildPhase section */ 320 | 321 | /* Begin PBXSourcesBuildPhase section */ 322 | EB131D6C1327F5C900C8F095 /* Sources */ = { 323 | isa = PBXSourcesBuildPhase; 324 | buildActionMask = 2147483647; 325 | files = ( 326 | EB8414141375A255002BF1E4 /* cuEnc64.c in Sources */, 327 | EB8414191375A255002BF1E4 /* WebSocket.c in Sources */, 328 | EB84141E1375A255002BF1E4 /* WebSocketClient.c in Sources */, 329 | EB93B6E616C19F780004C53A /* WebSocketFrame.c in Sources */, 330 | ); 331 | runOnlyForDeploymentPostprocessing = 0; 332 | }; 333 | EB6077831324E7FB00D98B17 /* Sources */ = { 334 | isa = PBXSourcesBuildPhase; 335 | buildActionMask = 2147483647; 336 | files = ( 337 | EB8414131375A255002BF1E4 /* cuEnc64.c in Sources */, 338 | EB8414181375A255002BF1E4 /* WebSocket.c in Sources */, 339 | EB84141D1375A255002BF1E4 /* WebSocketClient.c in Sources */, 340 | EBF1E9B71375A2F1009F3C50 /* main.c in Sources */, 341 | EB93B6E516C19F780004C53A /* WebSocketFrame.c in Sources */, 342 | ); 343 | runOnlyForDeploymentPostprocessing = 0; 344 | }; 345 | EBB035941327F6FA005EF338 /* Sources */ = { 346 | isa = PBXSourcesBuildPhase; 347 | buildActionMask = 2147483647; 348 | files = ( 349 | EB8414151375A255002BF1E4 /* cuEnc64.c in Sources */, 350 | EB84141A1375A255002BF1E4 /* WebSocket.c in Sources */, 351 | EB84141F1375A255002BF1E4 /* WebSocketClient.c in Sources */, 352 | EB93B6E716C19F780004C53A /* WebSocketFrame.c in Sources */, 353 | ); 354 | runOnlyForDeploymentPostprocessing = 0; 355 | }; 356 | /* End PBXSourcesBuildPhase section */ 357 | 358 | /* Begin PBXVariantGroup section */ 359 | EB131D7B1327F5C900C8F095 /* InfoPlist.strings */ = { 360 | isa = PBXVariantGroup; 361 | children = ( 362 | EB131D7C1327F5C900C8F095 /* en */, 363 | ); 364 | name = InfoPlist.strings; 365 | sourceTree = ""; 366 | }; 367 | /* End PBXVariantGroup section */ 368 | 369 | /* Begin XCBuildConfiguration section */ 370 | EB131D7F1327F5C900C8F095 /* Debug */ = { 371 | isa = XCBuildConfiguration; 372 | buildSettings = { 373 | ALWAYS_SEARCH_USER_PATHS = NO; 374 | COMBINE_HIDPI_IMAGES = YES; 375 | COPY_PHASE_STRIP = NO; 376 | DYLIB_COMPATIBILITY_VERSION = 1; 377 | DYLIB_CURRENT_VERSION = 1; 378 | FRAMEWORK_VERSION = A; 379 | GCC_DYNAMIC_NO_PIC = NO; 380 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 381 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 382 | GCC_PREFIX_HEADER = "CoreWebSocket/CoreWebSocket-Prefix.pch"; 383 | INFOPLIST_FILE = "CoreWebSocket/CoreWebSocket-Info.plist"; 384 | PRODUCT_NAME = "$(TARGET_NAME)"; 385 | WRAPPER_EXTENSION = framework; 386 | }; 387 | name = Debug; 388 | }; 389 | EB131D801327F5C900C8F095 /* Release */ = { 390 | isa = XCBuildConfiguration; 391 | buildSettings = { 392 | ALWAYS_SEARCH_USER_PATHS = NO; 393 | COMBINE_HIDPI_IMAGES = YES; 394 | COPY_PHASE_STRIP = YES; 395 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 396 | DYLIB_COMPATIBILITY_VERSION = 1; 397 | DYLIB_CURRENT_VERSION = 1; 398 | FRAMEWORK_VERSION = A; 399 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 400 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 401 | GCC_PREFIX_HEADER = "CoreWebSocket/CoreWebSocket-Prefix.pch"; 402 | INFOPLIST_FILE = "CoreWebSocket/CoreWebSocket-Info.plist"; 403 | PRODUCT_NAME = "$(TARGET_NAME)"; 404 | WRAPPER_EXTENSION = framework; 405 | }; 406 | name = Release; 407 | }; 408 | EB6077911324E7FB00D98B17 /* Debug */ = { 409 | isa = XCBuildConfiguration; 410 | buildSettings = { 411 | CLANG_WARN_BOOL_CONVERSION = YES; 412 | CLANG_WARN_CONSTANT_CONVERSION = YES; 413 | CLANG_WARN_EMPTY_BODY = YES; 414 | CLANG_WARN_ENUM_CONVERSION = YES; 415 | CLANG_WARN_INT_CONVERSION = YES; 416 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 417 | GCC_C_LANGUAGE_STANDARD = gnu99; 418 | GCC_OPTIMIZATION_LEVEL = 0; 419 | GCC_PREPROCESSOR_DEFINITIONS = DEBUG; 420 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 421 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 422 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 423 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 424 | GCC_WARN_UNDECLARED_SELECTOR = YES; 425 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 426 | GCC_WARN_UNUSED_FUNCTION = YES; 427 | GCC_WARN_UNUSED_VARIABLE = YES; 428 | MACOSX_DEPLOYMENT_TARGET = 10.6; 429 | ONLY_ACTIVE_ARCH = YES; 430 | SDKROOT = macosx; 431 | USER_HEADER_SEARCH_PATHS = "\"$(SRCROOT)/\""; 432 | }; 433 | name = Debug; 434 | }; 435 | EB6077921324E7FB00D98B17 /* Release */ = { 436 | isa = XCBuildConfiguration; 437 | buildSettings = { 438 | CLANG_WARN_BOOL_CONVERSION = YES; 439 | CLANG_WARN_CONSTANT_CONVERSION = YES; 440 | CLANG_WARN_EMPTY_BODY = YES; 441 | CLANG_WARN_ENUM_CONVERSION = YES; 442 | CLANG_WARN_INT_CONVERSION = YES; 443 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 444 | GCC_C_LANGUAGE_STANDARD = gnu99; 445 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 446 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 447 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 448 | GCC_WARN_UNDECLARED_SELECTOR = YES; 449 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 450 | GCC_WARN_UNUSED_FUNCTION = YES; 451 | GCC_WARN_UNUSED_VARIABLE = YES; 452 | MACOSX_DEPLOYMENT_TARGET = 10.6; 453 | SDKROOT = macosx; 454 | USER_HEADER_SEARCH_PATHS = "\"$(SRCROOT)/\""; 455 | }; 456 | name = Release; 457 | }; 458 | EB6077941324E7FB00D98B17 /* Debug */ = { 459 | isa = XCBuildConfiguration; 460 | buildSettings = { 461 | ALWAYS_SEARCH_USER_PATHS = NO; 462 | COPY_PHASE_STRIP = NO; 463 | GCC_DYNAMIC_NO_PIC = NO; 464 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 465 | PRODUCT_NAME = "${TARGET_NAME}"; 466 | }; 467 | name = Debug; 468 | }; 469 | EB6077951324E7FB00D98B17 /* Release */ = { 470 | isa = XCBuildConfiguration; 471 | buildSettings = { 472 | ALWAYS_SEARCH_USER_PATHS = NO; 473 | COPY_PHASE_STRIP = YES; 474 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 475 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 476 | PRODUCT_NAME = "${TARGET_NAME}"; 477 | }; 478 | name = Release; 479 | }; 480 | EBB0359E1327F6FA005EF338 /* Debug */ = { 481 | isa = XCBuildConfiguration; 482 | buildSettings = { 483 | ALWAYS_SEARCH_USER_PATHS = NO; 484 | DSTROOT = /tmp/CoreWebSocket_iOS.dst; 485 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 486 | GCC_PREFIX_HEADER = "CoreWebSocket_iOS/CoreWebSocket_iOS-Prefix.pch"; 487 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 488 | IPHONEOS_DEPLOYMENT_TARGET = 4.3; 489 | OTHER_LDFLAGS = "-ObjC"; 490 | PRODUCT_NAME = "$(TARGET_NAME)"; 491 | SDKROOT = iphoneos; 492 | }; 493 | name = Debug; 494 | }; 495 | EBB0359F1327F6FA005EF338 /* Release */ = { 496 | isa = XCBuildConfiguration; 497 | buildSettings = { 498 | ALWAYS_SEARCH_USER_PATHS = NO; 499 | DSTROOT = /tmp/CoreWebSocket_iOS.dst; 500 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 501 | GCC_PREFIX_HEADER = "CoreWebSocket_iOS/CoreWebSocket_iOS-Prefix.pch"; 502 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 503 | IPHONEOS_DEPLOYMENT_TARGET = 4.3; 504 | OTHER_LDFLAGS = "-ObjC"; 505 | PRODUCT_NAME = "$(TARGET_NAME)"; 506 | SDKROOT = iphoneos; 507 | }; 508 | name = Release; 509 | }; 510 | /* End XCBuildConfiguration section */ 511 | 512 | /* Begin XCConfigurationList section */ 513 | EB131D811327F5C900C8F095 /* Build configuration list for PBXNativeTarget "CoreWebSocket" */ = { 514 | isa = XCConfigurationList; 515 | buildConfigurations = ( 516 | EB131D7F1327F5C900C8F095 /* Debug */, 517 | EB131D801327F5C900C8F095 /* Release */, 518 | ); 519 | defaultConfigurationIsVisible = 0; 520 | defaultConfigurationName = Release; 521 | }; 522 | EB6077811324E7FB00D98B17 /* Build configuration list for PBXProject "CoreWebSocket" */ = { 523 | isa = XCConfigurationList; 524 | buildConfigurations = ( 525 | EB6077911324E7FB00D98B17 /* Debug */, 526 | EB6077921324E7FB00D98B17 /* Release */, 527 | ); 528 | defaultConfigurationIsVisible = 0; 529 | defaultConfigurationName = Release; 530 | }; 531 | EB6077931324E7FB00D98B17 /* Build configuration list for PBXNativeTarget "CoreWebSocketConsole" */ = { 532 | isa = XCConfigurationList; 533 | buildConfigurations = ( 534 | EB6077941324E7FB00D98B17 /* Debug */, 535 | EB6077951324E7FB00D98B17 /* Release */, 536 | ); 537 | defaultConfigurationIsVisible = 0; 538 | defaultConfigurationName = Release; 539 | }; 540 | EBB035A01327F6FA005EF338 /* Build configuration list for PBXNativeTarget "CoreWebSocket_iOS" */ = { 541 | isa = XCConfigurationList; 542 | buildConfigurations = ( 543 | EBB0359E1327F6FA005EF338 /* Debug */, 544 | EBB0359F1327F6FA005EF338 /* Release */, 545 | ); 546 | defaultConfigurationIsVisible = 0; 547 | defaultConfigurationName = Release; 548 | }; 549 | /* End XCConfigurationList section */ 550 | }; 551 | rootObject = EB60777E1324E7FB00D98B17 /* Project object */; 552 | } 553 | -------------------------------------------------------------------------------- /CoreWebSocket/WebSocketClient.c: -------------------------------------------------------------------------------- 1 | // 2 | // The MIT License 3 | // 4 | // Copyright (c) 2011 - 2013, Mirek Rusin 5 | // http://github.com/mirek/CoreWebSocket 6 | // 7 | // Permission to use, copy, modify, and/or distribute this software for any 8 | // purpose with or without fee is hereby granted, provided that the above 9 | // copyright notice and this permission notice appear in all copies. 10 | // 11 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | // 19 | 20 | #include "WebSocketClient.h" 21 | //#include 22 | 23 | #define if_self if (self) 24 | 25 | #pragma mark Write 26 | 27 | // Internal, write provided buffer as websocket frame [0x00 buffer 0xff] 28 | CFIndex 29 | __WebSocketClientWriteFrameWithData (WebSocketClientRef client, WebSocketFrameOpCode opCode, Boolean isMasked, UInt8 *maskingKey, CFDataRef payload) { 30 | CFIndex result = 0; 31 | WebSocketFrameRef frame = WebSocketFrameCreateWithPayloadData(client->allocator, opCode, isMasked, maskingKey, payload); 32 | if (frame != NULL) { 33 | CFIndex length = CFDataGetLength(frame->data); 34 | while (CFWriteStreamCanAcceptBytes(client->write) && (result < length)) { 35 | CFIndex didWrite = CFWriteStreamWrite(client->write, WebSocketFrameGetBytesPtr(frame) + result, length - result); 36 | if (didWrite > 0) { 37 | result += didWrite; 38 | } 39 | } 40 | WebSocketFrameDealloc(frame); 41 | } 42 | return result; 43 | } 44 | 45 | // Write data as websocket frame (with binary frame opcode). 46 | CFIndex 47 | WebSocketClientWriteWithData (WebSocketClientRef self, CFDataRef value) { 48 | return __WebSocketClientWriteFrameWithData(self, kWebSocketFrameOpCodeBinary, FALSE, NULL, value); 49 | } 50 | 51 | // Write UTF-8 encoded string as a websocket frame (with text frame opcode). 52 | CFIndex 53 | WebSocketClientWriteWithString (WebSocketClientRef self, CFStringRef value) { 54 | CFIndex result = -1; 55 | if_self { 56 | if (value) { 57 | CFDataRef data = CFStringCreateExternalRepresentation(self->allocator, value, kCFStringEncodingUTF8, 0); 58 | if (data) { 59 | result = __WebSocketClientWriteFrameWithData(self, kWebSocketFrameOpCodeText, FALSE, NULL, data); 60 | CFRelease(data); 61 | } 62 | } 63 | } 64 | return result; 65 | } 66 | 67 | // Write UTF-8 encoded string as a websocket frame (with text frame opcode). 68 | CFIndex 69 | WebSocketClientWriteWithFormat (WebSocketClientRef self, CFStringRef fmt, ...) { 70 | CFIndex result = -1; 71 | if_self if (fmt != NULL) { 72 | va_list args; 73 | va_start(args, fmt); 74 | CFStringRef string = CFStringCreateWithFormatAndArguments(self->allocator, NULL, fmt, args); 75 | va_end(args); 76 | if (string != NULL) { 77 | result = WebSocketClientWriteWithString(self, string); 78 | CFRelease(string); 79 | } 80 | } 81 | return result; 82 | } 83 | 84 | #pragma mark Read callback 85 | 86 | Boolean 87 | __WebSocketClientWriteHandShake (WebSocketClientRef client); 88 | 89 | void 90 | __WebSocketClientReadFrame (WebSocketClientRef self, CFReadStreamRef stream) { 91 | 92 | // Reset last frame if it has been completed. 93 | if (WebSocketFrameGetState(self->frame) == kWebSocketFrameStateReady) { 94 | WebSocketFrameReset(self->frame); 95 | } 96 | 97 | // Did handshake already and there are bytes to read. 98 | // It's an incomming message. 99 | { 100 | CFIndex size = 64 * 1024; 101 | UInt8 *buffer = (UInt8 *) CFAllocatorAllocate(self->allocator, size, 0); 102 | if (buffer != NULL) { 103 | CFIndex didRead = 0; 104 | while (CFReadStreamHasBytesAvailable(self->read)) { 105 | if ((didRead = CFReadStreamRead(stream, buffer, size)) != -1) { 106 | WebSocketFrameAppend(self->frame, buffer, didRead); 107 | } else { 108 | break; 109 | } 110 | } 111 | CFAllocatorDeallocate(self->allocator, buffer); 112 | } 113 | } 114 | 115 | if (WebSocketFrameParse(self->frame) == kWebSocketFrameStateReady) { 116 | CFStringRef string = WebSocketFrameCopyPayloadString(self->frame, kCFStringEncodingUTF8); 117 | if (string) { 118 | self->webSocket->callbacks.didClientReadCallback(self->webSocket, self, string); 119 | CFRelease(string); 120 | } 121 | } 122 | } 123 | 124 | void 125 | __WebSocketClientReadCallBack (CFReadStreamRef stream, CFStreamEventType eventType, void *info) { 126 | WebSocketClientRef self = (WebSocketClientRef) info; 127 | if (self) { 128 | switch (eventType) { 129 | case kCFStreamEventOpenCompleted: 130 | break; 131 | 132 | case kCFStreamEventHasBytesAvailable: 133 | if (!self->didReadHandShake) { 134 | if (__WebSocketClientReadHandShake(self)) { 135 | if (!self->didWriteHandShake) { 136 | if (CFWriteStreamCanAcceptBytes(self->write)) { 137 | if (__WebSocketClientWriteHandShake(self)) { 138 | // printf("Successfully written handshake\n"); 139 | } else { 140 | printf("TODO: Error writting handshake\n"); 141 | } 142 | } else { 143 | // printf("TODO: Didn't handshake and client doesn't accept bytes yet. Write callback will handle writting handshake as soon as we can write.\n"); 144 | } 145 | } else { 146 | printf("TODO: Just read handshake and handshake already written, shouldn't happen, fault?\n"); 147 | } 148 | __WebSocketAppendClient(self->webSocket, self); 149 | } else { 150 | printf("TODO: Didn't read handshake and __WebSocketClientReadHandShake failed.\n"); 151 | } 152 | } else { 153 | 154 | __WebSocketClientReadFrame(self, stream); 155 | 156 | } 157 | break; 158 | 159 | case kCFStreamEventErrorOccurred: 160 | break; 161 | 162 | case kCFStreamEventEndEncountered: 163 | break; 164 | 165 | default: 166 | break; 167 | } 168 | } 169 | } 170 | 171 | Boolean 172 | __WebSocketClientWriteWithHTTPMessage (WebSocketClientRef client, CFHTTPMessageRef message) { 173 | Boolean result = FALSE; 174 | if (client && message) { 175 | CFDataRef data = CFHTTPMessageCopySerializedMessage(message); 176 | if (data) { 177 | CFIndex written = CFWriteStreamWrite(client->write, CFDataGetBytePtr(data), CFDataGetLength(data)); 178 | if (written == CFDataGetLength(data)) { 179 | result = TRUE; // TODO: do it properly 180 | } 181 | client->didWriteHandShake = 1; 182 | CFRelease(data); 183 | } 184 | } 185 | return result; 186 | } 187 | 188 | Boolean 189 | __WebSocketClientWriteHandShakeDraftIETF_HYBI_00 (WebSocketClientRef client) { 190 | Boolean result = FALSE; 191 | if (client) { 192 | if (client->protocol == kWebSocketProtocolDraftIETF_HYBI_00) { 193 | CFStringRef key1 = CFHTTPMessageCopyHeaderFieldValue(client->handShakeRequestHTTPMessage, CFSTR("Sec-Websocket-Key1")); 194 | CFStringRef key2 = CFHTTPMessageCopyHeaderFieldValue(client->handShakeRequestHTTPMessage, CFSTR("Sec-Websocket-Key2")); 195 | CFDataRef key3 = CFHTTPMessageCopyBody(client->handShakeRequestHTTPMessage); 196 | CFStringRef origin = CFHTTPMessageCopyHeaderFieldValue(client->handShakeRequestHTTPMessage, CFSTR("Origin")); 197 | CFStringRef host = CFHTTPMessageCopyHeaderFieldValue(client->handShakeRequestHTTPMessage, CFSTR("Host")); 198 | 199 | CFHTTPMessageRef response = CFHTTPMessageCreateEmpty(NULL, 0); 200 | CFHTTPMessageAppendBytes(response, (const UInt8 *)"HTTP/1.1 101 Web Socket Protocol Handshake\r\n", 44); 201 | CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Upgrade"), CFSTR("WebSocket")); 202 | CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Connection"), CFSTR("Upgrade")); 203 | CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Sec-Websocket-Origin"), origin); 204 | 205 | CFMutableStringRef location = CFStringCreateMutable(client->allocator, 0); 206 | CFStringAppend(location, CFSTR("ws://")); 207 | CFStringAppend(location, host); 208 | CFStringAppend(location, CFSTR("/")); 209 | CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Sec-Websocket-Location"), location); 210 | CFRelease(location); 211 | 212 | // Set MD5 hash 213 | { 214 | CFMutableDataRef mutable = CFDataCreateMutable(client->allocator, 0); 215 | __WebSocketDataAppendMagickNumberWithKeyValueString(mutable, key1); 216 | __WebSocketDataAppendMagickNumberWithKeyValueString(mutable, key2); 217 | CFDataAppendBytes(mutable, CFDataGetBytePtr(key3), CFDataGetLength(key3)); 218 | CFDataRef data = __WebSocketCreateMD5Data(client->allocator, mutable); 219 | CFHTTPMessageSetBody(response, data); 220 | CFRelease(mutable); 221 | CFRelease(data); 222 | } 223 | 224 | result = __WebSocketClientWriteWithHTTPMessage(client, response) ? TRUE : FALSE; 225 | 226 | CFRelease(response); 227 | 228 | CFRelease(host); 229 | CFRelease(origin); 230 | CFRelease(key3); 231 | CFRelease(key2); 232 | CFRelease(key1); 233 | } 234 | } 235 | return result; 236 | } 237 | 238 | // The source code has been copied and modified from 239 | // http://www.opensource.apple.com/source/CFNetwork/CFNetwork-128/HTTP/CFHTTPAuthentication.c 240 | // See _CFEncodeBase64 function. The source code has been released under 241 | // Apple Public Source License Version 2.0 http://www.opensource.apple.com/apsl/ 242 | CFStringRef 243 | __WebSocketCreateBase64StringWithData (CFAllocatorRef allocator, CFDataRef inputData) { 244 | unsigned outDataLen; 245 | CFStringRef result = NULL; 246 | unsigned char *outData = cuEnc64(CFDataGetBytePtr(inputData), (unsigned int)CFDataGetLength(inputData), &outDataLen); 247 | if(outData) { 248 | // current cuEnc64 appends \n and NULL, trim them 249 | unsigned char *c = outData + outDataLen - 1; 250 | while((*c == '\n') || (*c == '\0')) { 251 | c--; 252 | outDataLen--; 253 | } 254 | result = CFStringCreateWithBytes(allocator, outData, outDataLen, kCFStringEncodingASCII, FALSE); 255 | free(outData); 256 | } 257 | return result; 258 | } 259 | 260 | Boolean 261 | __WebSocketClientWriteHandShakeDraftIETF_HYBI_06 (WebSocketClientRef client) { 262 | Boolean success = 0; 263 | if (client) { 264 | if (client->protocol == kWebSocketProtocolDraftIETF_HYBI_06) { 265 | 266 | CFStringRef key = CFHTTPMessageCopyHeaderFieldValue(client->handShakeRequestHTTPMessage, CFSTR("Sec-WebSocket-Key")); 267 | CFStringRef keyWithMagick = CFStringCreateWithFormat(client->allocator, NULL, CFSTR("%@%@"), key, CFSTR("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")); 268 | CFDataRef keyWithMagickSHA1 = __WebSocketCreateSHA1DataWithString(client->allocator, keyWithMagick, kCFStringEncodingUTF8); 269 | CFStringRef keyWithMagickSHA1Base64 = __WebSocketCreateBase64StringWithData(client->allocator, keyWithMagickSHA1); 270 | 271 | CFStringRef origin = CFHTTPMessageCopyHeaderFieldValue(client->handShakeRequestHTTPMessage, CFSTR("Sec-WebSocket-Origin")); 272 | CFStringRef host = CFHTTPMessageCopyHeaderFieldValue(client->handShakeRequestHTTPMessage, CFSTR("Host")); 273 | 274 | CFHTTPMessageRef response = CFHTTPMessageCreateEmpty(NULL, 0); 275 | CFHTTPMessageAppendBytes(response, (const UInt8 *)"HTTP/1.1 101 Switching Protocols\r\n", 44); 276 | CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Upgrade"), CFSTR("websocket")); 277 | CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Connection"), CFSTR("Upgrade")); 278 | CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Sec-WebSocket-Accept"), keyWithMagickSHA1Base64); 279 | 280 | success = __WebSocketClientWriteWithHTTPMessage(client, response); 281 | 282 | CFRelease(response); 283 | 284 | CFRelease(keyWithMagickSHA1Base64); 285 | CFRelease(keyWithMagickSHA1); 286 | CFRelease(keyWithMagick); 287 | CFRelease(key); 288 | CFRelease(origin); 289 | CFRelease(host); 290 | } 291 | } 292 | return success; 293 | } 294 | 295 | Boolean 296 | __WebSocketClientWriteHandShakeRFC6455_13 (WebSocketClientRef client) { 297 | Boolean success = 0; 298 | if (client) { 299 | if (client->protocol == kWebSocketProtocol_RFC6455_13) { 300 | 301 | CFStringRef key = CFHTTPMessageCopyHeaderFieldValue(client->handShakeRequestHTTPMessage, CFSTR("Sec-WebSocket-Key")); 302 | CFStringRef keyWithMagick = CFStringCreateWithFormat(client->allocator, NULL, CFSTR("%@%@"), key, CFSTR("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")); 303 | CFDataRef keyWithMagickSHA1 = __WebSocketCreateSHA1DataWithString(client->allocator, keyWithMagick, kCFStringEncodingUTF8); 304 | CFStringRef keyWithMagickSHA1Base64 = __WebSocketCreateBase64StringWithData(client->allocator, keyWithMagickSHA1); 305 | 306 | // CFStringRef host = CFHTTPMessageCopyHeaderFieldValue(client->handShakeRequestHTTPMessage, CFSTR("Host")); 307 | 308 | CFHTTPMessageRef response = CFHTTPMessageCreateEmpty(NULL, 0); 309 | CFHTTPMessageAppendBytes(response, (const UInt8 *)"HTTP/1.1 101 Switching Protocols\r\n", 44); 310 | CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Upgrade"), CFSTR("websocket")); 311 | CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Connection"), CFSTR("Upgrade")); 312 | CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Sec-WebSocket-Accept"), keyWithMagickSHA1Base64); 313 | 314 | success = __WebSocketClientWriteWithHTTPMessage(client, response); 315 | 316 | CFRelease(response); 317 | 318 | CFRelease(keyWithMagickSHA1Base64); 319 | CFRelease(keyWithMagickSHA1); 320 | CFRelease(keyWithMagick); 321 | CFRelease(key); 322 | // CFRelease(host); 323 | } 324 | } 325 | return success; 326 | } 327 | 328 | Boolean 329 | __WebSocketClientWriteHandShake (WebSocketClientRef self) { 330 | Boolean success = 0; 331 | if (self->didReadHandShake) { 332 | if (!self->didWriteHandShake) { 333 | switch (self->protocol) { 334 | case kWebSocketProtocolDraftIETF_HYBI_00: 335 | success = __WebSocketClientWriteHandShakeDraftIETF_HYBI_00(self); 336 | break; 337 | 338 | case kWebSocketProtocolDraftIETF_HYBI_06: 339 | success = __WebSocketClientWriteHandShakeDraftIETF_HYBI_06(self); 340 | break; 341 | 342 | case kWebSocketProtocol_RFC6455_13: 343 | success = __WebSocketClientWriteHandShakeRFC6455_13(self); 344 | break; 345 | 346 | default: 347 | printf("Unknown protocol, can't write handshake. TODO: disconnect\n"); 348 | // Unknown protocol, can't write handshake 349 | break; 350 | } 351 | } 352 | } 353 | return success; 354 | } 355 | 356 | void 357 | __WebSocketClientWriteCallBack (CFWriteStreamRef stream, CFStreamEventType eventType, void *info) { 358 | WebSocketClientRef client = info; 359 | if (client) { 360 | switch (eventType) { 361 | 362 | case kCFStreamEventCanAcceptBytes: 363 | if (!client->didWriteHandShake && client->didReadHandShake) 364 | __WebSocketClientWriteHandShake(client); 365 | break; 366 | 367 | case kCFStreamEventEndEncountered: 368 | break; 369 | 370 | case kCFStreamEventErrorOccurred: 371 | printf("kCFStreamEventErrorOccurred (write)\n"); 372 | CFErrorRef error = CFWriteStreamCopyError(stream); 373 | if (error) { 374 | CFShow(error); 375 | CFRelease(error); 376 | } 377 | break; 378 | 379 | default: 380 | break; 381 | } 382 | } 383 | } 384 | 385 | #pragma mark Lifecycle 386 | 387 | WebSocketClientRef 388 | WebSocketClientCreate (WebSocketRef webSocket, CFSocketNativeHandle handle) { 389 | WebSocketClientRef self = NULL; 390 | if (webSocket) { 391 | self = CFAllocatorAllocate(webSocket->allocator, sizeof(WebSocketClient), 0); 392 | if (self) { 393 | self->allocator = webSocket->allocator ? CFRetain(webSocket->allocator) : NULL; 394 | self->retainCount = 1; 395 | 396 | WebSocketRetain(webSocket), self->webSocket = webSocket; 397 | self->handle = handle; 398 | 399 | self->frame = WebSocketFrameCreate(self->allocator); 400 | 401 | self->read = NULL; 402 | self->write = NULL; 403 | 404 | self->context.version = 0; 405 | self->context.info = self; 406 | self->context.copyDescription = NULL; 407 | self->context.retain = NULL; 408 | self->context.release = NULL; 409 | 410 | self->handShakeRequestHTTPMessage = NULL; 411 | self->didReadHandShake = 0; 412 | self->didWriteHandShake = 0; 413 | self->protocol = kWebSocketProtocolUnknown; 414 | 415 | CFStreamCreatePairWithSocket(self->allocator, handle, &self->read, &self->write); 416 | if (!self->read || !self->write) { 417 | close(handle); 418 | fprintf(stderr, "CFStreamCreatePairWithSocket() failed, %p, %p\n", read, write); 419 | } else { 420 | // printf("ok\n"); 421 | } 422 | 423 | CFOptionFlags flags = kCFStreamEventOpenCompleted | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered; 424 | CFReadStreamSetClient(self->read, flags | kCFStreamEventHasBytesAvailable, __WebSocketClientReadCallBack, &self->context); 425 | CFWriteStreamSetClient(self->write, flags | kCFStreamEventCanAcceptBytes, __WebSocketClientWriteCallBack, &self->context); 426 | 427 | CFReadStreamScheduleWithRunLoop(self->read, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); 428 | CFWriteStreamScheduleWithRunLoop(self->write, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); 429 | 430 | if (!CFReadStreamOpen(self->read)) { 431 | printf("couldn't open read stream\n"); 432 | } else { 433 | // printf("opened read stream\n"); 434 | } 435 | 436 | if (!CFWriteStreamOpen(self->write)) { 437 | printf("couldn't open write stream\n"); 438 | } else { 439 | // printf("opened write stream\n"); 440 | } 441 | } 442 | } 443 | return self; 444 | } 445 | 446 | void 447 | WebSocketClientRetain (WebSocketClientRef self) { 448 | if_self { 449 | ++self->retainCount; 450 | } 451 | } 452 | 453 | void 454 | WebSocketClientRelease (WebSocketClientRef self) { 455 | if_self { 456 | if (--self->retainCount == 0) { 457 | CFAllocatorRef allocator = self->allocator; 458 | 459 | if (self->read) { 460 | if (CFReadStreamGetStatus(self->read) != kCFStreamStatusClosed) 461 | CFReadStreamClose(self->read); 462 | CFRelease(self->read); 463 | self->read = NULL; 464 | } 465 | 466 | if (self->write) { 467 | if (CFWriteStreamGetStatus(self->write) != kCFStreamStatusClosed) 468 | CFWriteStreamClose(self->write); 469 | CFRelease(self->write); 470 | self->write = NULL; 471 | } 472 | 473 | if (self->frame != NULL) { 474 | WebSocketFrameRelease(self->frame), self->frame = NULL; 475 | } 476 | 477 | CFAllocatorDeallocate(allocator, self); 478 | self = NULL; 479 | 480 | if (allocator) 481 | CFRelease(allocator); 482 | } 483 | } 484 | } 485 | 486 | #pragma Handshake 487 | 488 | // Return magic number for the key needed to generate handshake hash 489 | uint32_t 490 | __WebSocketGetMagicNumberWithKeyValueString (CFStringRef string) { 491 | uint32_t magick = -1; 492 | if (string) { 493 | UInt8 buffer[__WebSocketMaxHeaderKeyLength]; 494 | CFIndex usedBufferLength = 0; 495 | char numberBuffer[__WebSocketMaxHeaderKeyLength]; 496 | memset(numberBuffer, 0, sizeof(numberBuffer)); 497 | CFIndex usedNumberBufferLength = 0; 498 | CFStringGetBytes(string, CFRangeMake(0, CFStringGetLength(string)), kCFStringEncodingASCII, 0, 0, buffer, sizeof(buffer), &usedBufferLength); 499 | UInt32 number = 0; 500 | UInt32 spaces = 0; 501 | for (int i = 0; i < usedBufferLength; i++) { 502 | if (buffer[i] >= '0' && buffer[i] <= '9') 503 | numberBuffer[usedNumberBufferLength++] = buffer[i]; 504 | if (buffer[i] == ' ') 505 | spaces++; 506 | } 507 | if (spaces > 0) { 508 | number = (UInt32)strtoul(numberBuffer, NULL, 10); 509 | magick = number / spaces; 510 | } 511 | } 512 | return magick; 513 | } 514 | 515 | // Appends big-endian uint32 magic number with key string to the mutable data 516 | Boolean 517 | __WebSocketDataAppendMagickNumberWithKeyValueString (CFMutableDataRef data, CFStringRef string) { 518 | Boolean success = 0; 519 | if (data && string) { 520 | uint32_t magick = __WebSocketGetMagicNumberWithKeyValueString(string); 521 | uint32_t swapped = CFSwapInt32HostToBig(magick); 522 | CFDataAppendBytes(data, (const void *)&swapped, sizeof(swapped)); 523 | success = 1; 524 | } 525 | return success; 526 | } 527 | 528 | CFDataRef 529 | __WebSocketCreateMD5Data (CFAllocatorRef allocator, CFDataRef value) { 530 | unsigned char digest[CC_MD5_DIGEST_LENGTH]; 531 | CC_MD5((unsigned char *)CFDataGetBytePtr(value), (CC_LONG)CFDataGetLength(value), digest); 532 | return CFDataCreate(allocator, digest, CC_MD5_DIGEST_LENGTH); 533 | } 534 | 535 | CFDataRef 536 | __WebSocketCreateSHA1DataWithData (CFAllocatorRef allocator, CFDataRef value) { 537 | unsigned char digest[CC_SHA1_DIGEST_LENGTH]; 538 | CC_SHA1((unsigned char *)CFDataGetBytePtr(value), (CC_LONG)CFDataGetLength(value), digest); 539 | return CFDataCreate(allocator, digest, CC_SHA1_DIGEST_LENGTH); 540 | } 541 | 542 | CFDataRef 543 | __WebSocketCreateSHA1DataWithString (CFAllocatorRef allocator, CFStringRef value, CFStringEncoding encoding) { 544 | CFDataRef data = NULL; 545 | if (value) { 546 | CFDataRef valueData = CFStringCreateExternalRepresentation(allocator, value, encoding, 0); 547 | if (valueData) { 548 | data = __WebSocketCreateSHA1DataWithData(allocator, valueData); 549 | CFRelease(valueData); 550 | } 551 | } 552 | return data; 553 | } 554 | 555 | Boolean 556 | __WebSocketClientHandShakeConsumeHTTPMessage (WebSocketClientRef client) { 557 | Boolean success = 0; 558 | if (client) { 559 | UInt8 buffer[4096]; 560 | CFIndex bytes = 0; 561 | client->handShakeRequestHTTPMessage = CFHTTPMessageCreateEmpty(client->allocator, 1); 562 | while (CFReadStreamHasBytesAvailable(client->read)) { 563 | if ((bytes = CFReadStreamRead(client->read, buffer, sizeof(buffer))) > 0) { 564 | CFHTTPMessageAppendBytes(client->handShakeRequestHTTPMessage, buffer, bytes); 565 | } else if (bytes < 0) { 566 | CFErrorRef error = CFReadStreamCopyError(client->read); 567 | CFShow(error); 568 | CFRelease(error); 569 | goto fin; 570 | } 571 | } 572 | success = 1; 573 | } 574 | 575 | fin: 576 | return success; 577 | } 578 | 579 | // Private method 580 | 581 | Boolean 582 | __WebSocketClientHandShakeUpdateProtocolBasedOnHTTPMessage (WebSocketClientRef self) { 583 | Boolean result = FALSE; 584 | if (self) { 585 | 586 | // Get the protocol version 587 | self->protocol = kWebSocketProtocolUnknown; 588 | 589 | // Is Upgrade header available? It has to for ws... 590 | CFStringRef upgrade = CFHTTPMessageCopyHeaderFieldValue(self->handShakeRequestHTTPMessage, CFSTR("Upgrade")); 591 | if (upgrade) { 592 | if (CFStringCompare(CFSTR("WebSocket"), upgrade, kCFCompareCaseInsensitive) == kCFCompareEqualTo) { 593 | CFStringRef version = CFHTTPMessageCopyHeaderFieldValue(self->handShakeRequestHTTPMessage, CFSTR("Sec-WebSocket-Version")); 594 | if (version) { 595 | if (CFStringCompare(CFSTR("6"), version, kCFCompareCaseInsensitive) == kCFCompareEqualTo) { 596 | self->protocol = kWebSocketProtocolDraftIETF_HYBI_06; 597 | result = TRUE; 598 | } else if (CFStringCompare(CFSTR("13"), version, kCFCompareCaseInsensitive) == kCFCompareEqualTo) { 599 | self->protocol = kWebSocketProtocol_RFC6455_13; 600 | result = TRUE; 601 | } else { 602 | 603 | // Version different than 6, we don't know of any other, leave the protocol as unknown 604 | } 605 | CFRelease(version); 606 | } else { 607 | 608 | // Sec-WebSocket-Version header field is missing. 609 | // It may be 00 protocol, which doesn't have this field. 610 | // 00 protocol has to have Sec-WebSocket-Key1 and Sec-WebSocket-Key2 611 | // fields - let's check for those. 612 | CFStringRef key1 = CFHTTPMessageCopyHeaderFieldValue(self->handShakeRequestHTTPMessage, CFSTR("Sec-WebSocket-Key1")); 613 | if (key1) { 614 | CFStringRef key2 = CFHTTPMessageCopyHeaderFieldValue(self->handShakeRequestHTTPMessage, CFSTR("Sec-WebSocket-Key2")); 615 | if (key2) { 616 | self->protocol = kWebSocketProtocolDraftIETF_HYBI_00; 617 | result = TRUE; 618 | CFRelease(key2); 619 | } 620 | CFRelease(key1); 621 | } else { 622 | 623 | // Key2 missing, no version specified = unknown protocol 624 | } 625 | } 626 | } else { 627 | 628 | // Upgrade HTTP field seems to be different from "WebSocket" (case ignored) 629 | } 630 | CFRelease(upgrade); 631 | } else { 632 | 633 | // Upgrade HTTP field seems to be absent 634 | } 635 | } 636 | return result; 637 | } 638 | 639 | Boolean 640 | __WebSocketClientReadHandShake (WebSocketClientRef self) { 641 | Boolean result = FALSE; 642 | if_self { 643 | if ((result = __WebSocketClientHandShakeConsumeHTTPMessage(self))) { 644 | if ((result = __WebSocketClientHandShakeUpdateProtocolBasedOnHTTPMessage(self))) { 645 | 646 | // Dump http message: 647 | // 648 | // CFDictionaryRef headerFields = CFHTTPMessageCopyAllHeaderFields(self->handShakeRequestHTTPMessage); 649 | // if (headerFields) { 650 | // CFRelease(headerFields); 651 | // } 652 | } 653 | } 654 | self->didReadHandShake = TRUE; 655 | } 656 | return result; 657 | } 658 | 659 | --------------------------------------------------------------------------------