Skip to content

Instantly share code, notes, and snippets.

@matteomattei
Last active July 8, 2021 05:55
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save matteomattei/092dfc31798f909403580994275fa69e to your computer and use it in GitHub Desktop.
Save matteomattei/092dfc31798f909403580994275fa69e to your computer and use it in GitHub Desktop.
HTTP/HTTPS GET and POST (including files) with built-in python modules
#!/usr/bin/env python3
# This script performs a GET and a POST request through HTTP/HTTPS using
# builtin python3 moudules. There is also a class to encode files for upload!
import urllib.request
import http.client
import mimetypes
import codecs
import uuid
import binascii
import io
import os
import sys
REMOTE_PROTOCOL = 'http' # or https
REMOTE_HOST = 'httpbin.org'
REMOTE_PORT = '80' # 443 for https
USERAGENT = 'MySuperUserAgent'
class MultipartFormdataEncoder(object):
def __init__(self):
self.boundary = uuid.uuid4().hex
self.content_type = 'multipart/form-data; boundary={}'.format(self.boundary)
@classmethod
def u(cls, s):
if sys.hexversion < 0x03000000 and isinstance(s, str):
s = s.decode('utf-8')
if sys.hexversion >= 0x03000000 and isinstance(s, bytes):
s = s.decode('utf-8')
return s
def iter(self, fields, files):
"""
fields is a sequence of (name, value) elements for regular form fields.
files is a sequence of (name, filename, file-type) elements for data to be uploaded as files
Yield body's chunk as bytes
"""
encoder = codecs.getencoder('utf-8')
for (key, value) in fields:
key = self.u(key)
yield encoder('--{}\r\n'.format(self.boundary))
yield encoder(self.u('Content-Disposition: form-data; name="{}"\r\n').format(key))
yield encoder('\r\n')
if isinstance(value, int) or isinstance(value, float):
value = str(value)
yield encoder(self.u(value))
yield encoder('\r\n')
for (key, filename, fpath) in files:
key = self.u(key)
filename = self.u(filename)
yield encoder('--{}\r\n'.format(self.boundary))
yield encoder(self.u('Content-Disposition: form-data; name="{}"; filename="{}"\r\n').format(key, filename))
yield encoder('Content-Type: {}\r\n'.format(mimetypes.guess_type(filename)[0] or 'application/octet-stream'))
yield encoder('\r\n')
with open(fpath,'rb') as fd:
buff = fd.read()
yield (buff, len(buff))
yield encoder('\r\n')
yield encoder('--{}--\r\n'.format(self.boundary))
def encode(self, fields, files):
body = io.BytesIO()
for chunk, chunk_len in self.iter(fields, files):
body.write(chunk)
return self.content_type, body.getvalue()
if len(sys.argv) < 2:
sys.stderr.write('Please pass a file as argument\n')
sys.exit(1)
if not os.path.isfile(sys.argv[1]):
sys.stderr.write('%s is not a valid file\n' % sys.argv[1])
sys.exit(1)
# SIMPLE HTTP GET
req = urllib.request.Request(REMOTE_PROTOCOL+'://'+REMOTE_HOST+':'+REMOTE_PORT+'/get',headers={'User-Agent':USERAGENT})
with urllib.request.urlopen(req,timeout=10) as f:
buf = f.read(300).decode('utf-8')
print("GET RESPONSE")
print(buf)
# POST FIELDS AND FILES
fields = [('param1','paramvalue1'),('param2','paramversion2'),('param3','paramvalue3')]
files = [('fieldname',os.path.basename(sys.argv[1]),sys.argv[1])]
content_type, body = MultipartFormdataEncoder().encode(fields, files)
h = None
if REMOTE_PROTOCOL == 'http':
h = http.client.HTTPConnection(REMOTE_HOST,REMOTE_PORT)
else:
h = http.client.HTTPSConnection(REMOTE_URL,REMOTE_PORT)
headers = {
'User-Agent': USERAGENT,
'Content-Type': content_type
}
h.request('POST', '/post', body, headers)
res = h.getresponse()
if not res.status in (200,201):
sys.stderr.write(res.read().decode('utf-8')+'\n')
sys.exit(1)
print("POST RESPONSE")
print(res.read().decode('utf-8'))
@andreif
Copy link

andreif commented Apr 4, 2017

Typo REMOTE_URL

@andreif
Copy link

andreif commented Apr 4, 2017

guess_type(filename) should use fpath instead

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment