diff options
author | Bryan Ischo <bryan@ischo.com> | 2008-07-06 12:06:04 +0000 |
---|---|---|
committer | Bryan Ischo <bryan@ischo.com> | 2008-07-06 12:06:04 +0000 |
commit | 07ce7e4123f0ec6b88e65269c6edd69e13b4935c (patch) | |
tree | cb7af33122a56bd257cc20e605c22971cb446aeb | |
parent | 4bd797254bb9c04218ffa304b048a600cb80cbdc (diff) | |
download | ceph-libs3-07ce7e4123f0ec6b88e65269c6edd69e13b4935c.tar.gz |
* More work in progress. Better S3 error support, still needs some cleanup work.
-rw-r--r-- | inc/libs3.h | 4 | ||||
-rw-r--r-- | inc/private.h | 90 | ||||
-rw-r--r-- | src/request.c | 196 | ||||
-rw-r--r-- | src/s3.c | 23 | ||||
-rw-r--r-- | src/testsimplexml.c | 3 | ||||
-rw-r--r-- | test/goodxml_01.xml | 2 |
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); } @@ -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 & so there </Message> + <Message> The resource <![CDATA[<now> & then]]> you requested does not exist & so there </Message> <Resource>/mybucket/myfoto.jpg</Resource> <RequestId>4442587FB7D0A2F9</RequestId> </Error> |