@@ -82,6 +82,9 @@ def self.build(client, mechanism, *args, sasl_ir: true, **kwargs, &block)
8282        # An exception that has been raised by <tt>authenticator.process</tt>. 
8383        attr_reader  :process_error 
8484
85+         # An exception that represents an error response from the server. 
86+         attr_reader  :response_error 
87+ 
8588        def  initialize ( client ,  mechanism ,  authenticator ,  sasl_ir : true ) 
8689          client  =>  SASL ::ClientAdapter 
8790          @client  =  client 
@@ -104,9 +107,11 @@ def initialize(client, mechanism, authenticator, sasl_ir: true)
104107        # Unfortunately, the original error will not be the +#cause+ for the 
105108        # client error.  But it will be available on #process_error. 
106109        def  authenticate 
107-           client . run_command ( mechanism ,  initial_response )  {  process  _1  } 
108-             . tap  {  raise  process_error  if  process_error  } 
109-             . tap  {  raise  AuthenticationIncomplete ,  _1  unless  done?  } 
110+           handle_cancellation  do 
111+             client . run_command ( mechanism ,  initial_response )  {  process  _1  } 
112+               . tap  {  raise  process_error  if  process_error  } 
113+               . tap  {  raise  AuthenticationIncomplete ,  _1  unless  done?  } 
114+           end 
110115        rescue  AuthenticationCanceled ,  *client . response_errors 
111116          raise  # but don't drop the connection 
112117        rescue 
@@ -142,11 +147,53 @@ def process(challenge)
142147          @processed  =  true 
143148          return  client . cancel_response  if  process_error 
144149          client . encode  authenticator . process  client . decode  challenge 
145-         rescue  =>  process_error 
146-           @process_error  =  process_error 
150+         rescue  AuthenticationCanceled  =>  error 
151+           @process_error  =  error 
152+           client . cancel_response 
153+         rescue  =>  error 
154+           @process_error  =  begin 
155+             raise  AuthenticationError ,  "error while processing server challenge" 
156+           rescue 
157+             $!
158+           end 
147159          client . cancel_response 
148160        end 
149161
162+         # | process | response | => result                                | 
163+         # |---------|----------|------------------------------------------| 
164+         # | success | success  | success                                  | 
165+         # | success | error    | reraise response error                   | 
166+         # | error   | success  | raise incomplete error (cause = process) | 
167+         # | error   | error    | raise canceled error   (cause = process) | 
168+         def  handle_cancellation 
169+           result  =  begin 
170+             yield 
171+           rescue  *client . response_errors  =>  error 
172+             @response_error  =  error 
173+             raise  unless  process_error 
174+           end 
175+           raise_mutual_cancellation!        if  process_error  &&  response_error 
176+           raise_incomplete_cancel! ( result )  if  process_error  && !response_error 
177+           result 
178+         end 
179+ 
180+         def  raise_mutual_cancellation! 
181+           raise  process_error  # sets the cause 
182+         rescue 
183+           raise  AuthenticationCanceled . new ( 
184+             "authentication canceled (see error #cause and #response)" , 
185+             response : response_error 
186+           ) 
187+         end 
188+ 
189+         def  raise_incomplete_cancellation! 
190+           raise  process_error  # sets the cause 
191+         rescue 
192+           raise  AuthenticationIncomplete . new ( 
193+             response_error ,  "server ignored canceled authentication" 
194+           ) 
195+         end 
196+ 
150197      end 
151198    end 
152199  end 
0 commit comments