Permalink
Browse files

Make direct PUT to attachment URL work with streamed request body

This case was accidentally not fixed back in commit 7f24896.
Fixes #198.
  • Loading branch information...
1 parent 8f6ba32 commit d752da8a0869e2dbb0606cf7054e2ee2c7da0ec0 @snej snej committed Dec 12, 2012
Showing with 58 additions and 18 deletions.
  1. +29 −7 Source/TDRouter+Handlers.m
  2. +1 −1 Source/TD_Database+Attachments.h
  3. +10 −7 Source/TD_Database+Attachments.m
  4. +18 −3 Source/TD_Database_Tests.m
@@ -854,7 +854,10 @@ - (TDStatus) do_DELETE: (TD_Database*)db docID: (NSString*)docID {
}
-- (TDStatus) updateAttachment: (NSString*)attachment docID: (NSString*)docID body: (NSData*)body {
+- (TDStatus) updateAttachment: (NSString*)attachment
+ docID: (NSString*)docID
+ body: (TDBlobStoreWriter*)body
+{
TDStatus status;
TD_Revision* rev = [_db updateAttachment: attachment
body: body
@@ -874,16 +877,35 @@ - (TDStatus) updateAttachment: (NSString*)attachment docID: (NSString*)docID bod
- (TDStatus) do_PUT: (TD_Database*)db docID: (NSString*)docID attachment: (NSString*)attachment {
- return [self updateAttachment: attachment
- docID: docID
- body: (_request.HTTPBody ?: [NSData data])];
+ TDBlobStoreWriter* blob = db.attachmentWriter;
+ NSInputStream* bodyStream = _request.HTTPBodyStream;
+ if (bodyStream) {
+ // OPT: Should read this asynchronously
+ NSMutableData* buffer = [NSMutableData dataWithLength: 32768];
+ NSInteger bytesRead;
+ do {
+ bytesRead = [bodyStream read: buffer.mutableBytes maxLength: buffer.length];
+ if (bytesRead > 0) {
+ [blob appendData: [NSData dataWithBytesNoCopy: buffer.mutableBytes
+ length: bytesRead freeWhenDone: NO]];
+ }
+ } while (bytesRead > 0);
+ if (bytesRead < 0)
+ return kTDStatusBadAttachment;
+
+ } else {
+ NSData* body = _request.HTTPBody;
+ if (body)
+ [blob appendData: body];
+ }
+ [blob finish];
+
+ return [self updateAttachment: attachment docID: docID body: blob];
}
- (TDStatus) do_DELETE: (TD_Database*)db docID: (NSString*)docID attachment: (NSString*)attachment {
- return [self updateAttachment: attachment
- docID: docID
- body: nil];
+ return [self updateAttachment: attachment docID: docID body: nil];
}
@@ -68,7 +68,7 @@ typedef enum {
/** Updates or deletes an attachment, creating a new document revision in the process.
Used by the PUT / DELETE methods called on attachment URLs. */
- (TD_Revision*) updateAttachment: (NSString*)filename
- body: (NSData*)body
+ body: (TDBlobStoreWriter*)body
type: (NSString*)contentType
encoding: (TDAttachmentEncoding)encoding
ofDocID: (NSString*)docID
@@ -69,6 +69,13 @@ - (void) rememberPendingKey: (TDBlobKey)key forDigest: (NSString*)digest {
}
+- (void) rememberAttachmentWriter: (TDBlobStoreWriter*)writer forDigest:(NSString*)digest {
+ if (!_pendingAttachmentsByDigest)
+ _pendingAttachmentsByDigest = [[NSMutableDictionary alloc] init];
+ _pendingAttachmentsByDigest[digest] = writer;
+}
+
+
// This is ONLY FOR TESTS (see TDMultipartDownloader.m)
#if DEBUG
- (id) attachmentWriterForAttachment: (NSDictionary*)attachment {
@@ -561,7 +568,7 @@ - (TDMultipartWriter*) multipartWriterForRevision: (TD_Revision*)rev
- (TD_Revision*) updateAttachment: (NSString*)filename
- body: (NSData*)body
+ body: (TDBlobStoreWriter*)body
type: (NSString*)contentType
encoding: (TDAttachmentEncoding)encoding
ofDocID: (NSString*)docID
@@ -593,14 +600,10 @@ - (TD_Revision*) updateAttachment: (NSString*)filename
if (!attachments)
attachments = $mdict();
if (body) {
- TDBlobKey key;
- if (![self storeBlob: body creatingKey: &key]) {
- *outStatus = kTDStatusAttachmentError;
- return nil;
- }
+ TDBlobKey key = body.blobKey;
NSString* digest = [@"sha1-" stringByAppendingString: [TDBase64 encode: &key
length: sizeof(key)]];
- [self rememberPendingKey: key forDigest: digest];
+ [self rememberAttachmentWriter: body forDigest: digest];
NSString* encodingName = (encoding == kTDAttachmentEncodingGZIP) ? @"gzip" : nil;
attachments[filename] = $dict({@"digest", digest},
{@"length", @(body.length)},
@@ -574,6 +574,14 @@ static void insertAttachment(TD_Database* db, NSData* blob,
}
+static TDBlobStoreWriter* blobForData(TD_Database* db, NSData* data) {
+ TDBlobStoreWriter* blob = db.attachmentWriter;
+ [blob appendData: data];
+ [blob finish];
+ return blob;
+}
+
+
TestCase(TD_Database_PutAttachment) {
RequireTestCase(TD_Database_Attachments);
// Start with a fresh database in /tmp:
@@ -607,17 +615,20 @@ static void insertAttachment(TD_Database* db, NSData* blob,
// Update the attachment directly:
NSData* attachv2 = [@"Replaced body of attach" dataUsingEncoding: NSUTF8StringEncoding];
- [db updateAttachment: @"attach" body: attachv2 type: @"application/foo"
+ [db updateAttachment: @"attach" body: blobForData(db, attachv2)
+ type: @"application/foo"
encoding: kTDAttachmentEncodingNone
ofDocID: rev1.docID revID: nil
status: &status];
CAssertEq(status, kTDStatusConflict);
- [db updateAttachment: @"attach" body: attachv2 type: @"application/foo"
+ [db updateAttachment: @"attach" body: blobForData(db, attachv2)
+ type: @"application/foo"
encoding: kTDAttachmentEncodingNone
ofDocID: rev1.docID revID: @"1-bogus"
status: &status];
CAssertEq(status, kTDStatusConflict);
- TD_Revision* rev2 = [db updateAttachment: @"attach" body: attachv2 type: @"application/foo"
+ TD_Revision* rev2 = [db updateAttachment: @"attach" body: blobForData(db, attachv2)
+ type: @"application/foo"
encoding: kTDAttachmentEncodingNone
ofDocID: rev1.docID revID: rev1.revID
status: &status];
@@ -633,6 +644,10 @@ static void insertAttachment(TD_Database* db, NSData* blob,
{@"length", @(23)},
{@"stub", $true},
{@"revpos", @2})}));
+
+ NSData* gotAttach = [db getAttachmentForSequence: gotRev2.sequence named: @"attach"
+ type: NULL encoding: NULL status: &status];
+ CAssertEqual(gotAttach, attachv2);
// Delete the attachment:
[db updateAttachment: @"nosuchattach" body: nil type: nil

0 comments on commit d752da8

Please sign in to comment.