Redo how socket shutdown works:
[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 : listeningSocket_(NULL),
27 socketHandle_(NULL),
28 readStream_(NULL),
29 writeStream_(NULL),
30 connection_(connection),
31 runLoop_(CFRunLoopGetCurrent())
32 {
33 }
34
35 void NetworkCallbackController::OpenConnection(NSUInteger port)
36 {
37 // Pass ourselves to the callback so we don't have to use ugly globals.
38 CFSocketContext context = { 0 };
39 context.info = this;
40
41 // Create the address structure.
42 struct sockaddr_in address;
43 memset(&address, 0, sizeof(address));
44 address.sin_len = sizeof(address);
45 address.sin_family = AF_INET;
46 address.sin_port = htons(port);
47 address.sin_addr.s_addr = htonl(INADDR_ANY);
48
49 // Create the socket signature.
50 CFSocketSignature signature;
51 signature.protocolFamily = PF_INET;
52 signature.socketType = SOCK_STREAM;
53 signature.protocol = IPPROTO_TCP;
54 signature.address = (CFDataRef)[NSData dataWithBytes:&address length:sizeof(address)];
55
56 do {
57 listeningSocket_ =
58 CFSocketCreateWithSocketSignature(kCFAllocatorDefault,
59 &signature, // Socket signature.
60 kCFSocketAcceptCallBack, // Callback types.
61 &NetworkCallbackController::SocketAcceptCallback, // Callout function pointer.
62 &context); // Context to pass to callout.
63 if (!listeningSocket_) {
64 [connection_ errorEncountered:@"Could not open socket."];
65 sleep(1);
66 }
67 } while (!listeningSocket_);
68
69 // Allow old, yet-to-be recycled sockets to be reused.
70 BOOL yes = YES;
71 setsockopt(CFSocketGetNative(listeningSocket_), SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(BOOL));
72 setsockopt(CFSocketGetNative(listeningSocket_), SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(BOOL));
73
74 // Schedule the socket on the run loop.
75 CFRunLoopSourceRef source = CFSocketCreateRunLoopSource(kCFAllocatorDefault, listeningSocket_, 0);
76 CFRunLoopAddSource(runLoop_, source, kCFRunLoopCommonModes);
77 CFRelease(source);
78 }
79
80 void NetworkCallbackController::CloseConnection()
81 {
82 NSUInteger closeCount = 0;
83
84 if (readStream_) {
85 UnscheduleReadStream();
86 ++closeCount;
87 }
88 if (writeStream_) {
89 UnscheduleWriteStream();
90 ++closeCount;
91 }
92
93 if (socketHandle_) {
94 for ( ; closeCount > 0; --closeCount)
95 close(socketHandle_);
96
97 socketHandle_ = NULL;
98 [connection_ socketDisconnected];
99 }
100 }
101
102 BOOL NetworkCallbackController::WriteStreamCanAcceptBytes()
103 {
104 return writeStream_ && CFWriteStreamCanAcceptBytes(writeStream_);
105 }
106
107 BOOL NetworkCallbackController::WriteString(NSString* string)
108 {
109 BOOL done = NO;
110
111 char* cString = const_cast<char*>([string UTF8String]);
112 size_t stringLength = strlen(cString);
113
114 // Busy wait while writing. BAADD. Should background this operation.
115 while (!done) {
116 if (WriteStreamCanAcceptBytes()) {
117 // Include the NULL byte in the string when we write.
118 CFIndex bytesWritten = CFWriteStreamWrite(writeStream_, (UInt8*)cString, stringLength + 1);
119 if (bytesWritten < 0) {
120 CFErrorRef error = CFWriteStreamCopyError(writeStream_);
121 ReportError(error);
122 break;
123 }
124 // Incomplete write.
125 else if (bytesWritten < static_cast<CFIndex>(strlen(cString))) {
126 // Adjust the buffer and wait for another chance to write.
127 stringLength -= bytesWritten;
128 memmove(string, string + bytesWritten, stringLength);
129 }
130 else {
131 done = YES;
132 }
133 }
134 }
135
136 return done;
137 }
138
139 // Static Methods //////////////////////////////////////////////////////////////
140
141 void NetworkCallbackController::SocketAcceptCallback(CFSocketRef socket,
142 CFSocketCallBackType callbackType,
143 CFDataRef address,
144 const void* data,
145 void* self)
146 {
147 assert(callbackType == kCFSocketAcceptCallBack);
148 static_cast<NetworkCallbackController*>(self)->OnSocketAccept(socket, address, data);
149 }
150
151 void NetworkCallbackController::ReadStreamCallback(CFReadStreamRef stream,
152 CFStreamEventType eventType,
153 void* self)
154 {
155 static_cast<NetworkCallbackController*>(self)->OnReadStreamEvent(stream, eventType);
156 }
157
158 void NetworkCallbackController::WriteStreamCallback(CFWriteStreamRef stream,
159 CFStreamEventType eventType,
160 void* self)
161 {
162 static_cast<NetworkCallbackController*>(self)->OnWriteStreamEvent(stream, eventType);
163 }
164
165
166 // Private Instance Methods ////////////////////////////////////////////////////
167
168 void NetworkCallbackController::OnSocketAccept(CFSocketRef socket,
169 CFDataRef address,
170 const void* data)
171 {
172 socketHandle_ = *(CFSocketNativeHandle*)data;
173
174 // Create the streams on the socket.
175 CFStreamCreatePairWithSocket(kCFAllocatorDefault,
176 socketHandle_, // Socket handle.
177 &readStream_, // Read stream in-pointer.
178 &writeStream_); // Write stream in-pointer.
179
180 // Create struct to register callbacks for the stream.
181 CFStreamClientContext context = { 0 };
182 context.info = this;
183
184 // Set the client of the read stream.
185 CFOptionFlags readFlags = kCFStreamEventOpenCompleted |
186 kCFStreamEventHasBytesAvailable |
187 kCFStreamEventErrorOccurred |
188 kCFStreamEventEndEncountered;
189 if (CFReadStreamSetClient(readStream_, readFlags, &NetworkCallbackController::ReadStreamCallback, &context))
190 // Schedule in run loop to do asynchronous communication with the engine.
191 CFReadStreamScheduleWithRunLoop(readStream_, runLoop_, kCFRunLoopCommonModes);
192 else
193 return;
194
195 // Open the stream now that it's scheduled on the run loop.
196 if (!CFReadStreamOpen(readStream_)) {
197 ReportError(CFReadStreamCopyError(readStream_));
198 return;
199 }
200
201 // Set the client of the write stream.
202 CFOptionFlags writeFlags = kCFStreamEventOpenCompleted |
203 kCFStreamEventCanAcceptBytes |
204 kCFStreamEventErrorOccurred |
205 kCFStreamEventEndEncountered;
206 if (CFWriteStreamSetClient(writeStream_, writeFlags, &NetworkCallbackController::WriteStreamCallback, &context))
207 // Schedule it in the run loop to receive error information.
208 CFWriteStreamScheduleWithRunLoop(writeStream_, runLoop_, kCFRunLoopCommonModes);
209 else
210 return;
211
212 // Open the write stream.
213 if (!CFWriteStreamOpen(writeStream_)) {
214 ReportError(CFWriteStreamCopyError(writeStream_));
215 return;
216 }
217
218 [connection_ socketDidAccept];
219
220 CloseSocket();
221 }
222
223 void NetworkCallbackController::OnReadStreamEvent(CFReadStreamRef stream,
224 CFStreamEventType eventType)
225 {
226 switch (eventType)
227 {
228 case kCFStreamEventHasBytesAvailable:
229 if (readStream_)
230 [connection_ readStreamHasData:stream];
231 break;
232
233 case kCFStreamEventErrorOccurred:
234 ReportError(CFReadStreamCopyError(stream));
235 CloseConnection();
236 break;
237
238 case kCFStreamEventEndEncountered:
239 CloseConnection();
240 break;
241 };
242 }
243
244 void NetworkCallbackController::OnWriteStreamEvent(CFWriteStreamRef stream,
245 CFStreamEventType eventType)
246 {
247 switch (eventType)
248 {
249 case kCFStreamEventCanAcceptBytes:
250 [connection_ sendQueuedWrites];
251 break;
252
253 case kCFStreamEventErrorOccurred:
254 ReportError(CFWriteStreamCopyError(stream));
255 CloseConnection();
256 break;
257
258 case kCFStreamEventEndEncountered:
259 CloseConnection();
260 break;
261 }
262 }
263
264 void NetworkCallbackController::CloseSocket()
265 {
266 if (listeningSocket_) {
267 CFSocketInvalidate(listeningSocket_);
268 CFRelease(listeningSocket_);
269 listeningSocket_ = NULL;
270 }
271 }
272
273 void NetworkCallbackController::UnscheduleReadStream()
274 {
275 if (!readStream_)
276 return;
277 CFReadStreamUnscheduleFromRunLoop(readStream_, runLoop_, kCFRunLoopCommonModes);
278 CFReadStreamClose(readStream_);
279 CFRelease(readStream_);
280 readStream_ = NULL;
281 }
282
283 void NetworkCallbackController::UnscheduleWriteStream()
284 {
285 if (!writeStream_)
286 return;
287 CFWriteStreamUnscheduleFromRunLoop(writeStream_, runLoop_, kCFRunLoopCommonModes);
288 CFWriteStreamClose(writeStream_);
289 CFRelease(writeStream_);
290 writeStream_ = NULL;
291 }
292
293 void NetworkCallbackController::ReportError(CFErrorRef error)
294 {
295 [connection_ errorEncountered:[(NSError*)error description]];
296 CFRelease(error);
297 }