diff --git a/README.rst b/README.rst index a085287c..1b81514b 100644 --- a/README.rst +++ b/README.rst @@ -938,9 +938,10 @@ Sessions ======== By default, every request is completely independent of the previous ones. -HTTPie also supports persistent sessions, where custom headers, authorization, -and cookies (manually specified or sent by the server) persist between -requests to the same host. +HTTPie also supports persistent sessions, where custom headers (except for the +ones starting with ``Content-`` or ``If-``), authorization, and cookies +(manually specified or sent by the server) persist between requests +to the same host. Create a new session named ``user1``: diff --git a/httpie/client.py b/httpie/client.py index 49fcdbd6..0f7daf79 100644 --- a/httpie/client.py +++ b/httpie/client.py @@ -29,7 +29,7 @@ def get_response(args, config_dir): response = sessions.get_response( config_dir=config_dir, name=args.session or args.session_read_only, - request_kwargs=requests_kwargs, + requests_kwargs=requests_kwargs, read_only=bool(args.session_read_only), ) diff --git a/httpie/sessions.py b/httpie/sessions.py index 9824d233..e2410b4a 100644 --- a/httpie/sessions.py +++ b/httpie/sessions.py @@ -19,7 +19,13 @@ SESSIONS_DIR_NAME = 'sessions' DEFAULT_SESSIONS_DIR = os.path.join(DEFAULT_CONFIG_DIR, SESSIONS_DIR_NAME) -def get_response(name, request_kwargs, config_dir, read_only=False): +# Request headers starting with these prefixes won't be stored in sessions. +# They are specific to each request. +# http://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Requests +SESSION_IGNORED_HEADER_PREFIXES = ['Content-', 'If-'] + + +def get_response(name, requests_kwargs, config_dir, read_only=False): """Like `client.get_response`, but applies permanent aspects of the session to the request. @@ -27,28 +33,31 @@ def get_response(name, request_kwargs, config_dir, read_only=False): sessions_dir = os.path.join(config_dir, SESSIONS_DIR_NAME) host = Host( root_dir=sessions_dir, - name=request_kwargs['headers'].get('Host', None) - or urlsplit(request_kwargs['url']).netloc.split('@')[-1] + name=requests_kwargs['headers'].get('Host', None) + or urlsplit(requests_kwargs['url']).netloc.split('@')[-1] ) session = Session(host, name) session.load() + # Merge request and session headers to get final headers for this request. + request_headers = requests_kwargs.get('headers', {}) + merged_headers = session.headers.copy() + merged_headers.update(request_headers) + requests_kwargs['headers'] = merged_headers # Update session headers with the request headers. - session['headers'].update(request_kwargs.get('headers', {})) - # Use the merged headers for the request - request_kwargs['headers'] = session['headers'] + session.update_headers(request_headers) - auth = request_kwargs.get('auth', None) + auth = requests_kwargs.get('auth', None) if auth: session.auth = auth elif session.auth: - request_kwargs['auth'] = session.auth + requests_kwargs['auth'] = session.auth requests_session = requests.Session() requests_session.cookies = session.cookies try: - response = requests_session.request(**request_kwargs) + response = requests_session.request(**requests_kwargs) except Exception: raise else: @@ -138,6 +147,29 @@ class Session(BaseConfigDict): def verbose_name(self): return '%s %s %s' % (self.host.name, self.name, self.path) + def update_headers(self, request_headers): + """ + Update the session headers with the request ones while ignoring + certain name prefixes. + + :type request_headers: dict + + """ + for name, value in request_headers.items(): + + if name == 'User-Agent' and value.startswith('HTTPie/'): + continue + + for prefix in SESSION_IGNORED_HEADER_PREFIXES: + if name.lower().startswith(prefix.lower()): + break + else: + self['headers'][name] = value + + @property + def headers(self): + return self['headers'] + @property def cookies(self): jar = RequestsCookieJar() @@ -149,6 +181,9 @@ class Session(BaseConfigDict): @cookies.setter def cookies(self, jar): + """ + :type jar: CookieJar + """ # http://docs.python.org/2/library/cookielib.html#cookie-objects stored_attrs = ['value', 'path', 'secure', 'expires'] self['cookies'] = {} diff --git a/requirements-dev.txt b/requirements-dev.txt index 5b4e154c..c3b33209 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,3 +1,3 @@ tox -httpbin +git+git://github.com/kennethreitz/httpbin.git@7c96875e87a448f08fb1981e85eb79e77d592d98 docutils diff --git a/tests/tests.py b/tests/tests.py index 79b85887..6b6e062d 100755 --- a/tests/tests.py +++ b/tests/tests.py @@ -1370,6 +1370,26 @@ class SessionTest(BaseTestCase): self.assertEqual(r.json['headers']['Cookie'], 'hello=world') self.assertIn('Basic ', r.json['headers']['Authorization']) + def test_session_ignored_header_prefixes(self): + r = http( + '--session=test', + 'GET', + httpbin('/get'), + 'Content-Type: text/plain', + 'If-Unmodified-Since: Sat, 29 Oct 1994 19:43:31 GMT', + env=self.env + ) + self.assertIn(OK, r) + + r2 = http( + '--session=test', + 'GET', + httpbin('/get') + ) + self.assertIn(OK, r2) + self.assertNotIn('Content-Type', r2.json['headers']) + self.assertNotIn('If-Unmodified-Since', r2.json['headers']) + def test_session_update(self): # Get a response to a request from the original session. r1 = http(