2 ;;;; Copyright (c) 2008 Zachary Beane, All Rights Reserved
4 ;;;; Redistribution and use in source and binary forms, with or without
5 ;;;; modification, are permitted provided that the following conditions
8 ;;;; * Redistributions of source code must retain the above copyright
9 ;;;; notice, this list of conditions and the following disclaimer.
11 ;;;; * Redistributions in binary form must reproduce the above
12 ;;;; copyright notice, this list of conditions and the following
13 ;;;; disclaimer in the documentation and/or other materials
14 ;;;; provided with the distribution.
16 ;;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
17 ;;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 ;;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 ;;;; ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
20 ;;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 ;;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
22 ;;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 ;;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 ;;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 ;;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 ;;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 ("Message" (bind :message
))
36 (elements-alist :data
)))
38 (defclass amazon-error
(response)
47 :accessor error-data
)))
49 (set-element-class "Error" 'amazon-error
)
51 (defgeneric error-data-value
(name instance
)
52 (:method
(name (response amazon-error
))
53 (cdr (assoc name
(error-data response
) :test
#'equalp
))))
55 (defmethod specialized-initialize ((response amazon-error
) source
)
56 (let ((bindings (xml-bind 'error source
)))
57 (setf (code response
) (bvalue :code bindings
))
58 (setf (message response
) (bvalue :message bindings
))
59 (setf (error-data response
) (bvalue :data bindings
))))
61 (defmethod print-object ((response amazon-error
) stream
)
62 (print-unreadable-object (response stream
:type t
)
63 (prin1 (code response
) stream
)))
65 ;;; Further specializing error messages/conditions
67 (defun report-request-error (condition stream
)
68 (format stream
"~A: ~A"
69 (code (request-error-response condition
))
70 (message (request-error-response condition
))))
72 (define-condition request-error
(error)
75 :reader request-error-request
)
78 :reader request-error-response
)
81 :reader request-error-data
))
82 (:report report-request-error
))
84 (defparameter *specific-errors
* (make-hash-table :test
'equalp
))
86 (defun specific-error (amazon-code)
87 (gethash amazon-code
*specific-errors
* 'request-error
))
89 (defgeneric signal-specific-error
(response condition-name
)
90 (:method
(response (condition-name t
))
92 :request
(request response
)
94 :data
(error-data response
))))
96 (defgeneric maybe-signal-error
(response)
97 (:method
((response t
))
99 (:method
((response amazon-error
))
100 (signal-specific-error response
(specific-error (code response
)))))
102 (eval-when (:compile-toplevel
:load-toplevel
:execute
)
103 (defun error-reader-name (suffix)
104 (intern (concatenate 'string
(symbol-name 'request-error
)
106 (symbol-name suffix
))
109 (defmacro define-specific-error
((condition-name code
)
112 (labels ((slot-name (slot)
117 (keywordify (slot-name slot
)))
118 (slot-definition (slot)
120 :initarg
,(slot-keyword slot
)
121 :reader
,(error-reader-name (slot-name slot
))))
122 (slot-initializer (slot)
123 (list (slot-keyword slot
)
124 `(error-data-value ,(slot-code slot
) response
))))
126 (setf (gethash ,code
*specific-errors
*) ',condition-name
)
127 (define-condition ,condition-name
(,@superclasses request-error
)
128 ,(mapcar #'slot-definition slots
)
130 (defmethod signal-specific-error ((response amazon-error
)
131 (condition-name (eql ',condition-name
)))
132 (error ',condition-name
133 :request
(request response
)
135 :data
(error-data response
)
136 ,@(mapcan #'slot-initializer slots
))))))
139 ;;; The specific errors
141 (define-specific-error (internal-error "InternalError") () ())
143 (define-specific-error (slow-down "SlowDown") () ())
145 (define-specific-error (no-such-bucket "NoSuchBucket") ()
146 ((bucket-name "BucketName")))
148 (define-specific-error (no-such-key "NoSuchKey") ()
151 (define-specific-error (access-denied "AccessDenied") () ())
153 (define-specific-error (malformed-xml "MalformedXML") () ())
155 (define-condition redirect-error
(error) ())
157 (define-specific-error (permanent-redirect "PermanentRedirect") (redirect-error)
158 ((endpoint "Endpoint")))
160 (define-specific-error (temporary-redirect "TemporaryRedirect") (redirect-error)
161 ((endpoint "Endpoint")))
163 (define-specific-error (signature-mismatch "SignatureDoesNotMatch") ()
164 ((string-to-sign "StringToSign")
165 (canonical-request "CanonicalRequest"))
166 (:report
(lambda (condition stream
)
167 (report-request-error condition stream
)
168 (format stream
"You signed: ~S~%Amazon signed: ~S~%and~%~S"
169 (signed-string (request-error-request condition
))
170 (request-error-string-to-sign condition
)
171 (request-error-canonical-request condition
)))))
173 (define-specific-error (precondition-failed "PreconditionFailed") ()
174 ((condition "Condition")))
176 (define-specific-error (authorization-header-malformed
177 "AuthorizationHeaderMalformed") ()
181 (define-condition linked
()
185 (:report
(lambda (condition stream
)
186 (report-request-error condition stream
)
187 (format stream
"~&For more information, see:~% ~A"
188 (linked-url condition
)))))
191 (define-condition bucket-restrictions
(linked)
194 :url
"http://docs.aws.amazon.com/AmazonS3/latest/dev/BucketRestrictions.html"))
196 (define-specific-error (invalid-bucket-name "InvalidBucketName")
197 (bucket-restrictions)
200 (define-specific-error (bucket-exists "BucketAlreadyExists")
201 (bucket-restrictions)
204 (define-specific-error (too-many-buckets "TooManyBuckets")
205 (bucket-restrictions)
209 (define-specific-error (ambiguous-grant "AmbiguousGrantByEmailAddress") (linked)
212 :url
"http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUTacl.html"))
214 (define-specific-error (bucket-not-empty "BucketNotEmpty") (linked)
217 :url
"http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketDELETE.html"))
219 (define-specific-error (invalid-logging-target "InvalidTargetBucketForLogging")
222 (define-specific-error (key-too-long "KeyTooLong") (linked)
225 :url
"http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMetadata.html"))
227 (define-specific-error (request-time-skewed "RequestTimeTooSkewed") (linked)
230 :url
"http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html#RESTAuthenticationTimeStamp"))
232 (define-specific-error (operation-aborted "OperationAborted") () ())