httpie-cli/httpie/models.py
Jakub Roztocil 098e1d3100 Fixed multipart requests output; binary support.
* Bodies of multipart requests are correctly printed (closes #30).
* Binary requests and responses should always work (they are also suppressed
  for terminal output). So things like this work::

     http www.google.com/favicon.ico > favicon.ico
2012-07-28 05:50:12 +02:00

132 lines
4.1 KiB
Python

import os
import sys
from requests.compat import urlparse, is_windows, bytes, str
class Environment(object):
"""Holds information about the execution context.
Groups various aspects of the environment in a changeable object
and allows for mocking.
"""
progname = os.path.basename(sys.argv[0])
if progname not in ['http', 'https']:
progname = 'http'
stdin_isatty = sys.stdin.isatty()
stdin = sys.stdin
if is_windows:
# `colorama` patches `sys.stdout` so its initialization
# needs to happen before the default environment is set.
import colorama
colorama.init()
del colorama
stdout_isatty = sys.stdout.isatty()
stdout = sys.stdout
stderr = sys.stderr
# Can be set to 0 to disable colors completely.
colors = 256 if '256color' in os.environ.get('TERM', '') else 88
def __init__(self, **kwargs):
self.__dict__.update(**kwargs)
class HTTPMessage(object):
"""Model representing an HTTP message."""
def __init__(self, line, headers, body, encoding=None, content_type=None):
"""All args are a `str` except for `body` which is a `bytes`."""
assert isinstance(line, str)
assert content_type is None or isinstance(content_type, str)
assert isinstance(body, bytes)
self.line = line # {Request,Status}-Line
self.headers = headers
self.body = body
self.encoding = encoding
self.content_type = content_type
@classmethod
def from_response(cls, response):
"""Make an `HTTPMessage` from `requests.models.Response`."""
encoding = response.encoding or None
original = response.raw._original_response
response_headers = response.headers
status_line = str('HTTP/{version} {status} {reason}'.format(
version='.'.join(str(original.version)),
status=original.status,
reason=original.reason
))
body = response.content
return cls(line=status_line,
headers=str(original.msg),
body=body,
encoding=encoding,
content_type=str(response_headers.get('Content-Type', '')))
@classmethod
def from_request(cls, request):
"""Make an `HTTPMessage` from `requests.models.Request`."""
url = urlparse(request.url)
# Querystring
qs = ''
if url.query or request.params:
qs = '?'
if url.query:
qs += url.query
# Requests doesn't make params part of ``request.url``.
if request.params:
if url.query:
qs += '&'
#noinspection PyUnresolvedReferences
qs += type(request)._encode_params(request.params)
# Request-Line
request_line = str('{method} {path}{query} HTTP/1.1'.format(
method=request.method,
path=url.path or '/',
query=qs
))
# Headers
headers = dict(request.headers)
content_type = headers.get('Content-Type')
if isinstance(content_type, bytes):
# Happens when uploading files.
# TODO: submit a bug report for Requests
content_type = headers['Content-Type'] = content_type.decode('utf8')
if 'Host' not in headers:
headers['Host'] = url.netloc
headers = '\n'.join('%s: %s' % (name, value)
for name, value in headers.items())
# Body
if request.files:
body, _ = request._encode_files(request.files)
else:
try:
body = request.data
except AttributeError:
# requests < 0.12.1
body = request._enc_data
if isinstance(body, dict):
#noinspection PyUnresolvedReferences
body = type(request)._encode_params(body)
body = body.encode('utf8')
return cls(line=request_line,
headers=headers,
body=body,
content_type=content_type)