Skip to content

Instantly share code, notes, and snippets.

@pudquick
Last active May 14, 2020 15:44
Show Gist options
  • Save pudquick/a73d0ce7cd8730c97491 to your computer and use it in GitHub Desktop.
Save pudquick/a73d0ce7cd8730c97491 to your computer and use it in GitHub Desktop.
This example builds on the Gurl class from gurl.py in munki and creates a POST variant called Purl
# Based on: https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/URLLoadingSystem/Tasks/UsingNSURLConnection.html#//apple_ref/doc/uid/20001836-SW5
from gurl import *
from Foundation import NSData, NSString
class Purl(Gurl):
'''A POST variant of Gurl'''
def initWithOptions_(self, options):
'''Set up our Purl object'''
# Inherit our basic setup from Gurl
self = super(Purl, self).initWithOptions_(options)
if not self:
return
# Now we add in our additional properties: body, content_type
if options.get('content_type') is not None:
self.additional_headers['Content-Type'] = options.get('content_type')
self.body = options.get('body')
return self
def start(self):
'''Start the connection'''
# We're cloning Gurl's start and adding a section in the middle
if not self.destination_path:
self.log('No output file specified.')
self.done = True
return
url = NSURL.URLWithString_(self.url)
request = (
NSMutableURLRequest.requestWithURL_cachePolicy_timeoutInterval_(
url, NSURLRequestReloadIgnoringLocalCacheData,
self.connection_timeout))
if self.additional_headers:
for header, value in self.additional_headers.items():
request.setValue_forHTTPHeaderField_(value, header)
# new POST-specific code
request.setHTTPMethod_('POST')
body_unicode = unicode(self.body)
body_data = NSData.dataWithBytes_length_(NSString.stringWithString_(body_unicode).UTF8String(), len(body_unicode.encode('utf-8')))
request.setHTTPBody_(body_data)
# end POST-specific
# does the file already exist? See if we can resume a partial download
if os.path.isfile(self.destination_path):
stored_data = self.get_stored_headers()
if (self.can_resume and 'expected-length' in stored_data and
('last-modified' in stored_data or 'etag' in stored_data)):
# we have a partial file and we're allowed to resume
self.resume = True
local_filesize = os.path.getsize(self.destination_path)
byte_range = 'bytes=%s-' % local_filesize
request.setValue_forHTTPHeaderField_(byte_range, 'Range')
if self.download_only_if_changed and not self.resume:
stored_data = self.cache_data or self.get_stored_headers()
if 'last-modified' in stored_data:
request.setValue_forHTTPHeaderField_(
stored_data['last-modified'], 'if-modified-since')
if 'etag' in stored_data:
request.setValue_forHTTPHeaderField_(
stored_data['etag'], 'if-none-match')
self.connection = NSURLConnection.alloc().initWithRequest_delegate_(
request, self)
from purl import Purl
from urllib import urlencode
form_data = [
("field1", "value1"),
("field2", "A complex value example:?!$"),
]
options = dict()
options["url"] = "http://localhost:8000/"
options["file"] = "/Users/mike/Desktop/out.data"
options["content_type"] = "application/x-www-form-urlencoded"
options["body"] = urlencode(form_data)
# Build Purl with initial settings
p = Purl.alloc().initWithOptions_(options)
# Start it
p.start()
# Need to collect the results to have it actually run
result = p.isDone()
WARNING:root:======= POST STARTED =======
WARNING:root:Host: localhost:8000
Content-Type: application/x-www-form-urlencoded
Connection: keep-alive
Accept: */*
User-Agent: Python/2.7.5 CFNetwork/673.5 Darwin/13.4.0 (x86_64) (MacBookAir6%2C2)
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Content-Length: 56
WARNING:root:======= POST VALUES =======
WARNING:root:MiniFieldStorage('field1', 'value1')
WARNING:root:MiniFieldStorage('field2', 'A complex value example:?!$')
WARNING:root:
127.0.0.1 - - [08/Jun/2015 01:52:41] "POST / HTTP/1.1" 200 -
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment