Write ProtocolClient, the layer that talks XML on top of a MessageQueue.
[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 UnscheduleReadStream();
83 UnscheduleWriteStream();
84
85 if (socketHandle_) {
86 close(socketHandle_);
87 socketHandle_ = NULL;
88 [connection_ socketDisconnected];
89 }
90 }
91
92 BOOL NetworkCallbackController::WriteStreamCanAcceptBytes()
93 {
94 return writeStream_ && CFWriteStreamCanAcceptBytes(writeStream_);
95 }
96
97 BOOL NetworkCallbackController::WriteString(NSString* string)
98 {
99 // TODO: May need to negotiate with the server as to the string encoding.
100 const NSStringEncoding kEncoding = NSUTF8StringEncoding;
101 // Add space for the NUL byte.
102 NSUInteger maxBufferSize = [string maximumLengthOfBytesUsingEncoding:kEncoding] + 1;
103
104 UInt8* buffer = new UInt8[maxBufferSize];
105 bzero(buffer, maxBufferSize);
106
107 NSUInteger bufferSize = 0;
108 if (![string getBytes:buffer
109 maxLength:maxBufferSize
110 usedLength:&bufferSize
111 encoding:kEncoding
112 options:0
113 range:NSMakeRange(0, [string length])
114 remainingRange:NULL]) {
115 delete [] buffer;
116 return NO;
117 }
118
119 // Include a NUL byte.
120 ++bufferSize;
121
122 // Write the packet out, and spin in a busy wait loop if the stream is not ready. This
123 // method is only ever called in response to a stream ready event.
124 NSUInteger totalWritten = 0;
125 while (totalWritten < bufferSize) {
126 if (WriteStreamCanAcceptBytes()) {
127 CFIndex bytesWritten = CFWriteStreamWrite(writeStream_, buffer + totalWritten, bufferSize - totalWritten);
128 if (bytesWritten < 0) {
129 CFErrorRef error = CFWriteStreamCopyError(writeStream_);
130 ReportError(error);
131 break;
132 }
133 totalWritten += bytesWritten;
134 }
135 }
136
137 delete [] buffer;
138 return YES;
139 }
140
141 // Static Methods //////////////////////////////////////////////////////////////
142
143 void NetworkCallbackController::SocketAcceptCallback(CFSocketRef socket,
144 CFSocketCallBackType callbackType,
145 CFDataRef address,
146 const void* data,
147 void* self)
148 {
149 assert(callbackType == kCFSocketAcceptCallBack);
150 static_cast<NetworkCallbackController*>(self)->OnSocketAccept(socket, address, data);
151 }
152
153 void NetworkCallbackController::ReadStreamCallback(CFReadStreamRef stream,
154 CFStreamEventType eventType,
155 void* self)
156 {
157 static_cast<NetworkCallbackController*>(self)->OnReadStreamEvent(stream, eventType);
158 }
159
160 void NetworkCallbackController::WriteStreamCallback(CFWriteStreamRef stream,
161 CFStreamEventType eventType,
162 void* self)
163 {
164 static_cast<NetworkCallbackController*>(self)->OnWriteStreamEvent(stream, eventType);
165 }
166
167
168 // Private Instance Methods ////////////////////////////////////////////////////
169
170 void NetworkCallbackController::OnSocketAccept(CFSocketRef socket,
171 CFDataRef address,
172 const void* data)
173 {
174 // Keep a reference to the socket handle of the child socket. Do not create
175 // a CFSocket with this because doing so prohibits the use of streams. The
176 // kCFSocketDataCallBack would have to be used instead.
177 socketHandle_ = *(CFSocketNativeHandle*)data;
178
179 // Create the streams on the socket.
180 CFStreamCreatePairWithSocket(kCFAllocatorDefault,
181 socketHandle_, // Socket handle.
182 &readStream_, // Read stream in-pointer.
183 &writeStream_); // Write stream in-pointer.
184
185 // Create struct to register callbacks for the stream.
186 CFStreamClientContext context = { 0 };
187 context.info = this;
188
189 // Set the client of the read stream.
190 CFOptionFlags readFlags = kCFStreamEventOpenCompleted |
191 kCFStreamEventHasBytesAvailable |
192 kCFStreamEventErrorOccurred |
193 kCFStreamEventEndEncountered;
194 if (CFReadStreamSetClient(readStream_, readFlags, &NetworkCallbackController::ReadStreamCallback, &context))
195 // Schedule in run loop to do asynchronous communication with the engine.
196 CFReadStreamScheduleWithRunLoop(readStream_, runLoop_, kCFRunLoopCommonModes);
197 else
198 return;
199
200 // Open the stream now that it's scheduled on the run loop.
201 if (!CFReadStreamOpen(readStream_)) {
202 ReportError(CFReadStreamCopyError(readStream_));
203 return;
204 }
205
206 // Set the client of the write stream.
207 CFOptionFlags writeFlags = kCFStreamEventOpenCompleted |
208 kCFStreamEventCanAcceptBytes |
209 kCFStreamEventErrorOccurred |
210 kCFStreamEventEndEncountered;
211 if (CFWriteStreamSetClient(writeStream_, writeFlags, &NetworkCallbackController::WriteStreamCallback, &context))
212 // Schedule it in the run loop to receive error information.
213 CFWriteStreamScheduleWithRunLoop(writeStream_, runLoop_, kCFRunLoopCommonModes);
214 else
215 return;
216
217 // Open the write stream.
218 if (!CFWriteStreamOpen(writeStream_)) {
219 ReportError(CFWriteStreamCopyError(writeStream_));
220 return;
221 }
222
223 [connection_ socketDidAccept];
224
225 CloseSocket();
226 }
227
228 void NetworkCallbackController::OnReadStreamEvent(CFReadStreamRef stream,
229 CFStreamEventType eventType)
230 {
231 switch (eventType)
232 {
233 case kCFStreamEventHasBytesAvailable:
234 if (readStream_)
235 [connection_ readStreamHasData:stream];
236 break;
237
238 case kCFStreamEventErrorOccurred:
239 ReportError(CFReadStreamCopyError(stream));
240 CloseConnection();
241 break;
242
243 case kCFStreamEventEndEncountered:
244 CloseConnection();
245 break;
246 };
247 }
248
249 void NetworkCallbackController::OnWriteStreamEvent(CFWriteStreamRef stream,
250 CFStreamEventType eventType)
251 {
252 switch (eventType)
253 {
254 case kCFStreamEventCanAcceptBytes:
255 [connection_ sendQueuedWrites];
256 break;
257
258 case kCFStreamEventErrorOccurred:
259 ReportError(CFWriteStreamCopyError(stream));
260 CloseConnection();
261 break;
262
263 case kCFStreamEventEndEncountered:
264 CloseConnection();
265 break;
266 }
267 }
268
269 void NetworkCallbackController::CloseSocket()
270 {
271 if (listeningSocket_) {
272 CFSocketInvalidate(listeningSocket_);
273 CFRelease(listeningSocket_);
274 listeningSocket_ = NULL;
275 }
276 }
277
278 void NetworkCallbackController::UnscheduleReadStream()
279 {
280 if (!readStream_)
281 return;
282 CFReadStreamUnscheduleFromRunLoop(readStream_, runLoop_, kCFRunLoopCommonModes);
283 CFReadStreamClose(readStream_);
284 CFRelease(readStream_);
285 readStream_ = NULL;
286 }
287
288 void NetworkCallbackController::UnscheduleWriteStream()
289 {
290 if (!writeStream_)
291 return;
292 CFWriteStreamUnscheduleFromRunLoop(writeStream_, runLoop_, kCFRunLoopCommonModes);
293 CFWriteStreamClose(writeStream_);
294 CFRelease(writeStream_);
295 writeStream_ = NULL;
296 }
297
298 void NetworkCallbackController::ReportError(CFErrorRef error)
299 {
300 [connection_ errorEncountered:[(NSError*)error description]];
301 CFRelease(error);
302 }