Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pcurl command #109

Merged
merged 15 commits into from Sep 28, 2015
58 changes: 58 additions & 0 deletions commands/FBPrintCommands.py
Expand Up @@ -32,6 +32,7 @@ def lldbcommands():
FBPrintApplicationDocumentsPath(),
FBPrintData(),
FBPrintTargetActions(),
FBPrintAsCurl(),
]

class FBPrintViewHierarchyCommand(fb.FBCommand):
Expand Down Expand Up @@ -424,3 +425,60 @@ def run(self, arguments, options):
actionsDescription = fb.evaluateExpressionValue('(id)[{actions} componentsJoinedByString:@", "]'.format(actions=actions)).GetObjectDescription()

print '{target}: {actions}'.format(target=targetDescription, actions=actionsDescription)

class FBPrintAsCurl(fb.FBCommand):
def name(self):
return 'pcurl'

def description(self):
return 'Print the NSURLRequest (HTTP) as curl command.'

def options(self):
return [
fb.FBCommandArgument(short='-e', long='--embed-data', arg='embed', boolean=True, default=False, help='Embed request data as base64.'),
]

def args(self):
return [ fb.FBCommandArgument(arg='request', type='NSURLRequest*/NSMutableURLRequest*', help='The request to convert to the curl command.') ]

def run(self, arguments, options):
request = arguments[0]
HTTPHeaderSring = ''
HTTPMethod = fb.evaluateExpressionValue('(id)[{} HTTPMethod]'.format(request)).GetObjectDescription()
URL = fb.evaluateExpressionValue('(id)[{} URL]'.format(request)).GetObjectDescription()
timeout = fb.evaluateExpression('(NSTimeInterval)[{} timeoutInterval]'.format(request))
HTTPHeaders = fb.evaluateObjectExpression('(id)[{} allHTTPHeaderFields]'.format(request))
HTTPHeadersCount = fb.evaluateIntegerExpression('[{} count]'.format(HTTPHeaders))
allHTTPKeys = fb.evaluateObjectExpression('[{} allKeys]'.format(HTTPHeaders))
for index in range(0, HTTPHeadersCount):
key = fb.evaluateObjectExpression('[{} objectAtIndex:{}]'.format(allHTTPKeys, index))
keyDescription = fb.evaluateExpressionValue('(id){}'.format(key)).GetObjectDescription()
value = fb.evaluateExpressionValue('(id)[(id){} objectForKey:{}]'.format(HTTPHeaders, key)).GetObjectDescription()
if len(HTTPHeaderSring) > 0:
HTTPHeaderSring += ' '
HTTPHeaderSring += '-H "{}: {}"'.format(keyDescription, value)
HTTPData = fb.evaluateObjectExpression('[{} HTTPBody]'.format(request))
dataFile = None
dataAsString = None
if fb.evaluateIntegerExpression('[{} length]'.format(HTTPData)) > 0:
dataFile = '/tmp/curl_data_{}'.format(fb.evaluateExpression('(NSTimeInterval)[NSDate timeIntervalSinceReferenceDate]'))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Move this inside the elif not runtimeHelpers.isIOSDevice():.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this line should be moved into the elif not runtimeHelpers.isIOSDevice(): branch. EDIT: Oops I had "Show notes" turned off, didn't see that I was duplicating @mattjgalloway's comment.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@VTopoliuk actually pointed out that this is required outside of the elif, as it's used down on line 476.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mattjgalloway Ah I see. I didn't know that sandbox apps could write to /tmp. If that works, then sweet!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. Actually, it would be best to set to None here, and then inside the elif not runtimeHelpers.isIOSDevice() set it to the string. That way it's only set if we did actually write it to the file.

if options.embed:
if fb.evaluateIntegerExpression('[{} respondsToSelector:@selector(base64EncodedStringWithOptions:)]'.format(HTTPData)):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably no need to do this, since it's available in iOS 7 and I don't think we support <iOS 7. Although I can't remember for sure. @kastiglione?

And if we do supper <iOS 7 I would prefer in the else case here to print an error, and include an option to skip the data part of this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if we state an official iOS version support. If we don't, this is as good a time to start. So yeah, let's say iOS 7+.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This command works on OS X as well, but this method available only for 10.9+

dataAsString = fb.evaluateExpressionValue('(id)[(id){} base64EncodedStringWithOptions:0]'.format(HTTPData)).GetObjectDescription()
elif not runtimeHelpers.isIOSDevice():
fb.evaluateExpression('(BOOL)[{} writeToFile:@"{}" atomically:NO]'.format(HTTPData, dataFile))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to at least warn if this fails.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, you are totally right!

else:
print 'HTTPBody data for iOS Device is supported only with "--embed-data" flag'
return False

commandString = ''
if dataAsString is not None and len(dataAsString) > 0:
commandString += 'echo "{}" | base64 -D -o "{}" && '.format(dataAsString, dataFile)
commandString += 'curl -X {} --connect-timeout {}'.format(HTTPMethod, timeout)
if len(HTTPHeaderSring) > 0:
commandString += ' ' + HTTPHeaderSring
if dataFile is not None:
commandString += ' --data-binary @"{}"'.format(dataFile)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mattjgalloway actually same file are used in two cases:

  1. To embed data
  2. To write data to file

If I will place name generation under the not runtimeHelpers.isIOSDevice(): condition, then it will be not available here with --embed-data option.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh whoops sorry!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we go?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kastiglione do you think it's ready for merge?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a couple comments. Primarily needs a fix for the location of dataFile.


commandString += ' "{}"'.format(URL)
print commandString
6 changes: 6 additions & 0 deletions fblldbobjcruntimehelpers.py
Expand Up @@ -88,3 +88,9 @@ def isMacintoshArch():
command = '(void*)objc_getClass("{}")'.format(nsClassName)

return (fb.evaluateBooleanExpression(command + '!= nil'))

def isIOSSimulator():
return fb.evaluateExpressionValue('(id)[[UIDevice currentDevice] model]').GetObjectDescription().lower().find('simulator') >= 0

def isIOSDevice():
return not isMacintoshArch() and not isIOSSimulator()