summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBryan Ischo <bryan@ischo.com>2008-07-06 12:06:04 +0000
committerBryan Ischo <bryan@ischo.com>2008-07-06 12:06:04 +0000
commit07ce7e4123f0ec6b88e65269c6edd69e13b4935c (patch)
treecb7af33122a56bd257cc20e605c22971cb446aeb
parent4bd797254bb9c04218ffa304b048a600cb80cbdc (diff)
downloadceph-libs3-07ce7e4123f0ec6b88e65269c6edd69e13b4935c.tar.gz
* More work in progress. Better S3 error support, still needs some cleanup work.
-rw-r--r--inc/libs3.h4
-rw-r--r--inc/private.h90
-rw-r--r--src/request.c196
-rw-r--r--src/s3.c23
-rw-r--r--src/testsimplexml.c3
-rw-r--r--test/goodxml_01.xml2
6 files changed, 265 insertions, 53 deletions
diff --git a/inc/libs3.h b/inc/libs3.h
index 62f665a..e66d9e7 100644
--- a/inc/libs3.h
+++ b/inc/libs3.h
@@ -78,7 +78,9 @@
************************************************************************** **/
/**
- * S3Status is a status code as returned by a libs3 function.
+ * S3Status is a status code as returned by a libs3 function. These status
+ * codes identify the success or failure of the request to be sent to S3
+ * and the response to be read.
**/
typedef enum
{
diff --git a/inc/private.h b/inc/private.h
index d3c2ed0..fe21272 100644
--- a/inc/private.h
+++ b/inc/private.h
@@ -77,6 +77,33 @@ typedef enum
} HttpRequestType;
+// Simple XML callback.
+//
+// elementPath: is the full "path" of the element; i.e.
+// <foo><bar><baz>data</baz><bar><foo> would have 'data' in the element
+// foo/bar/baz.
+//
+// Return of anything other than S3StatusOK causes the calling
+// simplexml_add() function to immediately stop and return the status.
+typedef S3Status (SimpleXmlCallback)(const char *elementPath, const char *data,
+ int dataLen, void *callbackData);
+
+typedef struct SimpleXml
+{
+ void *xmlParser;
+
+ SimpleXmlCallback *callback;
+
+ void *callbackData;
+
+ char elementPath[512];
+
+ int elementPathLen;
+
+ S3Status status;
+} SimpleXml;
+
+
// This completely describes a request. A RequestParams is not required to be
// allocated from the heap and its lifetime is not assumed to extend beyond
// the lifetime of the function to which it has been passed.
@@ -192,8 +219,6 @@ typedef struct Request
// Response meta headers
S3MetaHeader responseMetaHeaders[MAX_META_HEADER_COUNT];
- // Callback stuff ---------------------------------------------------------
-
// Callback to make when headers are available
S3ResponseHeadersCallback *headersCallback;
@@ -203,6 +228,9 @@ typedef struct Request
// This is set to nonzero after the haders callback has been made
int headersCallbackMade;
+ // The HTTP response code that S3 sent back for this request
+ int httpResponseCode;
+
// This is the write callback that the user of the request wants to have
// called back when data is available.
size_t (*curlWriteCallback)(void *data, size_t s, size_t n, void *req);
@@ -214,12 +242,31 @@ typedef struct Request
// The callback to make when the response has been completely handled
S3ResponseCompleteCallback *completeCallback;
- // This will be 0 if S3 didn't send any XML error
- int receivedS3Error;
+ // This is nonzero if the error XML parser has been initialized
+ int errorXmlParserInitialized;
+
+ // This is the error XML parser
+ SimpleXml errorXmlParser;
// If S3 did send an XML error, this is the parsed form of it
S3Error s3Error;
+ // These are the buffers used to store the S3Error values
+ char s3ErrorCode[1024];
+ int s3ErrorCodeLen;
+
+ // These are the buffers used to store the S3Error values
+ char s3ErrorMessage[1024];
+ int s3ErrorMessageLen;
+
+ // These are the buffers used to store the S3Error values
+ char s3ErrorResource[1024];
+ int s3ErrorResourceLen;
+
+ // These are the buffers used to store the S3Error values
+ char s3ErrorFurtherDetails[1024];
+ int s3ErrorFurtherDetailsLen;
+
// The callbacks to make for the data payload of the response
union {
S3ListServiceCallback *listServiceCallback;
@@ -276,42 +323,13 @@ void request_finish(Request *request, S3Status status);
// Simple XML parsing
// ----------------------------------------------------------------------------
-// Simple XML callback.
-//
-// elementPath: is the full "path" of the element; i.e.
-// <foo><bar><baz>data</baz><bar><foo> would have 'data' in the element
-// foo/bar/baz.
-//
-// Return of anything other than S3StatusOK causes the calling
-// simplexml_add() function to immediately stop and return the status.
-//
-// element data meaning
-// ------- ---- -------
-// !0 !0 element data for element
-// !0 0 empty element
-typedef S3Status (SimpleXmlCallback)(const char *elementPath, const char *data,
- int dataLen, void *callbackData);
-
-typedef struct SimpleXml
-{
- void *xmlParser;
-
- SimpleXmlCallback *callback;
-
- void *callbackData;
-
- char elementPath[512];
-
- int elementPathLen;
-
- S3Status status;
-} SimpleXml;
-
-
S3Status simplexml_initialize(SimpleXml *simpleXml,
SimpleXmlCallback *callback, void *callbackData);
S3Status simplexml_add(SimpleXml *simpleXml, const char *data, int dataLen);
+void simplexml_deinitialize(SimpleXml *simpleXml);
+
+
#endif /* PRIVATE_H */
diff --git a/src/request.c b/src/request.c
index 228b641..e0c57e9 100644
--- a/src/request.c
+++ b/src/request.c
@@ -54,6 +54,10 @@ static S3Status request_headers_done(Request *request)
return S3StatusOK;
}
+ // Get the http response code
+ (void) curl_easy_getinfo
+ (request->curl, CURLINFO_RESPONSE_CODE, &(request->httpResponseCode));
+
// Now get the last modification time from curl, since it's easiest to
// let curl parse it
(void) curl_easy_getinfo(request->curl, CURLINFO_FILETIME,
@@ -204,21 +208,103 @@ static size_t curl_header_func(void *ptr, size_t size, size_t nmemb, void *data)
}
+static S3Status errorXmlCallback(const char *elementPath, const char *data,
+ int dataLen, void *callbackData)
+{
+ Request *request = (Request *) callbackData;
+
+#define APPEND_DATA(requestField) \
+ do { \
+ request-> requestField##Len += \
+ snprintf(&(request-> requestField \
+ [request-> requestField##Len]), \
+ sizeof(request-> requestField) - \
+ request-> requestField##Len - 1, "%.*s", \
+ dataLen, data); \
+ } while (0)
+
+#define RESET_FIELD(requestField, errorField) \
+ request->s3Error. errorField = request-> requestField;
+
+
+ if (!strcmp(elementPath, "Error")) {
+ // Ignore, this is the Error element itself, we only care about subs
+ }
+ else if (!strcmp(elementPath, "Error/Code")) {
+ APPEND_DATA(s3ErrorCode);
+ }
+ else if (!strcmp(elementPath, "Error/Message")) {
+ APPEND_DATA(s3ErrorMessage);
+ RESET_FIELD(s3ErrorMessage, message);
+ }
+ else if (!strcmp(elementPath, "Error/Resource")) {
+ APPEND_DATA(s3ErrorResource);
+ RESET_FIELD(s3ErrorResource, resource);
+ }
+ else if (!strcmp(elementPath, "Error/FurtherDetails")) {
+ APPEND_DATA(s3ErrorFurtherDetails);
+ RESET_FIELD(s3ErrorFurtherDetails, furtherDetails);
+ }
+ else {
+ if (strncmp(elementPath, "Error/", sizeof("Error/") - 1)) {
+ // If for some weird reason it's not within the Error element,
+ // ignore it
+ return S3StatusOK;
+ }
+ // OK, it's an unknown error element ... pass these back?
+ // xxx todo
+ }
+
+ return S3StatusOK;
+}
+
+
static size_t curl_write_func(void *ptr, size_t size, size_t nmemb, void *data)
{
Request *request = (Request *) data;
+ // Make sure that headers have been handled
if (request_headers_done(request) != S3StatusOK) {
return 0;
}
+ // On HTTP error, we expect to parse an HTTP error response
+ if ((request->httpResponseCode < 200) ||
+ (request->httpResponseCode > 299)) {
+ // If we haven't set up for reading an error yet, do so
+ if (!request->errorXmlParserInitialized) {
+ request->errorXmlParserInitialized = 1;
+ // Set up the simplexml parser
+ if (simplexml_initialize
+ (&(request->errorXmlParser),
+ &errorXmlCallback, request) != S3StatusOK) {
+ return 0;
+ }
+
+ // Set up the S3Error that we will be returning
+ request->s3ErrorCodeLen = 0;
+ request->s3Error.message = 0;
+ request->s3ErrorMessageLen = 0;
+ request->s3Error.resource = 0;
+ request->s3ErrorResourceLen = 0;
+ request->s3Error.furtherDetails = 0;
+ request->s3ErrorFurtherDetailsLen = 0;
+ }
+
+ // Now put the data into the xml parser
+ size_t len = (size * nmemb);
+ return ((simplexml_add(&(request->errorXmlParser),
+ (char *) ptr, len) == S3StatusOK) ? len : 0);
+ }
+
+ // If there was a callback registered, make it
if (request->curlWriteCallback) {
return (*(request->curlWriteCallback))(ptr, size, nmemb, data);
}
+ // Else, consider this an error - S3 has sent back data when it was not
+ // expected
else {
- // For requests that don't expect to get any data, they still might
- // get error data, and we handle that here.
- return (size * nmemb);
+ return 0;
}
}
@@ -906,6 +992,11 @@ static S3Status request_initialize(Request *request,
if (request->headers) {
curl_slist_free_all(request->headers);
}
+
+ // Deinitialize the error xml parser
+ if (request->errorXmlParserInitialized) {
+ simplexml_deinitialize(&(request->errorXmlParser));
+ }
}
else {
request->used = 1;
@@ -956,10 +1047,12 @@ static S3Status request_initialize(Request *request,
request->headersCallbackMade = 0;
+ request->httpResponseCode = 0;
+
request->completeCallback = handler->completeCallback;
- request->receivedS3Error = 0;
-
+ request->errorXmlParserInitialized = 0;
+
memcpy(&(request->u), &(params->u), sizeof(request->u));
return S3StatusOK;
@@ -1143,15 +1236,92 @@ S3Status request_perform(RequestParams *params, S3RequestContext *context)
void request_finish(Request *request, S3Status status)
{
request_headers_done(request);
-
- int httpResponseCode = 0;
- (void) curl_easy_getinfo
- (request->curl, CURLINFO_RESPONSE_CODE, &httpResponseCode);
-
+
+ // Convert the error status string into a code
+ if (request->errorXmlParserInitialized) {
+ if (request->s3ErrorCodeLen) {
+#define HANDLE_CODE(name) \
+ do { \
+ if (!strcmp(request->s3ErrorCode, #name)) { \
+ request->s3Error.code = S3ErrorCode##name; \
+ goto code_set; \
+ } \
+ } while (0)
+
+ HANDLE_CODE(AccessDenied);
+ HANDLE_CODE(AccountProblem);
+ HANDLE_CODE(AmbiguousGrantByEmailAddress);
+ HANDLE_CODE(BadDigest);
+ HANDLE_CODE(BucketAlreadyExists);
+ HANDLE_CODE(BucketAlreadyOwnedByYou);
+ HANDLE_CODE(BucketNotEmpty);
+ HANDLE_CODE(CredentialsNotSupported);
+ HANDLE_CODE(CrossLocationLoggingProhibited);
+ HANDLE_CODE(EntityTooSmall);
+ HANDLE_CODE(EntityTooLarge);
+ HANDLE_CODE(ExpiredToken);
+ HANDLE_CODE(IncompleteBody);
+ HANDLE_CODE(IncorrectNumberOfFilesInPostRequest);
+ HANDLE_CODE(InlineDataTooLarge);
+ HANDLE_CODE(InternalError);
+ HANDLE_CODE(InvalidAccessKeyId);
+ HANDLE_CODE(InvalidAddressingHeader);
+ HANDLE_CODE(InvalidArgument);
+ HANDLE_CODE(InvalidBucketName);
+ HANDLE_CODE(InvalidDigest);
+ HANDLE_CODE(InvalidLocationConstraint);
+ HANDLE_CODE(InvalidPayer);
+ HANDLE_CODE(InvalidPolicyDocument);
+ HANDLE_CODE(InvalidRange);
+ HANDLE_CODE(InvalidSecurity);
+ HANDLE_CODE(InvalidSOAPRequest);
+ HANDLE_CODE(InvalidStorageClass);
+ HANDLE_CODE(InvalidTargetBucketForLogging);
+ HANDLE_CODE(InvalidToken);
+ HANDLE_CODE(InvalidURI);
+ HANDLE_CODE(KeyTooLong);
+ HANDLE_CODE(MalformedACLError);
+ HANDLE_CODE(MalformedXML);
+ HANDLE_CODE(MaxMessageLengthExceeded);
+ HANDLE_CODE(MaxPostPreDataLengthExceededError);
+ HANDLE_CODE(MetadataTooLarge);
+ HANDLE_CODE(MethodNotAllowed);
+ HANDLE_CODE(MissingAttachment);
+ HANDLE_CODE(MissingContentLength);
+ HANDLE_CODE(MissingSecurityElement);
+ HANDLE_CODE(MissingSecurityHeader);
+ HANDLE_CODE(NoLoggingStatusForKey);
+ HANDLE_CODE(NoSuchBucket);
+ HANDLE_CODE(NoSuchKey);
+ HANDLE_CODE(NotImplemented);
+ HANDLE_CODE(NotSignedUp);
+ HANDLE_CODE(OperationAborted);
+ HANDLE_CODE(PermanentRedirect);
+ HANDLE_CODE(PreconditionFailed);
+ HANDLE_CODE(Redirect);
+ HANDLE_CODE(RequestIsNotMultiPartContent);
+ HANDLE_CODE(RequestTimeout);
+ HANDLE_CODE(RequestTimeTooSkewed);
+ HANDLE_CODE(RequestTorrentOfBucketError);
+ HANDLE_CODE(SignatureDoesNotMatch);
+ HANDLE_CODE(SlowDown);
+ HANDLE_CODE(TemporaryRedirect);
+ HANDLE_CODE(TokenRefreshRequired);
+ HANDLE_CODE(TooManyBuckets);
+ HANDLE_CODE(UnexpectedContent);
+ HANDLE_CODE(UnresolvableGrantByEmailAddress);
+ HANDLE_CODE(UserKeyMustBeSpecified);
+ }
+
+ request->s3Error.code = 0;
+ }
+
+ code_set:
+
(*(request->completeCallback))
- (status, httpResponseCode,
- request->receivedS3Error ? &(request->s3Error) : 0,
- request->callbackData);
+ (status, request->httpResponseCode,
+ request->errorXmlParserInitialized ? &(request->s3Error) : 0,
+ request->callbackData);
request_release(request);
}
diff --git a/src/s3.c b/src/s3.c
index aeba16b..eb3b8a8 100644
--- a/src/s3.c
+++ b/src/s3.c
@@ -323,8 +323,27 @@ static void test_bucket(int argc, char **argv, int optind)
printf("Bucket '%s' does not exist.\n", bucketName);
}
else if (httpResponseCodeG == 403) {
- // bucket exists, but no access
- printf("Bucket '%s' exists, but is not accessible.\n", bucketName);
+ if (errorG && (errorG->code == S3ErrorCodeAccessDenied)) {
+ // bucket exists, but no access
+ printf("Bucket '%s' exists, but is not accessible.\n", bucketName);
+ }
+ else {
+ fprintf(stderr, "ERROR: S3 returned error:\n");
+ fprintf(stderr, " HTTP Response Code: %d\n", httpResponseCodeG);
+ if (errorG) {
+ fprintf(stderr, " S3 Error Code: %d\n", errorG->code);
+ if (errorG->message) {
+ fprintf(stderr, " S3 Message: %s\n", errorG->message);
+ }
+ if (errorG->resource) {
+ fprintf(stderr, " S3 Resource: %s\n", errorG->resource);
+ }
+ if (errorG->furtherDetails) {
+ fprintf(stderr, " S3 Resource: %s\n",
+ errorG->furtherDetails);
+ }
+ }
+ }
}
else {
fprintf(stderr, "ERROR: S3 returned error: %d\n",
diff --git a/src/testsimplexml.c b/src/testsimplexml.c
index d50674e..3b4d86b 100644
--- a/src/testsimplexml.c
+++ b/src/testsimplexml.c
@@ -75,11 +75,14 @@ int main(int argc, char **argv)
status = simplexml_add(&simpleXml, buf, amt);
if (status != S3StatusOK) {
fprintf(stderr, "ERROR: Parse failure: %d\n", status);
+ simplexml_deinitialize(&simpleXml);
return -1;
}
buf += amt, amt_read -= amt;
}
}
+ simplexml_deinitialize(&simpleXml);
+
return 0;
}
diff --git a/test/goodxml_01.xml b/test/goodxml_01.xml
index 3cae4b4..687214e 100644
--- a/test/goodxml_01.xml
+++ b/test/goodxml_01.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>NoSuchKey</Code>
- <Message> The resource you requested does not exist &amp; so there </Message>
+ <Message> The resource <![CDATA[<now> & then]]> you requested does not exist &amp; so there </Message>
<Resource>/mybucket/myfoto.jpg</Resource>
<RequestId>4442587FB7D0A2F9</RequestId>
</Error>