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 specialized-initialize ((response amazon-error
) (source null
))
62 (setf (code response
) "InternalError"
63 (message response
) nil
64 (error-data response
) nil
))
66 (defmethod print-object ((response amazon-error
) stream
)
67 (print-unreadable-object (response stream
:type t
)
68 (prin1 (code response
) stream
)))
70 ;;; Further specializing error messages/conditions
72 (defun report-request-error (condition stream
)
73 (format stream
"~A~@[: ~A~]"
74 (code (request-error-response condition
))
75 (message (request-error-response condition
))))
77 (define-condition request-error
(error)
80 :reader request-error-request
)
83 :reader request-error-response
)
86 :reader request-error-data
))
87 (:report report-request-error
))
89 (defparameter *specific-errors
* (make-hash-table :test
'equalp
))
91 (defun specific-error (amazon-code)
92 (gethash amazon-code
*specific-errors
* 'request-error
))
94 (defgeneric signal-specific-error
(response condition-name
)
95 (:method
(response (condition-name t
))
97 :request
(request response
)
99 :data
(error-data response
))))
101 (defgeneric maybe-signal-error
(response)
102 (:method
((response t
))
104 (:method
((response amazon-error
))
105 (signal-specific-error response
(specific-error (code response
)))))
107 (eval-when (:compile-toplevel
:load-toplevel
:execute
)
108 (defun error-reader-name (suffix)
109 (intern (concatenate 'string
(symbol-name 'request-error
)
111 (symbol-name suffix
))
114 (defmacro define-specific-error
((condition-name code
)
117 (labels ((slot-name (slot)
122 (keywordify (slot-name slot
)))
123 (slot-definition (slot)
125 :initarg
,(slot-keyword slot
)
126 :reader
,(error-reader-name (slot-name slot
))))
127 (slot-initializer (slot)
128 (list (slot-keyword slot
)
129 `(error-data-value ,(slot-code slot
) response
))))
131 (setf (gethash ,code
*specific-errors
*) ',condition-name
)
132 (define-condition ,condition-name
(,@superclasses request-error
)
133 ,(mapcar #'slot-definition slots
)
135 (defmethod signal-specific-error ((response amazon-error
)
136 (condition-name (eql ',condition-name
)))
137 (error ',condition-name
138 :request
(request response
)
140 :data
(error-data response
)
141 ,@(mapcan #'slot-initializer slots
))))))
144 ;;; The specific errors
146 (define-specific-error (internal-error "InternalError") () ())
148 (define-specific-error (slow-down "SlowDown") () ())
150 (define-specific-error (no-such-bucket "NoSuchBucket") ()
151 ((bucket-name "BucketName")))
153 (define-specific-error (no-such-key "NoSuchKey") ()
156 (define-specific-error (access-denied "AccessDenied") () ())
158 (define-specific-error (malformed-xml "MalformedXML") () ())
160 (define-condition redirect-error
(error) ())
162 (define-specific-error (permanent-redirect "PermanentRedirect") (redirect-error)
163 ((endpoint "Endpoint")))
165 (define-specific-error (temporary-redirect "TemporaryRedirect") (redirect-error)
166 ((endpoint "Endpoint")))
168 (define-specific-error (signature-mismatch "SignatureDoesNotMatch") ()
169 ((string-to-sign "StringToSign")
170 (canonical-request "CanonicalRequest"))
171 (:report
(lambda (condition stream
)
172 (report-request-error condition stream
)
173 (format stream
"You signed: ~S~%Amazon signed: ~S~%and~%~S"
174 (signed-string (request-error-request condition
))
175 (request-error-string-to-sign condition
)
176 (request-error-canonical-request condition
)))))
178 (define-specific-error (precondition-failed "PreconditionFailed") ()
179 ((condition "Condition")))
181 (define-specific-error (authorization-header-malformed
182 "AuthorizationHeaderMalformed") ()
186 (define-condition linked
()
190 (:report
(lambda (condition stream
)
191 (report-request-error condition stream
)
192 (format stream
"~&For more information, see:~% ~A"
193 (linked-url condition
)))))
196 (define-condition bucket-restrictions
(linked)
199 :url
"http://docs.aws.amazon.com/AmazonS3/latest/dev/BucketRestrictions.html"))
201 (define-specific-error (invalid-bucket-name "InvalidBucketName")
202 (bucket-restrictions)
205 (define-specific-error (bucket-exists "BucketAlreadyExists")
206 (bucket-restrictions)
209 (define-specific-error (too-many-buckets "TooManyBuckets")
210 (bucket-restrictions)
214 (define-specific-error (ambiguous-grant "AmbiguousGrantByEmailAddress") (linked)
217 :url
"http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUTacl.html"))
219 (define-specific-error (bucket-not-empty "BucketNotEmpty") (linked)
222 :url
"http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketDELETE.html"))
224 (define-specific-error (invalid-logging-target "InvalidTargetBucketForLogging")
227 (define-specific-error (key-too-long "KeyTooLong") (linked)
230 :url
"http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMetadata.html"))
232 (define-specific-error (request-time-skewed "RequestTimeTooSkewed") (linked)
235 :url
"http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html#RESTAuthenticationTimeStamp"))
237 (define-specific-error (operation-aborted "OperationAborted") () ())