Allow sync data to arrive via the chunked transfer encoding.
* Fixes #2216885
* The bug is that the sync adapters weren't set up to handle chunked
encoding, primarily because 1) I hadn't seen any servers use it, and
2) when we changed from HttpUrlConnection to HttpClient, support for
chunked wasn't added (HttpUrlConnection didn't support it)
* The fix for xml data is trivial, since the Content-Length returned in
the chunked case (-1) was being disallowed, but works perfectly well
with HttpClient.
* The fix for attachments is less trivial, but still straightforward.
* With this change, we are no longer dependent on receiving content-length,
which is highly desirable
Change-Id: Ie3bd6af0cf68f3afa190711d96b1dbd2e6341f79
diff --git a/src/com/android/exchange/EasSyncService.java b/src/com/android/exchange/EasSyncService.java
index 81fd4ac..f15c181 100644
--- a/src/com/android/exchange/EasSyncService.java
+++ b/src/com/android/exchange/EasSyncService.java
@@ -314,19 +314,44 @@
destDir.mkdirs();
}
FileOutputStream os = new FileOutputStream(f);
- if (len > 0) {
+ // len > 0 means that Content-Length was set in the headers
+ // len < 0 means "chunked" transfer-encoding
+ if (len != 0) {
try {
mPendingPartRequest = req;
byte[] bytes = new byte[CHUNK_SIZE];
int length = len;
- while (len > 0) {
- int n = (len > CHUNK_SIZE ? CHUNK_SIZE : len);
- int read = is.read(bytes, 0, n);
+ // Loop terminates 1) when EOF is reached or 2) if an IOException occurs
+ // One of these is guaranteed to occur
+ int totalRead = 0;
+ userLog("Attachment content-length: ", len);
+ while (true) {
+ int read = is.read(bytes, 0, CHUNK_SIZE);
+
+ // read < 0 means that EOF was reached
+ if (read < 0) {
+ userLog("Attachment load reached EOF, totalRead: ", totalRead);
+ break;
+ }
+
+ // Keep track of how much we've read for progress callback
+ totalRead += read;
+
+ // Write these bytes out
os.write(bytes, 0, read);
- len -= read;
- int pct = ((length - len) * 100 / length);
- doProgressCallback(msg.mId, att.mId, pct);
- }
+
+ // We can't report percentages if this is chunked; by definition, the
+ // length of incoming data is unknown
+ if (length > 0) {
+ // Belt and suspenders check to prevent runaway reading
+ if (totalRead > length) {
+ errorLog("totalRead is greater than attachment length?");
+ break;
+ }
+ int pct = (totalRead * 100 / length);
+ doProgressCallback(msg.mId, att.mId, pct);
+ }
+ }
} finally {
mPendingPartRequest = null;
}
@@ -562,7 +587,7 @@
if (code == HttpStatus.SC_OK) {
HttpEntity entity = resp.getEntity();
int len = (int)entity.getContentLength();
- if (len > 0) {
+ if (len != 0) {
InputStream is = entity.getContent();
// Returns true if we need to sync again
if (new FolderSyncParser(is, new AccountSyncAdapter(mMailbox, this))
@@ -747,7 +772,7 @@
HttpEntity e = res.getEntity();
int len = (int)e.getContentLength();
InputStream is = res.getEntity().getContent();
- if (len > 0) {
+ if (len != 0) {
int pingResult = parsePingResult(is, mContentResolver, pingErrorMap);
// If our ping completed (status = 1), and we weren't forced and we're
// not at the maximum, try increasing timeout by two minutes
@@ -1019,7 +1044,7 @@
HttpResponse resp = sendHttpClientPost("Sync", s.toByteArray());
int code = resp.getStatusLine().getStatusCode();
if (code == HttpStatus.SC_OK) {
- InputStream is = resp.getEntity().getContent();
+ InputStream is = resp.getEntity().getContent();
if (is != null) {
moreAvailable = target.parse(is);
target.cleanup();