Permalink
Browse files

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.
  • Loading branch information...
1 parent 31c2880 commit 02622a4135077b2635428fbf6885927b80b55b68 @jkbrzt committed Mar 13, 2012
Showing with 169 additions and 58 deletions.
  1. +1 −1 LICENSE
  2. +17 −7 README.rst
  3. +1 −1 httpie/__init__.py
  4. +90 −30 httpie/__main__.py
  5. +59 −18 httpie/cli.py
  6. +1 −1 httpie/pretty.py
View
2 LICENSE
@@ -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
24 README.rst
@@ -89,10 +89,12 @@ 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]
- METHOD URL [items [items ...]]
+ [--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
2 httpie/__init__.py
@@ -3,5 +3,5 @@
"""
__author__ = 'Jakub Roztocil'
-__version__ = '0.1.6'
+__version__ = '0.1.7-dev'
__licence__ = 'BSD'
View
120 httpie/__main__.py
@@ -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_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.
- # 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())
- 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'))
+ output_request = (cli.OUT_REQUEST_HEADERS in args.output_options
+ or cli.OUT_REQUEST_BODY in args.output_options)
+
+ 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')
+
+ 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
77 httpie/cli.py
@@ -12,6 +12,16 @@
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 @@ def _(text):
)
-# Output options.
+# output_options options.
#############################################
parser.add_argument(
@@ -108,13 +130,12 @@ def _(text):
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 @@ def _(text):
''')
)
-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
2 httpie/pretty.py
@@ -8,8 +8,8 @@
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'

0 comments on commit 02622a4

Please sign in to comment.