30#ifndef OO_EXCLUDE_DEBUG_SUPPORT
51#ifdef OO_LOG_DEBUG_PROTOCOL_PACKETS
54#define LogSendPacket(packet) do {} while (0)
68@interface OODebugTCPConsoleClient (OOPrivate)
72- (BOOL) sendBytes:(const
void *)bytes count:(
size_t)count;
73- (void) sendDictionary:(NSDictionary *)dictionary;
75- (void) sendPacket:(NSString *)packetType
76 withParameters:(NSDictionary *)parameters;
78- (void) sendPacket:(NSString *)packetType
80 forParameter:(NSString *)paramKey;
83- (void) dispatchPacket:(NSDictionary *)packet ofType:(NSString *)packetType;
85- (void) handleApproveConnectionPacket:(NSDictionary *)packet;
86- (void) handleRejectConnectionPacket:(NSDictionary *)packet;
87- (void) handleCloseConnectionPacket:(NSDictionary *)packet;
88- (void) handleNoteConfigurationChangePacket:(NSDictionary *)packet;
89- (void) handlePerformCommandPacket:(NSDictionary *)packet;
90- (void) handleRequestConfigurationValuePacket:(NSDictionary *)packet;
91- (void) handlePingPacket:(NSDictionary *)packet;
92- (void) handlePongPacket:(NSDictionary *)packet;
94- (void) disconnectFromServerWithMessage:(NSString *)message;
95- (void) breakConnectionWithMessage:(NSString *)message;
96- (void) breakConnectionWithBadStream:(NSStream *)stream;
109- (id) initWithAddress:(NSString *)address port:(uint16_t)port
112 NSDictionary *parameters =
nil;
114 if (address ==
nil) address =
@"127.0.0.1";
120 _host = [NSHost hostWithName:address];
124 [NSStream getStreamsToHost:_host
126 inputStream:&_inStream
127 outputStream:&_outStream];
130 if (_inStream !=
nil && _outStream !=
nil)
134 [_inStream setDelegate:self];
135 [_outStream setDelegate:self];
136 [_inStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
137 [_outStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
143 NSRunLoop * myRunLoop = [NSRunLoop currentRunLoop];
144 NSDate * timeOut = [NSDate dateWithTimeIntervalSinceNow:3];
145 while( _host !=
nil && ([_inStream streamStatus] < 2 || [_outStream streamStatus] < 2) &&
146 [myRunLoop runMode:NSDefaultRunLoopMode beforeDate:timeOut] )
152 if (_decoder != NULL)
159 parameters = [NSDictionary dictionaryWithObjectsAndKeys:
160 [NSNumber numberWithUnsignedInt:kOOTCPProtocolVersion_1_1_0], kOOTCPProtocolVersion,
161 [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"], kOOTCPOoliteVersion,
163 [
self sendPacket:kOOTCPPacket_RequestConnection
164 withParameters:parameters];
172 OOLog(
@"debugTCP.connect.failed",
@"Failed to connect to debug console at address %@:%i.", address, port);
186 [
self disconnectFromServerWithMessage:@"TCP console bridge unexpectedly released while active."];
190 [_monitor disconnectDebugger:self message:@"TCP console bridge unexpectedly released while active."];
194 [
self closeConnection];
204 errorMessage:(out NSString **)message
208 if (message != NULL) *message =
@"Connection refused.";
213 if (message != NULL) *message =
@"Cannot reconnect after disconnecting.";
217 _monitor = debugMonitor;
224 message:(in NSString *)message
226 [
self disconnectFromServerWithMessage:message];
232 jsConsoleOutput:(in NSString *)output
233 colorKey:(in NSString *)colorKey
234 emphasisRange:(in NSRange)emphasisRange
236 NSMutableDictionary *parameters =
nil;
237 NSArray *range =
nil;
239 parameters = [NSMutableDictionary dictionaryWithCapacity:3];
240 [parameters setObject:output forKey:kOOTCPMessage];
241 [parameters setObject:colorKey ? colorKey : (NSString *)@"general" forKey:kOOTCPColorKey];
242 if (emphasisRange.length != 0)
244 range = [NSArray arrayWithObjects:
245 [NSNumber numberWithUnsignedInteger:emphasisRange.location],
246 [NSNumber numberWithUnsignedInteger:emphasisRange.length],
248 [parameters setObject:range forKey:kOOTCPEmphasisRanges];
251 [
self sendPacket:kOOTCPPacket_ConsoleOutput
252 withParameters:parameters];
256- (oneway void)debugMonitorClearConsole:(in
OODebugMonitor *)debugMonitor
258 [
self sendPacket:kOOTCPPacket_ClearConsole
263- (oneway void)debugMonitorShowConsole:(in
OODebugMonitor *)debugMonitor
265 [
self sendPacket:kOOTCPPacket_ShowConsole
271 noteConfiguration:(in NSDictionary *)configuration
273 [
self sendPacket:kOOTCPPacket_NoteConfiguration
274 withValue:configuration
275 forParameter:kOOTCPConfiguration];
280noteChangedConfigrationValue:(in
id)newValue
281 forKey:(in NSString *)key
285 [
self sendPacket:kOOTCPPacket_NoteConfiguration
286 withValue:[NSDictionary dictionaryWithObject:newValue forKey:key]
287 forParameter:kOOTCPConfiguration];
291 [
self sendPacket:kOOTCPPacket_NoteConfiguration
292 withValue:[NSArray arrayWithObject:key]
293 forParameter:kOOTCPRemovedConfigurationKeys];
298- (void)
stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode
302 if (
stream == _inStream && eventCode == NSStreamEventHasBytesAvailable)
306 else if (eventCode == NSStreamEventErrorOccurred)
308 [
self breakConnectionWithBadStream:stream];
310 else if (eventCode == NSStreamEventErrorOccurred)
312 [
self breakConnectionWithMessage:[NSString stringWithFormat:
313 @"Console closed the connection."]];
320@implementation OODebugTCPConsoleClient (OOPrivate)
325 [_inStream setDelegate:nil];
330 [_outStream setDelegate:nil];
331 [_outStream release];
339- (BOOL) sendBytes:(const
void *)bytes count:(
size_t)count
341 if (bytes == NULL ||
count == 0)
return YES;
346 NSInteger written = [_outStream write:bytes maxLength:count];
347 if (written < 1)
return NO;
358- (void) sendDictionary:(NSDictionary *)dictionary
361 NSString *errorDesc = NULL;
363 const uint8_t *bytes = NULL;
369 data = [NSPropertyListSerialization dataFromPropertyList:dictionary
370 format:NSPropertyListXMLFormat_v1_0
371 errorDescription:&errorDesc];
375 OOLog(
@"debugTCP.conversionFailure",
@"Could not convert dictionary to data for transmission to debug console: %@", errorDesc != NULL ? errorDesc : (NSString *)
@"unknown error.");
376#if OOLITE_RELEASE_PLIST_ERROR_STRINGS
377 [errorDesc autorelease];
384 count = [data length];
385 if (
count == 0)
return;
386 header = htonl(
count);
388 bytes = [data bytes];
394 if (![
self sendBytes:&header
count:
sizeof header])
396 OOLog(
@"debugTCP.send.warning",
@"%@",
@"Error sending packet header, retrying.");
398 [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:.008]];
399 if (![
self sendBytes:&header
count:
sizeof header])
403 [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:.016]];
404 if (![
self sendBytes:&header
count:
sizeof header])
411 if(sentOK && ![
self sendBytes:bytes
count:
count])
413 OOLog(
@"debugTCP.send.warning",
@"%@",
@"Error sending packet body, retrying.");
415 [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:.008]];
424 OOLog(
@"debugTCP.send.error",
@"The following packet could not be sent: %@", dictionary);
425 if(![[
OODebugMonitor sharedDebugMonitor] TCPIgnoresDroppedPackets])
427 [
self breakConnectionWithBadStream:_outStream];
433- (void) sendPacket:(NSString *)packetType
434 withParameters:(NSDictionary *)parameters
436 NSDictionary *dict =
nil;
438 if (packetType ==
nil)
return;
440 if (parameters !=
nil)
442 dict = [parameters dictionaryByAddingObject:packetType forKey:kOOTCPPacketType];
446 dict = [NSDictionary dictionaryWithObjectsAndKeys:packetType, kOOTCPPacketType, nil];
449 [
self sendDictionary:dict];
453- (void) sendPacket:(NSString *)packetType
455 forParameter:(NSString *)paramKey
457 if (packetType ==
nil)
return;
458 if (paramKey ==
nil) value =
nil;
460 [
self sendDictionary:[NSDictionary dictionaryWithObjectsAndKeys:
461 packetType, kOOTCPPacketType,
469 enum { kBufferSize = 16 << 10 };
471 uint8_t buffer[kBufferSize];
475 length = [_inStream read:buffer maxLength:kBufferSize];
478 data = [NSData dataWithBytesNoCopy:buffer length:length freeWhenDone:NO];
480 length = [_inStream read:buffer maxLength:kBufferSize];
485- (void) dispatchPacket:(NSDictionary *)packet ofType:(NSString *)packetType
487 if (packet ==
nil || packetType ==
nil)
return;
489#define PACKET_CASE(x) else if ([packetType isEqualToString:kOOTCPPacket_##x]) { [self handle##x##Packet:packet]; }
502 OOLog(
@"debugTCP.protocolError.unknownPacketType",
@"Unhandled packet type %@.", packetType);
507- (void) handleApproveConnectionPacket:(NSDictionary *)packet
509 NSMutableString *connectedMessage =
nil;
510 NSString *consoleIdentity =
nil;
511 NSString *hostName =
nil;
518 connectedMessage = [NSMutableString stringWithString:@"Connected to debug console"];
520 consoleIdentity = [packet oo_stringForKey:kOOTCPConsoleIdentity];
521 if (consoleIdentity !=
nil) [connectedMessage appendFormat:@" \"%@\"", consoleIdentity];
523 hostName = [_host name];
524 if ([hostName length] != 0 &&
525 ![hostName isEqual:
@"localhost"] &&
526 ![hostName isEqual:
@"127.0.0.1"] &&
527 ![hostName isEqual:
@"::1"])
529 [connectedMessage appendFormat:@" at %@", hostName];
532 OOLog(
@"debugTCP.connected",
@"%@.", connectedMessage);
541- (void) handleRejectConnectionPacket:(NSDictionary *)packet
543 NSString *message =
nil;
554 message = [packet oo_stringForKey:kOOTCPMessage];
555 if (message ==
nil) message =
@"Console refused connection.";
556 [
self breakConnectionWithMessage:message];
560- (void) handleCloseConnectionPacket:(NSDictionary *)packet
562 NSString *message =
nil;
568 message = [packet oo_stringForKey:kOOTCPMessage];
569 if (message ==
nil) message =
@"Console closed connection.";
570 [
self breakConnectionWithMessage:message];
574- (void) handleNoteConfigurationChangePacket:(NSDictionary *)packet
576 NSDictionary *configuration =
nil;
577 NSArray *removed =
nil;
578 NSEnumerator *keyEnum =
nil;
582 if (_monitor ==
nil)
return;
584 configuration = [packet oo_dictionaryForKey:kOOTCPConfiguration];
585 if (configuration !=
nil)
587 for (keyEnum = [configuration keyEnumerator]; (key = [keyEnum nextObject]); )
589 value = [configuration objectForKey:key];
590 [_monitor setConfigurationValue:value forKey:key];
594 removed = [configuration oo_arrayForKey:kOOTCPRemovedConfigurationKeys];
595 for (keyEnum = [removed objectEnumerator]; (key = [keyEnum nextObject]); )
597 [_monitor setConfigurationValue:nil forKey:key];
602- (void) handlePerformCommandPacket:(NSDictionary *)packet
604 NSString *message =
nil;
606 message = [packet oo_stringForKey:kOOTCPMessage];
607 if (message !=
nil) [_monitor performJSConsoleCommand:message];
611- (void) handleRequestConfigurationValuePacket:(NSDictionary *)packet
616 key = [packet oo_stringForKey:kOOTCPConfigurationKey];
619 value = [_monitor configurationValueForKey:key];
620 [
self debugMonitor:_monitor
621 noteChangedConfigrationValue:value
627- (void) handlePingPacket:(NSDictionary *)packet
631 message = [packet objectForKey:kOOTCPMessage];
632 [
self sendPacket:kOOTCPPacket_Pong
634 forParameter:kOOTCPMessage];
638- (void) handlePongPacket:(NSDictionary *)packet
644- (void) disconnectFromServerWithMessage:(NSString *)message
648 [
self sendPacket:kOOTCPPacket_CloseConnection
650 forParameter:kOOTCPMessage];
652 [
self closeConnection];
658- (void) breakConnectionWithMessage:(NSString *)message
660 [
self closeConnection];
664 if ([message length] > 0)
666 OOLog(
@"debugTCP.disconnect",
@"No connection to debug console: \"%@\
"", message);
670 OOLog(
@"debugTCP.disconnect",
@"%@",
@"Debug console not connected.");
675 [_monitor disconnectDebugger:self message:message];
681- (void) breakConnectionWithBadStream:(NSStream *)stream
683 NSString *errorDesc =
nil;
684 NSError *error =
nil;
686 error = [stream streamError];
687 errorDesc = [error localizedDescription];
688 if (errorDesc ==
nil) errorDesc = [error description];
689 if (errorDesc ==
nil) errorDesc =
@"bad stream.";
690 [
self breakConnectionWithMessage:[NSString stringWithFormat:
691 @"Connection to debug console failed: '%@' (outStream status: %li, inStream status: %li).",
692 errorDesc, [_outStream streamStatus], [_inStream streamStatus]]];
710#ifdef OO_LOG_DEBUG_PROTOCOL_PACKETS
716 data = [NSPropertyListSerialization dataFromPropertyList:packet format:NSPropertyListXMLFormat_v1_0 errorDescription:NULL];
717 xml = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
718 OOLog(
@"debugTCP.receive",
@"Received packet:\n%@", xml);
727 data = [NSPropertyListSerialization dataFromPropertyList:packet format:NSPropertyListXMLFormat_v1_0 errorDescription:NULL];
728 xml = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
729 OOLog(
@"debugTCP.send",
@"Sent packet:\n%@", xml);
OOTCPClientConnectionStatus
@ kOOTCPClientStartedConnectionStage2
@ kOOTCPClientStartedConnectionStage1
@ kOOTCPClientConnectionRefused
@ kOOTCPClientDisconnected
#define LogSendPacket(packet)
OOINLINE BOOL StatusIsSendable(OOTCPClientConnectionStatus status)
static void DecoderPacket(void *cbInfo, OOALStringRef packetType, OOALDictionaryRef packet)
static void DecoderError(void *cbInfo, OOALStringRef errorDesc)
#define kOOTCPPacket_ApproveConnection
#define kOOTCPPacket_RejectConnection
#define kOOTCPPacket_CloseConnection
#define OOLog(class, format,...)
const struct NSString * OOALStringRef
const struct NSDictionary * OOALDictionaryRef
OOTCPStreamDecoderRef OOTCPStreamDecoderCreate(OOTCPStreamDecoderPacketCallback packetCB, OOTCPStreamDecoderErrorCallback errorCB, OOTCPStreamDecoderFinalizeCallback finalizeCB, void *cbInfo)
void OOTCPStreamDecoderDestroy(OOTCPStreamDecoderRef decoder)
void OOTCPStreamDecoderReceiveData(OOTCPStreamDecoderRef decoder, OOALDataRef data)
#define LogOOTCPStreamDecoderPacket(packet)
id initWithAddress:port:(NSString *address,[port] uint16_t port)