/** * Append another chunk data to a previously-started chunked upload session. * * @param string $uploadId * The unique identifier for the chunked upload session. This is obtained via * {@link chunkedUploadStart}. * * @param int $byteOffset * The number of bytes you think you've already uploaded to the given chunked upload * session. The server will append the new chunk of data after that point. * * @param string $data * The data to append to the existing chunked upload session. * * @return int|bool * If <code>false</code>, it means the server didn't know about the given * <code>$uploadId</code>. This may be because the chunked upload session has expired * (they last around 24 hours). * If <code>true</code>, the chunk was successfully uploaded. If an integer, it means * you and the server don't agree on the current <code>$byteOffset</code>. The returned * integer is the server's internal byte offset for the chunked upload session. You need * to adjust your input to match. * * @throws Exception */ function chunkedUploadContinue($uploadId, $byteOffset, $data) { Checker::argStringNonEmpty("uploadId", $uploadId); Checker::argNat("byteOffset", $byteOffset); Checker::argString("data", $data); $response = $this->_chunkedUpload(array("upload_id" => $uploadId, "offset" => $byteOffset), $data); if ($response->statusCode === 404) { // The server doesn't know our upload ID. Maybe it expired? return false; } $correction = self::_chunkedUploadCheckForOffsetCorrection($response); if ($correction !== null) { list($correctedUploadId, $correctedByteOffset) = $correction; if ($correctedUploadId !== $uploadId) { throw new Exception_BadResponse("Corrective 400 upload_id mismatch: us=" . self::q($uploadId) . " server=" . self::q($correctedUploadId)); } if ($correctedByteOffset === $byteOffset) { throw new Exception_BadResponse("Corrective 400 offset is the same as ours: {$byteOffset}"); } return $correctedByteOffset; } if ($response->statusCode !== 200) { throw RequestUtil::unexpectedStatus($response); } list($retUploadId, $retByteOffset) = self::_chunkedUploadParse200Response($response->body); $nextByteOffset = $byteOffset + strlen($data); if ($uploadId !== $retUploadId) { throw new Exception_BadResponse("upload_id mismatch: us=" . self::q($uploadId) . ", server=" . self::q($uploadId)); } if ($nextByteOffset !== $retByteOffset) { throw new Exception_BadResponse("next-offset mismatch: us={$nextByteOffset}, server={$retByteOffset}"); } return true; }
/** * @param int $maxRetries * The number of times to retry it the action if it fails with one of the transient * API errors. A value of 1 means we'll try the action once and if it fails, we * will retry once. * * @param callable $action * The the action you want to retry. * * @return mixed * Whatever is returned by the $action callable. */ static function runWithRetry($maxRetries, $action) { Checker::argNat("maxRetries", $maxRetries); $retryDelay = 1; $numRetries = 0; while (true) { try { return $action(); } catch (Exception_NetworkIO $ex) { $savedEx = $ex; } catch (Exception_ServerError $ex) { $savedEx = $ex; } catch (Exception_RetryLater $ex) { $savedEx = $ex; } // We maxed out our retries. Propagate the last exception we got. if ($numRetries >= $maxRetries) { throw $savedEx; } $numRetries++; sleep($retryDelay); $retryDelay *= 2; // Exponential back-off. } throw new \RuntimeException("unreachable"); }