diff --git a/indra/viewer_components/updater/llupdatedownloader.cpp b/indra/viewer_components/updater/llupdatedownloader.cpp
index efb55ab83abbcb82dae5a1e595c4154dd89dd1e0..ca1d2d25de93153f939d28acef44522bae2cdb31 100644
--- a/indra/viewer_components/updater/llupdatedownloader.cpp
+++ b/indra/viewer_components/updater/llupdatedownloader.cpp
@@ -46,11 +46,12 @@ class LLUpdateDownloader::Implementation:
 	void cancel(void);
 	void download(LLURI const & uri, std::string const & hash);
 	bool isDownloading(void);
-	void onHeader(void * header, size_t size);
-	void onBody(void * header, size_t size);
+	size_t onHeader(void * header, size_t size);
+	size_t onBody(void * header, size_t size);
 	void resume(void);
 	
 private:
+	bool mCancelled;
 	LLUpdateDownloader::Client & mClient;
 	CURL * mCurl;
 	LLSD mDownloadData;
@@ -137,22 +138,21 @@ namespace {
 	size_t write_function(void * data, size_t blockSize, size_t blocks, void * downloader)
 	{
 		size_t bytes = blockSize * blocks;
-		reinterpret_cast<LLUpdateDownloader::Implementation *>(downloader)->onBody(data, bytes);
-		return bytes;
+		return reinterpret_cast<LLUpdateDownloader::Implementation *>(downloader)->onBody(data, bytes);
 	}
 
 
 	size_t header_function(void * data, size_t blockSize, size_t blocks, void * downloader)
 	{
 		size_t bytes = blockSize * blocks;
-		reinterpret_cast<LLUpdateDownloader::Implementation *>(downloader)->onHeader(data, bytes);
-		return bytes;
+		return reinterpret_cast<LLUpdateDownloader::Implementation *>(downloader)->onHeader(data, bytes);
 	}
 }
 
 
 LLUpdateDownloader::Implementation::Implementation(LLUpdateDownloader::Client & client):
 	LLThread("LLUpdateDownloader"),
+	mCancelled(false),
 	mClient(client),
 	mCurl(0),
 	mDownloadRecordPath(LLUpdateDownloader::downloadMarkerPath())
@@ -170,7 +170,7 @@ LLUpdateDownloader::Implementation::~Implementation()
 
 void LLUpdateDownloader::Implementation::cancel(void)
 {
-	llassert(!"not implemented");
+	mCancelled = true;
 }
 	
 
@@ -230,12 +230,12 @@ void LLUpdateDownloader::Implementation::resume(void)
 }
 
 
-void LLUpdateDownloader::Implementation::onHeader(void * buffer, size_t size)
+size_t LLUpdateDownloader::Implementation::onHeader(void * buffer, size_t size)
 {
 	char const * headerPtr = reinterpret_cast<const char *> (buffer);
 	std::string header(headerPtr, headerPtr + size);
 	size_t colonPosition = header.find(':');
-	if(colonPosition == std::string::npos) return; // HTML response; ignore.
+	if(colonPosition == std::string::npos) return size; // HTML response; ignore.
 	
 	if(header.substr(0, colonPosition) == "Content-Length") {
 		try {
@@ -255,20 +255,25 @@ void LLUpdateDownloader::Implementation::onHeader(void * buffer, size_t size)
 	} else {
 		; // No op.
 	}
+	
+	return size;
 }
 
 
-void LLUpdateDownloader::Implementation::onBody(void * buffer, size_t size)
+size_t LLUpdateDownloader::Implementation::onBody(void * buffer, size_t size)
 {
+	if(mCancelled) return 0; // Forces a write error which will halt curl thread.
+	
 	mDownloadStream.write(reinterpret_cast<const char *>(buffer), size);
+	return size;
 }
 
 
 void LLUpdateDownloader::Implementation::run(void)
 {
 	CURLcode code = curl_easy_perform(mCurl);
-	LLFile::remove(mDownloadRecordPath);
 	if(code == CURLE_OK) {
+		LLFile::remove(mDownloadRecordPath);
 		if(validateDownload()) {
 			LL_INFOS("UpdateDownload") << "download successful" << LL_ENDL;
 			mClient.downloadComplete(mDownloadData);
@@ -278,9 +283,13 @@ void LLUpdateDownloader::Implementation::run(void)
 			if(filePath.size() != 0) LLFile::remove(filePath);
 			mClient.downloadError("failed hash check");
 		}
+	} else if(mCancelled && (code == CURLE_WRITE_ERROR)) {
+		LL_INFOS("UpdateDownload") << "download canceled by user" << LL_ENDL;
+		// Do not call back client.
 	} else {
 		LL_WARNS("UpdateDownload") << "download failed with error '" << 
 			curl_easy_strerror(code) << "'" << LL_ENDL;
+		LLFile::remove(mDownloadRecordPath);
 		mClient.downloadError("curl error");
 	}
 }
@@ -378,6 +387,10 @@ bool LLUpdateDownloader::Implementation::validateDownload(void)
 		LL_INFOS("UpdateDownload") << "checking hash..." << LL_ENDL;
 		char digest[33];
 		LLMD5(fileStream).hex_digest(digest);
+		if(hash != digest) {
+			LL_WARNS("UpdateDownload") << "download hash mismatch; expeted " << hash <<
+				" but download is " << digest << LL_ENDL;
+		}
 		return hash == digest;
 	} else {
 		return true; // No hash check provided.
diff --git a/indra/viewer_components/updater/llupdatedownloader.h b/indra/viewer_components/updater/llupdatedownloader.h
index dc8ecc378adc872103e80f9d463002cf6d50ad91..491a638f9a9f7c8b0b9d031e0e20c177e9aa01da 100644
--- a/indra/viewer_components/updater/llupdatedownloader.h
+++ b/indra/viewer_components/updater/llupdatedownloader.h
@@ -47,7 +47,8 @@ class LLUpdateDownloader
 	
 	LLUpdateDownloader(Client & client);
 	
-	// Cancel any in progress download; a no op if none is in progress.
+	// Cancel any in progress download; a no op if none is in progress.  The
+	// client will not receive a complete or error callback.
 	void cancel(void);
 	
 	// Start a new download.