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