WriteStumbleOnThread::Partition WriteStumbleOnThread::GetWritePosition() { MOZ_ASSERT(!NS_IsMainThread()); nsCOMPtr<nsIFile> tmpFile; nsresult rv = nsDumpUtils::OpenTempFile(kOutputFileNameInProgress, getter_AddRefs(tmpFile), kOutputDirName, nsDumpUtils::CREATE); if (NS_WARN_IF(NS_FAILED(rv))) { STUMBLER_ERR("Open a file for stumble failed"); return Partition::Unknown; } int64_t fileSize = 0; rv = tmpFile->GetFileSize(&fileSize); if (NS_WARN_IF(NS_FAILED(rv))) { STUMBLER_ERR("GetFileSize failed"); return Partition::Unknown; } if (fileSize == 0) { return Partition::Begining; } else if (fileSize >= MAXFILESIZE_KB) { return Partition::End; } else { return Partition::Middle; } }
void WriteStumbleOnThread::Upload() { MOZ_ASSERT(!NS_IsMainThread()); bool b = sIsUploading.exchange(true); if (b) { return; } time_t seconds = time(0); int day = seconds / (60 * 60 * 24); if (sUploadFreqGuard.daySinceEpoch < day) { sUploadFreqGuard.daySinceEpoch = day; sUploadFreqGuard.attempts = 0; } sUploadFreqGuard.attempts++; if (sUploadFreqGuard.attempts > MAX_UPLOAD_ATTEMPTS) { STUMBLER_ERR("Too many upload attempts today"); return; } nsCOMPtr<nsIFile> tmpFile; nsresult rv = nsDumpUtils::OpenTempFile(kOutputFileNameCompleted, getter_AddRefs(tmpFile), kOutputDirName, nsDumpUtils::CREATE); int64_t fileSize; rv = tmpFile->GetFileSize(&fileSize); if (NS_WARN_IF(NS_FAILED(rv))) { STUMBLER_ERR("GetFileSize failed"); sIsUploading = false; return; } STUMBLER_LOG("size : %lld", fileSize); if (fileSize <= 0) { sIsUploading = false; return; } // prepare json into nsIInputStream nsCOMPtr<nsIInputStream> inStream; rv = NS_NewLocalFileInputStream(getter_AddRefs(inStream), tmpFile, -1, -1, nsIFileInputStream::DEFER_OPEN); NS_ENSURE_TRUE_VOID(inStream); nsAutoCString bufStr; rv = NS_ReadInputStreamToString(inStream, bufStr, fileSize); NS_ENSURE_SUCCESS_VOID(rv); nsCOMPtr<nsIRunnable> uploader = new UploadStumbleRunnable(bufStr); NS_DispatchToMainThread(uploader); }
NS_IMETHODIMP UploadEventListener::HandleEvent(nsIDOMEvent* aEvent) { nsString type; if (NS_FAILED(aEvent->GetType(type))) { STUMBLER_ERR("Failed to get event type"); WriteStumbleOnThread::UploadEnded(false); return NS_ERROR_FAILURE; } if (type.EqualsLiteral("load")) { STUMBLER_DBG("Got load Event\n"); } else if (type.EqualsLiteral("error") && mXHR) { STUMBLER_ERR("Upload Error"); } else { STUMBLER_DBG("Receive %s Event", NS_ConvertUTF16toUTF8(type).get()); } uint32_t statusCode = 0; bool doDelete = false; if (!mXHR) { return NS_OK; } nsresult rv = mXHR->GetStatus(&statusCode); if (NS_SUCCEEDED(rv)) { STUMBLER_DBG("statuscode %d \n", statusCode); } if (200 == statusCode || 400 == statusCode) { doDelete = true; } WriteStumbleOnThread::UploadEnded(doDelete); nsCOMPtr<EventTarget> target(do_QueryInterface(mXHR)); const char* const sEventStrings[] = { // nsIXMLHttpRequestEventTarget event types "abort", "error", "load", "timeout" }; for (uint32_t index = 0; index < MOZ_ARRAY_LENGTH(sEventStrings); index++) { nsAutoString eventType = NS_ConvertASCIItoUTF16(sEventStrings[index]); rv = target->RemoveEventListener(eventType, this, false); } mXHR = nullptr; return NS_OK; }
NS_IMETHODIMP WriteStumbleOnThread::Run() { MOZ_ASSERT(!NS_IsMainThread()); bool b = sIsAlreadyRunning.exchange(true); if (b) { return NS_OK; } STUMBLER_DBG("In WriteStumbleOnThread\n"); UploadFileStatus status = GetUploadFileStatus(); if (UploadFileStatus::NoFile != status) { if (UploadFileStatus::ExistsAndReadyToUpload == status) { Upload(); } } else { Partition partition = GetWritePosition(); if (partition == Partition::Unknown) { STUMBLER_ERR("GetWritePosition failed, skip once"); } else { WriteJSON(partition); } } sIsAlreadyRunning = false; return NS_OK; }
/* If the upload file exists, then check if it is one day old. • if it is a day old -> ExistsAndReadyToUpload • if it is less than the current day old -> Exists • otherwise -> NoFile The Exists case means that the upload and the stumbling is rate limited per-day to the size of the one file. */ WriteStumbleOnThread::UploadFileStatus WriteStumbleOnThread::GetUploadFileStatus() { nsCOMPtr<nsIFile> tmpFile; nsresult rv = nsDumpUtils::OpenTempFile(kOutputFileNameCompleted, getter_AddRefs(tmpFile), kOutputDirName, nsDumpUtils::CREATE); int64_t fileSize; rv = tmpFile->GetFileSize(&fileSize); if (NS_WARN_IF(NS_FAILED(rv))) { STUMBLER_ERR("GetFileSize failed"); return UploadFileStatus::NoFile; } if (fileSize <= 0) { tmpFile->Remove(true); return UploadFileStatus::NoFile; } PRTime lastModifiedTime; tmpFile->GetLastModifiedTime(&lastModifiedTime); if ((PR_Now() / PR_USEC_PER_MSEC) - lastModifiedTime >= ONEDAY_IN_MSEC) { return UploadFileStatus::ExistsAndReadyToUpload; } return UploadFileStatus::Exists; }
void WriteStumbleOnThread::WriteJSON(Partition aPart) { MOZ_ASSERT(!NS_IsMainThread()); nsCOMPtr<nsIFile> tmpFile; nsresult rv; rv = nsDumpUtils::OpenTempFile(kOutputFileNameInProgress, getter_AddRefs(tmpFile), kOutputDirName, nsDumpUtils::CREATE); if (NS_WARN_IF(NS_FAILED(rv))) { STUMBLER_ERR("Open a file for stumble failed"); return; } nsRefPtr<nsGZFileWriter> gzWriter = new nsGZFileWriter(nsGZFileWriter::Append); rv = gzWriter->Init(tmpFile); if (NS_WARN_IF(NS_FAILED(rv))) { STUMBLER_ERR("gzWriter init failed"); return; } /* The json format is like below. {items:[ {item}, {item}, {item} ]} */ // Need to add "]}" after the last item if (aPart == Partition::End) { gzWriter->Write("]}"); rv = gzWriter->Finish(); if (NS_WARN_IF(NS_FAILED(rv))) { STUMBLER_ERR("gzWriter finish failed"); } nsCOMPtr<nsIFile> targetFile; nsresult rv = nsDumpUtils::OpenTempFile(kOutputFileNameCompleted, getter_AddRefs(targetFile), kOutputDirName, nsDumpUtils::CREATE); nsAutoString targetFilename; rv = targetFile->GetLeafName(targetFilename); if (NS_WARN_IF(NS_FAILED(rv))) { STUMBLER_ERR("Get Filename failed"); return; } rv = targetFile->Remove(true); if (NS_WARN_IF(NS_FAILED(rv))) { STUMBLER_ERR("Remove File failed"); return; } // Rename tmpfile rv = tmpFile->MoveTo(/* directory */ nullptr, targetFilename); if (NS_WARN_IF(NS_FAILED(rv))) { STUMBLER_ERR("Rename File failed"); return; } return; } // Need to add "{items:[" before the first item if (aPart == Partition::Begining) { gzWriter->Write("{\"items\":[{"); } else if (aPart == Partition::Middle) { gzWriter->Write(",{"); } gzWriter->Write(mDesc.get()); // one item is end with '}' (e.g. {item}) gzWriter->Write("}"); rv = gzWriter->Finish(); if (NS_WARN_IF(NS_FAILED(rv))) { STUMBLER_ERR("gzWriter finish failed"); } // check if it is the end of this file int64_t fileSize = 0; rv = tmpFile->GetFileSize(&fileSize); if (NS_WARN_IF(NS_FAILED(rv))) { STUMBLER_ERR("GetFileSize failed"); return; } if (fileSize >= MAXFILESIZE_KB) { WriteJSON(Partition::End); return; } }