From 316e3f45a9d0d5baca14f72248d3f8b4927520cc Mon Sep 17 00:00:00 2001 From: Jakub Roztocil Date: Fri, 7 Sep 2012 12:38:52 +0200 Subject: [PATCH] Added `--session-read` for read-only sessions. --- README.rst | 6 ++++-- httpie/cli.py | 14 ++++++++++---- httpie/client.py | 13 +++++++++---- httpie/sessions.py | 43 ++++++++++++++++++++++++++++++------------- 4 files changed, 53 insertions(+), 23 deletions(-) diff --git a/README.rst b/README.rst index df35fc17..92216d26 100644 --- a/README.rst +++ b/README.rst @@ -509,13 +509,12 @@ Sessions *NOTE: This is an experimental feature. Feedback appretiated.* HTTPie supports named, per-host sessions, where custom headers, authorization, -and cookies sent by the server persist between requests: +and cookies (manually specified or sent by the server) persist between requests: .. code-block:: bash $ http --session user1 -a user1:password example.org X-Foo:Bar - Now you can refer to the session by its name: .. code-block:: bash @@ -529,6 +528,9 @@ To switch to another session simple pass a different name: $ http --session user2 -a user2:password example.org X-Bar:Foo +To use an existing session without updating it from the request/response +exchange, specify the session via ``--session-read=SESSION_NAME`` instead. + You can view and manipulate existing sessions via the ``httpie`` management command, see ``httpie --help``. diff --git a/httpie/cli.py b/httpie/cli.py index 61d16f2c..e5c7a0be 100644 --- a/httpie/cli.py +++ b/httpie/cli.py @@ -219,19 +219,25 @@ output_options.add_argument('--stream', '-S', action='store_true', default=False ############################################################################### -# Misc +# Sessions ############################################################################### -misc = parser.add_argument_group(title='Sessions') -misc.add_argument( +sessions = parser.add_argument_group(title='Sessions')\ + .add_mutually_exclusive_group(required=False) + +sessions.add_argument( '--session', metavar='SESSION_NAME', help=_(''' - Create or reuse a session. + Create, or reuse and update a session. Withing a session, custom headers, auth credential, as well as any cookies sent by the server persist between requests. You can use the `httpie' management command to manipulate and inspect existing sessions. See `httpie --help'. ''') ) +sessions.add_argument( + '--session-read', metavar='SESSION_NAME', + help=_('''Create or reuse a session, but do not update it once saved.''') +) ############################################################################### diff --git a/httpie/client.py b/httpie/client.py index 90ef5f22..f262f1b7 100644 --- a/httpie/client.py +++ b/httpie/client.py @@ -16,6 +16,7 @@ DEFAULT_UA = 'HTTPie/%s' % __version__ def get_response(args): + """Send the request and return a `request.Response`.""" requests_kwargs = get_requests_kwargs(args) @@ -23,14 +24,18 @@ def get_response(args): sys.stderr.write( '\n>>> requests.request(%s)\n\n' % pformat(requests_kwargs)) - if args.session: - return sessions.get_response(args.session, requests_kwargs) - else: + if not args.session and not args.session_read: return requests.request(**requests_kwargs) + else: + return sessions.get_response( + name=args.session or args.session_read, + request_kwargs=requests_kwargs, + read_only=bool(args.session_read), + ) def get_requests_kwargs(args): - """Send the request and return a `request.Response`.""" + """Translate our `args` into `requests.request` keyword arguments.""" base_headers = defaults['base_headers'].copy() base_headers['User-Agent'] = DEFAULT_UA diff --git a/httpie/sessions.py b/httpie/sessions.py index 82aca2db..8bf7ea7e 100644 --- a/httpie/sessions.py +++ b/httpie/sessions.py @@ -10,10 +10,11 @@ import codecs import shutil import subprocess +import requests from requests.compat import urlparse -from requests import Session as RSession from requests.cookies import RequestsCookieJar, create_cookie from requests.auth import HTTPBasicAuth, HTTPDigestAuth +from argparse import OPTIONAL from.import __version__ from.config import CONFIG_DIR @@ -23,7 +24,11 @@ from.output import PygmentsProcessor SESSIONS_DIR = os.path.join(CONFIG_DIR, 'sessions') -def get_response(name, request_kwargs): +def get_response(name, request_kwargs, read_only=False): + """Like `client.get_response`, but applies permanent + aspects of the session to the request. + + """ host = Host(request_kwargs['headers'].get('Host', None) or urlparse(request_kwargs['url']).netloc.split('@')[-1]) @@ -41,22 +46,26 @@ def get_response(name, request_kwargs): elif session.auth: request_kwargs['auth'] = session.auth - rsession = RSession(cookies=session.cookies) + rsession = requests.Session(cookies=session.cookies) try: response = rsession.request(**request_kwargs) except Exception: raise else: - session.cookies = rsession.cookies - session.save() + if not read_only or session.is_new: + session.cookies = rsession.cookies + session.save() return response class Host(object): + """A host is a per-host directory on the disk containing sessions files.""" + def __init__(self, name): self.name = name def __iter__(self): + """Return a iterator yielding `(session_name, session_path)`.""" for fn in sorted(glob.glob1(self.path, '*.json')): yield os.path.splitext(fn)[0], os.path.join(self.path, fn) @@ -78,12 +87,15 @@ class Host(object): @classmethod def all(cls): + """Return a generator yielding a host at a time.""" for name in sorted(glob.glob1(SESSIONS_DIR, '*')): if os.path.isdir(os.path.join(SESSIONS_DIR, name)): yield Host(name) class Session(dict): + """""" + def __init__(self, host, name, *args, **kwargs): super(Session, self).__init__(*args, **kwargs) self.host = host @@ -91,10 +103,6 @@ class Session(dict): self['headers'] = {} self['cookies'] = {} - @property - def path(self): - return os.path.join(self.host.path, self.name + '.json') - def load(self): try: with open(self.path, 'rt') as f: @@ -121,6 +129,14 @@ class Session(dict): if e.errno != errno.ENOENT: raise + @property + def path(self): + return os.path.join(self.host.path, self.name + '.json') + + @property + def is_new(self): + return not os.path.exists(self.path) + @property def cookies(self): jar = RequestsCookieJar() @@ -163,7 +179,7 @@ class Session(dict): HTTPDigestAuth: 'digest'}[type(cred)], 'username': cred.username, 'password': cred.password, - } + } def list_command(args): @@ -211,13 +227,14 @@ def edit_command(args): def add_commands(subparsers): + # List list_ = subparsers.add_parser('session-list', help='list sessions') list_.set_defaults(command=list_command) - list_.add_argument('host', nargs='?') + list_.add_argument('host', nargs=OPTIONAL) # Show - show = subparsers.add_parser('session-show', help='list or show sessions') + show = subparsers.add_parser('session-show', help='show a session') show.set_defaults(command=show_command) show.add_argument('host') show.add_argument('name') @@ -233,6 +250,6 @@ def add_commands(subparsers): delete = subparsers.add_parser('session-delete', help='delete a session') delete.set_defaults(command=delete_command) delete.add_argument('host') - delete.add_argument('name', nargs='?', + delete.add_argument('name', nargs=OPTIONAL, help='The name of the session to be deleted.' ' If not specified, all host sessions are deleted.')