3232import logging
3333from datetime import datetime
3434from functools import wraps
35+ from StringIO import StringIO
3536
3637from contextlib import contextmanager
3738
@@ -63,16 +64,6 @@ def new_f(*args, **kwargs):
6364 return val
6465 return new_f
6566
66- # Custom exceptions
67- class AuthenticationError (Exception ):
68- """Raised when a login request to Splunk fails.
69-
70- If your username was unknown or you provided an incorrect password
71- in a call to :meth:`Context.login` or :meth:`splunklib.client.Service.login`,
72- this exception is raised.
73- """
74- pass
75-
7667# Singleton values to eschew None
7768class _NoAuthenticationToken (object ):
7869 """The value stored in a :class:`Context` or :class:`splunklib.client.Service`
@@ -187,7 +178,7 @@ def _handle_auth_error(msg):
187178 yield
188179 except HTTPError as he :
189180 if he .status == 401 :
190- raise AuthenticationError (msg )
181+ raise AuthenticationError (msg , he )
191182 else :
192183 raise
193184
@@ -234,7 +225,11 @@ def wrapper(self, *args, **kwargs):
234225 # AuthenticationError if it fails.
235226 self .login ()
236227 else :
237- raise AuthenticationError ("Request aborted: not logged in." )
228+ # Try the request anyway without authentication.
229+ # Most requests will fail. Some will succeed, such as
230+ # 'GET server/info'.
231+ with _handle_auth_error ("Request aborted: not logged in." ):
232+ return request_fun (self , * args , ** kwargs )
238233 try :
239234 # Issue the request
240235 return request_fun (self , * args , ** kwargs )
@@ -248,7 +243,7 @@ def wrapper(self, *args, **kwargs):
248243 with _handle_auth_error ("Autologin succeeded, but there was an auth error on next request. Something's very wrong." ):
249244 return request_fun ()
250245 elif he .status == 401 and not self .autologin :
251- raise AuthenticationError ("Request failed: Session is not logged in." )
246+ raise AuthenticationError ("Request failed: Session is not logged in." , he )
252247 else :
253248 raise
254249 return wrapper
@@ -426,12 +421,15 @@ def _auth_headers(self):
426421
427422 :returns: A list of 2-tuples containing key and value
428423 """
429- # Ensure the token is properly formatted
430- if self .token .startswith ('Splunk ' ):
431- token = self .token
424+ if self .token is _NoAuthenticationToken :
425+ return []
432426 else :
433- token = 'Splunk %s' % self .token
434- return [("Authorization" , token )]
427+ # Ensure the token is properly formatted
428+ if self .token .startswith ('Splunk ' ):
429+ token = self .token
430+ else :
431+ token = 'Splunk %s' % self .token
432+ return [("Authorization" , token )]
435433
436434 def connect (self ):
437435 """Returns an open connection (socket) to the Splunk instance.
@@ -757,7 +755,7 @@ def login(self):
757755 return self
758756 except HTTPError as he :
759757 if he .status == 401 :
760- raise AuthenticationError ("Login failed." )
758+ raise AuthenticationError ("Login failed." , he )
761759 else :
762760 raise
763761
@@ -874,18 +872,33 @@ def connect(**kwargs):
874872# handler that wants to read multiple messages can do so.
875873class HTTPError (Exception ):
876874 """This exception is raised for HTTP responses that return an error."""
877- def __init__ (self , response ):
875+ def __init__ (self , response , _message = None ):
878876 status = response .status
879877 reason = response .reason
880878 body = response .body .read ()
881879 detail = XML (body ).findtext ("./messages/msg" )
882880 message = "HTTP %d %s%s" % (
883881 status , reason , "" if detail is None else " -- %s" % detail )
884- Exception .__init__ (self , message )
882+ Exception .__init__ (self , _message or message )
885883 self .status = status
886884 self .reason = reason
887885 self .headers = response .headers
888886 self .body = body
887+ self ._response = response
888+
889+ class AuthenticationError (HTTPError ):
890+ """Raised when a login request to Splunk fails.
891+
892+ If your username was unknown or you provided an incorrect password
893+ in a call to :meth:`Context.login` or :meth:`splunklib.client.Service.login`,
894+ this exception is raised.
895+ """
896+ def __init__ (self , message , cause ):
897+ # Put the body back in the response so that HTTPError's constructor can
898+ # read it again.
899+ cause ._response .body = StringIO (cause .body )
900+
901+ HTTPError .__init__ (self , cause ._response , message )
889902
890903#
891904# The HTTP interface used by the Splunk binding layer abstracts the underlying
0 commit comments