Added the option to print the request

It is now possible to print any combination of the following
request-response bits:

    - Request headers (H)
    - Request body (B)
    - Response headers (h)
    - Response body (b)

The output is controlled by the --print / -p option which
defaults to "hb" (i.e., response headers and response body).

Note that -p was previously shortcut for --prety.

Closes #29.
This commit is contained in:
Jakub Roztocil 2012-03-14 00:05:44 +01:00
parent 31c28807c9
commit 02622a4135
6 changed files with 168 additions and 57 deletions

View File

@ -1,4 +1,4 @@
Copyright © 2012 Jakub Roztocil
Copyright © 2012 Jakub Roztocil <jakub@roztocil.name>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@ -89,9 +89,11 @@ Flags
Most of the flags mirror the arguments understood by ``requests.request``. See ``http -h`` for more details::
usage: http [-h] [--version] [--json | --form] [--traceback]
[--pretty | --ugly] [--headers | --body] [--style STYLE]
[--auth AUTH] [--verify VERIFY] [--proxy PROXY]
[--allow-redirects] [--file PATH] [--timeout TIMEOUT]
[--pretty | --ugly]
[--print OUTPUT_OPTIONS | --headers | --body]
[--style STYLE] [--auth AUTH] [--verify VERIFY]
[--proxy PROXY] [--allow-redirects] [--file PATH]
[--timeout TIMEOUT]
METHOD URL [items [items ...]]
HTTPie - cURL for humans.
@ -113,13 +115,21 @@ Most of the flags mirror the arguments understood by ``requests.request``. See `
Type to application/x-www-form-urlencoded, if not
specified.
--traceback Print exception traceback should one occur.
--pretty, -p If stdout is a terminal, the response is prettified by
--pretty If stdout is a terminal, the response is prettified by
default (colorized and indented if it is JSON). This
flag ensures prettifying even when stdout is
redirected.
--ugly, -u Do not prettify the response.
--headers, -t Print only the response headers.
--body, -b Print only the response body.
--print OUTPUT_OPTIONS, -p OUTPUT_OPTIONS
String specifying what should the output contain. "H"
stands for request headers and "B" for request body.
"h" stands for response headers and "b" for response
body. Defaults to "hb" which means that the whole
response (headers and body) is printed.
--headers, -t Print only the response headers. It's a shortcut for
--print=h.
--body, -b Print only the response body. It's a shortcut for
--print=b.
--style STYLE, -s STYLE
Output coloring style, one of autumn, borland, bw,
colorful, default, emacs, friendly, fruity, manni,

View File

@ -3,5 +3,5 @@ HTTPie - cURL for humans.
"""
__author__ = 'Jakub Roztocil'
__version__ = '0.1.6'
__version__ = '0.1.7-dev'
__licence__ = 'BSD'

View File

@ -2,6 +2,7 @@
import os
import sys
import json
from urlparse import urlparse
import requests
try:
from collections import OrderedDict
@ -18,6 +19,69 @@ TYPE_FORM = 'application/x-www-form-urlencoded; charset=utf-8'
TYPE_JSON = 'application/json; charset=utf-8'
class HTTPMessage(object):
def __init__(self, line, headers, body, content_type=None):
# {Request,Status}-Line
self.line = line
self.headers = headers
self.body = body
self.content_type = content_type
def format_http_message(message, prettifier=None,
with_headers=True, with_body=True):
bits = []
if with_headers:
if prettifier:
bits.append(prettifier.headers(message.line))
bits.append(prettifier.headers(message.headers))
else:
bits.append(message.line)
bits.append(message.headers)
if with_body:
bits.append('\n')
if with_body:
if prettifier and message.content_type:
bits.append(prettifier.body(message.body, message.content_type))
else:
bits.append(message.body)
bits = [bit.strip() for bit in bits]
bits.append('')
return '\n'.join(bits)
def make_request_message(request):
"""Make an `HTTPMessage` from `requests.models.Request`."""
url = urlparse(request.url)
request_headers = dict(request.headers)
request_headers['Host'] = url.netloc
return HTTPMessage(
line='{method} {path} HTTP/1.1'.format(
method=request.method,
path=url.path),
headers='\n'.join('%s: %s' % (name, value)
for name, value
in request_headers.iteritems()),
body=request._enc_data,
content_type=request_headers.get('Content-Type')
)
def make_response_message(response):
"""Make an `HTTPMessage` from `requests.models.Response`."""
encoding = response.encoding or 'ISO-8859-1'
original = response.raw._original_response
response_headers = response.headers
return HTTPMessage(
line='HTTP/{version} {status} {reason}'.format(
version='.'.join(str(original.version)),
status=original.status, reason=original.reason,),
headers=str(original.msg).decode(encoding),
body=response.content.decode(encoding) if response.content else u'',
content_type=response_headers.get('Content-Type'))
def main(args=None,
stdin=sys.stdin,
stdin_isatty=sys.stdin.isatty(),
@ -28,7 +92,8 @@ def main(args=None,
args = parser.parse_args(args if args is not None else sys.argv[1:])
do_prettify = (args.prettify is True or
(args.prettify == cli.PRETTIFY_STDOUT_TTY_ONLY and stdout_isatty))
(args.prettify == cli.PRETTIFY_STDOUT_TTY_ONLY
and stdout_isatty))
# Parse request headers and data from the command line.
headers = CaseInsensitiveDict()
@ -79,36 +144,31 @@ def main(args=None,
sys.stderr.write(str(e.message) + '\n')
sys.exit(1)
# Reconstruct the raw response.
encoding = response.encoding or 'ISO-8859-1'
original = response.raw._original_response
status_line, headers, body = (
'HTTP/{version} {status} {reason}'.format(
version='.'.join(str(original.version)),
status=original.status, reason=original.reason,
),
str(original.msg).decode(encoding),
response.content.decode(encoding) if response.content else u''
)
prettifier = pretty.PrettyHttp(args.style) if do_prettify else None
if do_prettify:
prettify = pretty.PrettyHttp(args.style)
if args.print_headers:
status_line = prettify.headers(status_line)
headers = prettify.headers(headers)
if args.print_body and 'Content-Type' in response.headers:
body = prettify.body(body, response.headers['Content-Type'])
output_request = (cli.OUT_REQUEST_HEADERS in args.output_options
or cli.OUT_REQUEST_BODY in args.output_options)
# Output.
# TODO: preserve leading/trailing whitespaces in the body.
# Some of the Pygments styles add superfluous line breaks.
if args.print_headers:
stdout.write(status_line.strip())
output_response = (cli.OUT_RESPONSE_HEADERS in args.output_options
or cli.OUT_RESPONSE_BODY in args.output_options)
if output_request:
stdout.write(format_http_message(
message=make_request_message(response.request),
prettifier=prettifier,
with_headers=cli.OUT_REQUEST_HEADERS in args.output_options,
with_body=cli.OUT_REQUEST_BODY in args.output_options
).encode('utf-8'))
if output_response:
stdout.write('\n')
stdout.write(headers.strip().encode('utf-8'))
stdout.write('\n\n')
if args.print_body:
stdout.write(body.strip().encode('utf-8'))
if output_response:
stdout.write(format_http_message(
message=make_response_message(response),
prettifier=prettifier,
with_headers=cli.OUT_RESPONSE_HEADERS in args.output_options,
with_body=cli.OUT_RESPONSE_BODY in args.output_options
).encode('utf-8'))
stdout.write('\n')

View File

@ -12,6 +12,16 @@ SEP_DATA = '='
SEP_DATA_RAW_JSON = ':='
PRETTIFY_STDOUT_TTY_ONLY = object()
OUT_REQUEST_HEADERS = 'H'
OUT_REQUEST_BODY = 'B'
OUT_RESPONSE_HEADERS = 'h'
OUT_RESPONSE_BODY = 'b'
OUTPUT_OPTIONS = [OUT_REQUEST_HEADERS,
OUT_REQUEST_BODY,
OUT_RESPONSE_HEADERS,
OUT_RESPONSE_BODY]
class ParseError(Exception):
pass
@ -72,7 +82,19 @@ def _(text):
return ' '.join(text.strip().split())
parser = argparse.ArgumentParser(description=doc.strip(),)
class HTTPieArgumentParser(argparse.ArgumentParser):
def parse_args(self, args=None, namespace=None):
args = super(HTTPieArgumentParser, self).parse_args(args, namespace)
self._validate_output_options(args)
return args
def _validate_output_options(self, args):
unknown_output_options = set(args.output_options) - set(OUTPUT_OPTIONS)
if unknown_output_options:
self.error('Unknown output options: %s' % ','.join(unknown_output_options))
parser = HTTPieArgumentParser(description=doc.strip(),)
parser.add_argument('--version', action='version', version=version)
# Content type.
@ -96,7 +118,7 @@ group_type.add_argument(
)
# Output options.
# output_options options.
#############################################
parser.add_argument(
@ -108,13 +130,12 @@ parser.add_argument(
prettify = parser.add_mutually_exclusive_group(required=False)
prettify.add_argument(
'--pretty', '-p', dest='prettify', action='store_true',
'--pretty', dest='prettify', action='store_true',
default=PRETTIFY_STDOUT_TTY_ONLY,
help=_('''
If stdout is a terminal,
the response is prettified by default (colorized and
indented if it is JSON). This flag ensures
prettifying even when stdout is redirected.
If stdout is a terminal, the response is prettified
by default (colorized and indented if it is JSON).
This flag ensures prettifying even when stdout is redirected.
''')
)
prettify.add_argument(
@ -124,21 +145,41 @@ prettify.add_argument(
''')
)
only = parser.add_mutually_exclusive_group(required=False)
only.add_argument(
'--headers', '-t', dest='print_body',
action='store_false', default=True,
help=('''
output_options = parser.add_mutually_exclusive_group(required=False)
output_options.add_argument('--print', '-p', dest='output_options',
default=OUT_RESPONSE_HEADERS + OUT_RESPONSE_BODY,
help=_('''
String specifying what should the output contain.
"{request_headers}" stands for request headers and
"{request_body}" for request body.
"{response_headers}" stands for response headers and
"{response_body}" for response body.
Defaults to "hb" which means that the whole response
(headers and body) is printed.
'''.format(
request_headers=OUT_REQUEST_HEADERS,
request_body=OUT_REQUEST_BODY,
response_headers=OUT_RESPONSE_HEADERS,
response_body=OUT_RESPONSE_BODY,
))
)
output_options.add_argument(
'--headers', '-t', dest='output_options',
action='store_const', const=OUT_RESPONSE_HEADERS,
help=_('''
Print only the response headers.
''')
It's a shortcut for --print={0}.
'''.format(OUT_RESPONSE_HEADERS))
)
only.add_argument(
'--body', '-b', dest='print_headers',
action='store_false', default=True,
help=('''
output_options.add_argument(
'--body', '-b', dest='output_options',
action='store_const', const=OUT_RESPONSE_BODY,
help=_('''
Print only the response body.
''')
It's a shortcut for --print={0}.
'''.format(OUT_RESPONSE_BODY))
)
parser.add_argument(
'--style', '-s', dest='style', default='solarized', metavar='STYLE',
choices=pretty.AVAILABLE_STYLES,

View File

@ -8,8 +8,8 @@ from pygments.formatters.terminal256 import Terminal256Formatter
from pygments.formatters.terminal import TerminalFormatter
from pygments.lexer import RegexLexer, bygroups
from pygments.styles import get_style_by_name, STYLE_MAP
from . import solarized
from .pygson import JSONLexer
from . import solarized
DEFAULT_STYLE = 'solarized'