Rename socket_ to listeningSocket_ and add CloseSocket() to help shut it down
[macgdbp.git] / Source / NetworkCallbackController.mm
1 /*
2 * MacGDBp
3 * Copyright (c) 2007 - 2011, Blue Static <http://www.bluestatic.org>
4 *
5 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
6 * General Public License as published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
10 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along with this program; if not,
14 * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17 #import "NetworkCallbackController.h"
18
19 #import <sys/socket.h>
20 #import <netinet/in.h>
21
22 #import "NetworkConnection.h"
23 #import "NetworkConnectionPrivate.h"
24
25 NetworkCallbackController::NetworkCallbackController(NetworkConnection* connection)
26 : readStream_(NULL),
27 writeStream_(NULL),
28 connection_(connection),
29 runLoop_(CFRunLoopGetCurrent())
30 {
31 }
32
33 void NetworkCallbackController::OpenConnection(NSUInteger port)
34 {
35 // Pass ourselves to the callback so we don't have to use ugly globals.
36 CFSocketContext context = { 0 };
37 context.info = this;
38
39 // Create the address structure.
40 struct sockaddr_in address;
41 memset(&address, 0, sizeof(address));
42 address.sin_len = sizeof(address);
43 address.sin_family = AF_INET;
44 address.sin_port = htons(port);
45 address.sin_addr.s_addr = htonl(INADDR_ANY);
46
47 // Create the socket signature.
48 CFSocketSignature signature;
49 signature.protocolFamily = PF_INET;
50 signature.socketType = SOCK_STREAM;
51 signature.protocol = IPPROTO_TCP;
52 signature.address = (CFDataRef)[NSData dataWithBytes:&address length:sizeof(address)];
53
54 do {
55 listeningSocket_ =
56 CFSocketCreateWithSocketSignature(kCFAllocatorDefault,
57 &signature, // Socket signature.
58 kCFSocketAcceptCallBack, // Callback types.
59 &NetworkCallbackController::SocketAcceptCallback, // Callout function pointer.
60 &context); // Context to pass to callout.
61 if (!listeningSocket_) {
62 [connection_ errorEncountered:@"Could not open socket."];
63 sleep(1);
64 }
65 } while (!listeningSocket_);
66
67 // Allow old, yet-to-be recycled sockets to be reused.
68 BOOL yes = YES;
69 setsockopt(CFSocketGetNative(listeningSocket_), SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(BOOL));
70 setsockopt(CFSocketGetNative(listeningSocket_), SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(BOOL));
71
72 // Schedule the socket on the run loop.
73 CFRunLoopSourceRef source = CFSocketCreateRunLoopSource(kCFAllocatorDefault, listeningSocket_, 0);
74 CFRunLoopAddSource(runLoop_, source, kCFRunLoopCommonModes);
75 CFRelease(source);
76 }
77
78 void NetworkCallbackController::CloseConnection()
79 {
80 CloseSocket();
81 UnscheduleReadStream();
82 UnscheduleWriteStream();
83 }
84
85 BOOL NetworkCallbackController::WriteStreamCanAcceptBytes()
86 {
87 return writeStream_ && CFWriteStreamCanAcceptBytes(writeStream_);
88 }
89
90 BOOL NetworkCallbackController::WriteString(NSString* string)
91 {
92 BOOL done = NO;
93
94 char* cString = const_cast<char*>([string UTF8String]);
95 size_t stringLength = strlen(cString);
96
97 // Busy wait while writing. BAADD. Should background this operation.
98 while (!done) {
99 if (WriteStreamCanAcceptBytes()) {
100 // Include the NULL byte in the string when we write.
101 CFIndex bytesWritten = CFWriteStreamWrite(writeStream_, (UInt8*)cString, stringLength + 1);
102 if (bytesWritten < 0) {
103 CFErrorRef error = CFWriteStreamCopyError(writeStream_);
104 ReportError(error);
105 break;
106 }
107 // Incomplete write.
108 else if (bytesWritten < static_cast<CFIndex>(strlen(cString))) {
109 // Adjust the buffer and wait for another chance to write.
110 stringLength -= bytesWritten;
111 memmove(string, string + bytesWritten, stringLength);
112 }
113 else {
114 done = YES;
115 }
116 }
117 }
118
119 return done;
120 }
121
122 // Static Methods //////////////////////////////////////////////////////////////
123
124 void NetworkCallbackController::SocketAcceptCallback(CFSocketRef socket,
125 CFSocketCallBackType callbackType,
126 CFDataRef address,
127 const void* data,
128 void* self)
129 {
130 assert(callbackType == kCFSocketAcceptCallBack);
131 static_cast<NetworkCallbackController*>(self)->OnSocketAccept(socket, address, data);
132 }
133
134 void NetworkCallbackController::ReadStreamCallback(CFReadStreamRef stream,
135 CFStreamEventType eventType,
136 void* self)
137 {
138 static_cast<NetworkCallbackController*>(self)->OnReadStreamEvent(stream, eventType);
139 }
140
141 void NetworkCallbackController::WriteStreamCallback(CFWriteStreamRef stream,
142 CFStreamEventType eventType,
143 void* self)
144 {
145 static_cast<NetworkCallbackController*>(self)->OnWriteStreamEvent(stream, eventType);
146 }
147
148
149 // Private Instance Methods ////////////////////////////////////////////////////
150
151 void NetworkCallbackController::OnSocketAccept(CFSocketRef socket,
152 CFDataRef address,
153 const void* data)
154 {
155 // Create the streams on the socket.
156 CFStreamCreatePairWithSocket(kCFAllocatorDefault,
157 *(CFSocketNativeHandle*)data, // Socket handle.
158 &readStream_, // Read stream in-pointer.
159 &writeStream_); // Write stream in-pointer.
160
161 // Create struct to register callbacks for the stream.
162 CFStreamClientContext context = { 0 };
163 context.info = this;
164
165 // Set the client of the read stream.
166 CFOptionFlags readFlags = kCFStreamEventOpenCompleted |
167 kCFStreamEventHasBytesAvailable |
168 kCFStreamEventErrorOccurred |
169 kCFStreamEventEndEncountered;
170 if (CFReadStreamSetClient(readStream_, readFlags, &NetworkCallbackController::ReadStreamCallback, &context))
171 // Schedule in run loop to do asynchronous communication with the engine.
172 CFReadStreamScheduleWithRunLoop(readStream_, runLoop_, kCFRunLoopCommonModes);
173 else
174 return;
175
176 // Open the stream now that it's scheduled on the run loop.
177 if (!CFReadStreamOpen(readStream_)) {
178 ReportError(CFReadStreamCopyError(readStream_));
179 return;
180 }
181
182 // Set the client of the write stream.
183 CFOptionFlags writeFlags = kCFStreamEventOpenCompleted |
184 kCFStreamEventCanAcceptBytes |
185 kCFStreamEventErrorOccurred |
186 kCFStreamEventEndEncountered;
187 if (CFWriteStreamSetClient(writeStream_, writeFlags, &NetworkCallbackController::WriteStreamCallback, &context))
188 // Schedule it in the run loop to receive error information.
189 CFWriteStreamScheduleWithRunLoop(writeStream_, runLoop_, kCFRunLoopCommonModes);
190 else
191 return;
192
193 // Open the write stream.
194 if (!CFWriteStreamOpen(writeStream_)) {
195 ReportError(CFWriteStreamCopyError(writeStream_));
196 return;
197 }
198
199 [connection_ socketDidAccept];
200
201 CloseSocket();
202 }
203
204 void NetworkCallbackController::OnReadStreamEvent(CFReadStreamRef stream,
205 CFStreamEventType eventType)
206 {
207 switch (eventType)
208 {
209 case kCFStreamEventHasBytesAvailable:
210 if (readStream_)
211 [connection_ readStreamHasData:stream];
212 break;
213
214 case kCFStreamEventErrorOccurred:
215 ReportError(CFReadStreamCopyError(stream));
216 UnscheduleReadStream();
217 break;
218
219 case kCFStreamEventEndEncountered:
220 UnscheduleReadStream();
221 [connection_ socketDisconnected];
222 break;
223 };
224 }
225
226 void NetworkCallbackController::OnWriteStreamEvent(CFWriteStreamRef stream,
227 CFStreamEventType eventType)
228 {
229 switch (eventType)
230 {
231 case kCFStreamEventCanAcceptBytes:
232 [connection_ sendQueuedWrites];
233 break;
234
235 case kCFStreamEventErrorOccurred:
236 ReportError(CFWriteStreamCopyError(stream));
237 UnscheduleWriteStream();
238 break;
239
240 case kCFStreamEventEndEncountered:
241 UnscheduleReadStream();
242 [connection_ socketDisconnected];
243 break;
244 }
245 }
246
247 void NetworkCallbackController::CloseSocket()
248 {
249 if (listeningSocket_) {
250 CFSocketInvalidate(listeningSocket_);
251 CFRelease(listeningSocket_);
252 listeningSocket_ = NULL;
253 }
254 }
255
256 void NetworkCallbackController::UnscheduleReadStream()
257 {
258 if (!readStream_)
259 return;
260 CFReadStreamUnscheduleFromRunLoop(readStream_, runLoop_, kCFRunLoopCommonModes);
261 CFReadStreamClose(readStream_);
262 CFRelease(readStream_);
263 readStream_ = NULL;
264 }
265
266 void NetworkCallbackController::UnscheduleWriteStream()
267 {
268 if (!writeStream_)
269 return;
270 CFWriteStreamUnscheduleFromRunLoop(writeStream_, runLoop_, kCFRunLoopCommonModes);
271 CFWriteStreamClose(writeStream_);
272 CFRelease(writeStream_);
273 writeStream_ = NULL;
274 }
275
276 void NetworkCallbackController::ReportError(CFErrorRef error)
277 {
278 [connection_ errorEncountered:[(NSError*)error description]];
279 CFRelease(error);
280 }