示例#1
0
        public void Push()
        {
            var baseRevisions = GetCommonBaseHashesWithRemoteRepo();

            if (baseRevisions == null)
            {
                const string errorMessage = "Push failed: A common revision could not be found with the server.";
                _progress.WriteError(errorMessage);
                throw new HgResumeOperationFailed(errorMessage);
            }

            // create a bundle to push
            var bundleHelper = new PushStorageManager(PathToLocalStorage,
                                                      GetBundleIdFilenameBase("push", baseRevisions.Select(rev => rev.Number.Hash), _repo.GetTip().Number.Hash));
            var bundleFileInfo = new FileInfo(bundleHelper.BundlePath);

            if (bundleFileInfo.Length == 0)
            {
                _progress.WriteStatus("Preparing data to send");
                bool bundleCreatedSuccessfully = _repo.MakeBundle(GetHashStringsFromRevisions(baseRevisions), bundleHelper.BundlePath);
                if (!bundleCreatedSuccessfully)
                {
                    // try again after clearing revision cache
                    LastKnownCommonBases      = new List <Revision>();
                    baseRevisions             = GetCommonBaseHashesWithRemoteRepo();
                    bundleCreatedSuccessfully = _repo.MakeBundle(GetHashStringsFromRevisions(baseRevisions), bundleHelper.BundlePath);
                    if (!bundleCreatedSuccessfully)
                    {
                        const string errorMessage = "Push failed: Unable to create local bundle.";
                        _progress.WriteError(errorMessage);
                        throw new HgResumeOperationFailed(errorMessage);
                    }
                }
                bundleFileInfo.Refresh();
                if (bundleFileInfo.Length == 0)
                {
                    bundleHelper.Cleanup();
                    _progress.WriteMessage("No changes to send.  Push operation completed");
                    return;
                }
            }

            var req = new HgResumeApiParameters
            {
                RepoId        = _apiServer.ProjectId,
                TransId       = bundleHelper.TransactionId,
                StartOfWindow = 0,
                BundleSize    = (int)bundleFileInfo.Length
            };

            req.ChunkSize = (req.BundleSize < InitialChunkSize) ? req.BundleSize : InitialChunkSize;
//            req.BaseHash = baseRevisions; <-- Unless I'm not reading the php right we don't need to set this on push.  JLN Aug-12
            _progress.ProgressIndicator.Initialize();

            _progress.WriteStatus("Sending data");
            int loopCtr = 0;

            do             // loop until finished... or until the user cancels
            {
                loopCtr++;
                if (_progress.CancelRequested)
                {
                    throw new UserCancelledException();
                }

                int dataRemaining = req.BundleSize - req.StartOfWindow;
                if (dataRemaining < req.ChunkSize)
                {
                    req.ChunkSize = dataRemaining;
                }
                byte[] bundleChunk = bundleHelper.GetChunk(req.StartOfWindow, req.ChunkSize);

                /* API parameters
                 * $repoId, $baseHash, $bundleSize, $offset, $data, $transId
                 * */
                var response = PushOneChunk(req, bundleChunk);
                if (response.Status == PushStatus.NotAvailable)
                {
                    _progress.ProgressIndicator.Initialize();
                    _progress.ProgressIndicator.Finish();
                    return;
                }
                if (response.Status == PushStatus.Timeout)
                {
                    _progress.WriteWarning("Push operation timed out.  Retrying...");
                    continue;
                }
                if (response.Status == PushStatus.InvalidHash)
                {
                    // this should not happen...but sometimes it gets into a state where it remembers the wrong basehash of the server (CJH Feb-12)
                    _progress.WriteVerbose("Invalid basehash response received from server... clearing cache and retrying");
//                    req.BaseHash = GetCommonBaseHashesWithRemoteRepo(false); <-- Unless I'm misreading the php we don't need to set this on a push. JLN Aug-12
                    continue;
                }
                if (response.Status == PushStatus.Fail)
                {
                    // This 'Fail' intentionally aborts the push attempt.  I think we can continue to go around the loop and retry. See Pull also. CP 2012-06
                    continue;
                    //var errorMessage = "Push operation failed";
                    //_progress.WriteError(errorMessage);
                    //throw new HgResumeOperationFailed(errorMessage);
                }
                if (response.Status == PushStatus.Reset)
                {
                    FinishPush(req.TransId);
                    bundleHelper.Cleanup();
                    const string errorMessage = "Push failed: Server reset.";
                    _progress.WriteError(errorMessage);
                    throw new HgResumeOperationFailed(errorMessage);
                }

                if (response.Status == PushStatus.Complete || req.StartOfWindow >= req.BundleSize)
                {
                    if (response.Status == PushStatus.Complete)
                    {
                        _progress.WriteMessage("Finished sending");
                    }
                    else
                    {
                        _progress.WriteMessage("Finished sending. Server unpacking data");
                    }
                    _progress.ProgressIndicator.Finish();

                    // update our local knowledge of what the server has
                    LastKnownCommonBases = new List <Revision>(_repo.BranchingHelper.GetBranches());                    // This may be a little optimistic, the server may still be unbundling the data.
                    //FinishPush(req.TransId);  // We can't really tell when the server has finished processing our pulled data.  The server cleans up after itself. CP 2012-07
                    bundleHelper.Cleanup();
                    return;
                }

                req.ChunkSize     = response.ChunkSize;
                req.StartOfWindow = response.StartOfWindow;
                if (loopCtr == 1 && req.StartOfWindow > req.ChunkSize)
                {
                    string message = String.Format("Resuming push operation at {0} sent", GetHumanReadableByteSize(req.StartOfWindow));
                    _progress.WriteVerbose(message);
                }
                string eta = CalculateEstimatedTimeRemaining(req.BundleSize, req.ChunkSize, req.StartOfWindow);
                _progress.WriteStatus(string.Format("Sending {0} {1}", GetHumanReadableByteSize(req.BundleSize), eta));
                _progress.ProgressIndicator.PercentCompleted = (int)((long)req.StartOfWindow * 100 / req.BundleSize);
            } while (req.StartOfWindow < req.BundleSize);
        }
示例#2
0
        private PushResponse PushOneChunk(HgResumeApiParameters request, byte[] dataToPush)
        {
            var pushResponse = new PushResponse(PushStatus.Fail);

            try
            {
                HgResumeApiResponse response = _apiServer.Execute("pushBundleChunk", request, dataToPush, TimeoutInSeconds);
                if (response == null)
                {
                    _progress.WriteVerbose("API REQ: {0} Timeout");
                    pushResponse.Status = PushStatus.Timeout;
                    return(pushResponse);
                }

                /* API returns the following HTTP codes:
                 * 200 OK (SUCCESS)
                 * 202 Accepted (RECEIVED)
                 * 412 Precondition Failed (RESEND)
                 * 400 Bad Request (FAIL, UNKNOWNID, and RESET)
                 */
                _progress.WriteVerbose("API REQ: {0} RSP: {1} in {2}ms", _apiServer.Url, response.HttpStatus, response.ResponseTimeInMilliseconds);
                if (response.HttpStatus == HttpStatusCode.ServiceUnavailable && response.Content.Length > 0)
                {
                    var msg = String.Format("Server temporarily unavailable: {0}",
                                            Encoding.UTF8.GetString(response.Content));
                    _progress.WriteError(msg);
                    pushResponse.Status = PushStatus.NotAvailable;
                    return(pushResponse);
                }
                if (response.ResumableResponse.HasNote)
                {
                    _progress.WriteWarning(String.Format("Server replied: {0}", response.ResumableResponse.Note));
                }
                // the chunk was received successfully
                if (response.HttpStatus == HttpStatusCode.Accepted)
                {
                    pushResponse.StartOfWindow = response.ResumableResponse.StartOfWindow;
                    pushResponse.Status        = PushStatus.Received;
                    pushResponse.ChunkSize     = CalculateChunkSize(request.ChunkSize, response.ResponseTimeInMilliseconds);
                    return(pushResponse);
                }

                // the final chunk was received successfully
                if (response.HttpStatus == HttpStatusCode.OK)
                {
                    pushResponse.Status = PushStatus.Complete;
                    return(pushResponse);
                }

                if (response.HttpStatus == HttpStatusCode.BadRequest)
                {
                    if (response.ResumableResponse.Status == "UNKNOWNID")
                    {
                        _progress.WriteError("The server {0} does not have the project '{1}'", _targetLabel, _apiServer.ProjectId);
                        return(pushResponse);
                    }
                    if (response.ResumableResponse.Status == "RESET")
                    {
                        _progress.WriteError("Push failed: All chunks were pushed to the server, but the unbundle operation failed.  Try again later.");
                        _progress.WriteVerbose(string.Format("Additional error info: {0}", response.ResumableResponse.Error));
                        pushResponse.Status = PushStatus.Reset;
                        return(pushResponse);
                    }
                    if (response.ResumableResponse.HasError)
                    {
                        if (response.ResumableResponse.Error == "invalid baseHash")
                        {
                            pushResponse.Status = PushStatus.InvalidHash;
                        }
                        else
                        {
                            _progress.WriteError("Server Error: {0}", response.ResumableResponse.Error);
                        }
                        return(pushResponse);
                    }
                }
                _progress.WriteWarning("Invalid Server Response '{0}'", response.HttpStatus);
                return(pushResponse);
            }
            catch (WebException e)
            {
                _progress.WriteWarning(String.Format("Push data chunk failed: {0}", e.Message));
                return(pushResponse);
            }
        }
示例#3
0
 public HgResumeApiResponse Execute(string method, HgResumeApiParameters request, int secondsBeforeTimeout)
 {
     return Execute(method, request, new byte[0], secondsBeforeTimeout);
 }
示例#4
0
        public HgResumeApiResponse Execute(string method, HgResumeApiParameters request, byte[] bytesToWrite, int secondsBeforeTimeout)
        {
            ValidateParameters(method, request, bytesToWrite, secondsBeforeTimeout);
            if (method == "getRevisions")
            {
                IEnumerable<Revision> revisions = _repo.GetAllRevisions();

                if (revisions.Count() == 0)
                {
                    return ApiResponses.Revisions("0:default");
                }
                var revisionStrings = _repo.GetAllRevisions().Select(rev => rev.Number.Hash + ':' + rev.Branch);
                return ApiResponses.Revisions(string.Join("|", revisionStrings.ToArray()));
            }
            if (method == "finishPushBundle")
            {
                return ApiResponses.PushComplete();
            }
            if (method == "pushBundleChunk")
            {
                _executeCount++;
                if (_cancelCount == _executeCount)
                {
                    _progress.CancelRequested = true;
                    return ApiResponses.Failed("");
                }
                if (_failCount == _executeCount)
                {
                    return ApiResponses.Failed("");
                }
                if (_timeoutList.Contains(_executeCount))
                {
                    return null;
                }
                if (_serverUnavailableList.Any(i => i.ExecuteCount == _executeCount))
                {
                    return ApiResponses.NotAvailable(
                            _serverUnavailableList.Where(i => i.ExecuteCount == _executeCount).First().Message
                            );
                }
                _helper = new PullStorageManager(_localStorage.Path, request.TransId);

                //int bundleSize = Convert.ToInt32(parameters["bundleSize"]);
                //int offset = Convert.ToInt32(parameters["offset"]);
                //int chunkSize = bytesToWrite.Length;

                _helper.WriteChunk(request.StartOfWindow, bytesToWrite);

                if (request.StartOfWindow + request.ChunkSize == request.BundleSize)
                {
                    if (_repo.Unbundle(_helper.BundlePath))
                    {
                        return ApiResponses.PushComplete();
                    }
                    return ApiResponses.Reset();
                }
                if (request.StartOfWindow + request.ChunkSize < request.BundleSize)
                {
                    return ApiResponses.PushAccepted(_helper.StartOfWindow);
                }
                return ApiResponses.Failed("offset + chunkSize > bundleSize !");
            }
            return ApiResponses.Custom(HttpStatusCode.InternalServerError);
        }
示例#5
0
 public HgResumeApiResponse Execute(string method, HgResumeApiParameters request, byte[] contentToSend, int secondsBeforeTimeout)
 {
     ValidateParameters(method, request, contentToSend, secondsBeforeTimeout);
     HgResumeApiResponse response;
     if (_responseQueue.Count > 0)
     {
         response = _responseQueue.Dequeue();
         if (response.HttpStatus == HttpStatusCode.RequestTimeout)
         {
             return null;
         }
     } else
     {
         response = new HgResumeApiResponse {HttpStatus = HttpStatusCode.InternalServerError};
     }
     response.ResponseTimeInMilliseconds = 200;
     return response;
 }
示例#6
0
        public HgResumeApiResponse Execute(string method, HgResumeApiParameters request, byte[] bytesToWrite, int secondsBeforeTimeout)
        {
            ValidateParameters(method, request, bytesToWrite, secondsBeforeTimeout);
            _executeCount++;
            if (method == "finishPullBundle")
            {
                if (CurrentTip != OriginalTip)
                {
                    PrepareBundle(HgResumeTransport.GetHashStringsFromRevisions(_repo.BranchingHelper.GetBranches()));
                    return ApiResponses.Reset(); // repo changed in between pulls
                }
                return ApiResponses.Custom(HttpStatusCode.OK);
            }
            if (method == "getRevisions")
            {
                IEnumerable<string> revisions = _repo.GetAllRevisions().Select(rev => rev.Number.Hash + ':' + rev.Branch);
                return ApiResponses.Revisions(string.Join("|", revisions.ToArray()));
            }
            if (method == "pullBundleChunk")
            {
                if (_cancelCount == _executeCount)
                {
                    _progress.CancelRequested = true;
                    return ApiResponses.Failed("");
                }
                if (_failCount == _executeCount)
                {
                    return ApiResponses.Failed("");
                }
                if (_timeoutList.Contains(_executeCount))
                {
                    return null;
                }
                if (_serverUnavailableList.Any(i => i.ExecuteCount == _executeCount))
                {
                    return ApiResponses.NotAvailable(
                            _serverUnavailableList.Where(i => i.ExecuteCount == _executeCount).First().Message
                            );
                }
                if (Array.BinarySearch(request.BaseHashes, 0, request.BaseHashes.Length, CurrentTip) >= 0)
                {
                    return ApiResponses.PullNoChange();
                }

                var bundleFileInfo = new FileInfo(_helper.BundlePath);
                if (bundleFileInfo.Exists && bundleFileInfo.Length == 0  || !bundleFileInfo.Exists)
                {
                    PrepareBundle(request.BaseHashes);
                }
                //int offset = Convert.ToInt32(request["offset"]);
                //int chunkSize = Convert.ToInt32(request["chunkSize"]);
                var bundleFile = new FileInfo(_helper.BundlePath);
                if (request.StartOfWindow >= bundleFile.Length)
                {
                    return ApiResponses.Failed("offset greater than bundleSize");
                }
                var chunk = _helper.GetChunk(request.StartOfWindow, request.ChunkSize);
                return ApiResponses.PullOk(Convert.ToInt32(bundleFile.Length), chunk);
            }
            return ApiResponses.Custom(HttpStatusCode.InternalServerError);
        }
示例#7
0
        public HgResumeApiResponse Execute(string method, HgResumeApiParameters parameters, byte[] contentToSend, int secondsBeforeTimeout)
        {
            string queryString = parameters.BuildQueryString();

            _urlExecuted = String.Format("{0}://{1}/api/v{2}/{3}?{4}", _url.Scheme, _url.Host, APIVERSION, method, queryString);
            var req = WebRequest.Create(_urlExecuted) as HttpWebRequest;

            req.UserAgent       = String.Format("HgResume v{0}", APIVERSION);
            req.PreAuthenticate = true;
            if (!_url.UserInfo.Contains(":"))
            {
                throw new HgResumeException("Username or password were not supplied in custom location");
            }
            req.Credentials = new NetworkCredential(UserName, Password);
            req.Timeout     = secondsBeforeTimeout * 1000;         // timeout is in milliseconds
            if (contentToSend.Length == 0)
            {
                req.Method = WebRequestMethods.Http.Get;
            }
            else
            {
                req.Method        = WebRequestMethods.Http.Post;
                req.ContentLength = contentToSend.Length;
                req.ContentType   = "text/plain";                // i'm not sure this is really what we want.  The other possibility is "application/x-www-form-urlencoded"
                using (var reqStream = req.GetRequestStream())
                {
                    reqStream.Write(contentToSend, 0, contentToSend.Length);
                }
            }


            HttpWebResponse     res;
            HgResumeApiResponse apiResponse;
            var stopwatch = new Stopwatch();

            stopwatch.Start();
            try
            {
                using (res = (HttpWebResponse)req.GetResponse())
                {
                    apiResponse = HandleResponse(res);
                }
            }
            catch (WebException e)
            {
                if (e.Status == WebExceptionStatus.ProtocolError)
                {
                    using (res = (HttpWebResponse)e.Response)
                    {
                        apiResponse = HandleResponse(res);
                    }
                }
                else if (e.Status == WebExceptionStatus.Timeout)
                {
                    apiResponse = null;
                }
                else
                {
                    throw;                     // throw for other types of network errors (see WebExceptionStatus for the full list of errors)
                }
            }
            finally
            {
                stopwatch.Stop();
            }
            if (apiResponse != null)
            {
                apiResponse.ResponseTimeInMilliseconds = stopwatch.ElapsedMilliseconds;
            }
            return(apiResponse);
        }
示例#8
0
        public HgResumeApiResponse Execute(string method, HgResumeApiParameters parameters, byte[] contentToSend, int secondsBeforeTimeout)
        {
            string queryString = parameters.BuildQueryString();

            Url = string.Format("{0}://{1}/api/v{2}/{3}?{4}", _url.Scheme, _url.Host, ApiVersion, method, queryString);
            var req = (HttpWebRequest)WebRequest.Create(Url);

            req.UserAgent       = $"HgResume v{ApiVersion}";
            req.PreAuthenticate = true;
            if (string.IsNullOrEmpty(Properties.Settings.Default.LanguageForgeUser) ||
                string.IsNullOrEmpty(ServerSettingsModel.PasswordForSession))
            {
                throw new HgResumeException("Missing username or password");
            }
            req.Credentials = new NetworkCredential(Properties.Settings.Default.LanguageForgeUser, ServerSettingsModel.PasswordForSession);
            req.Timeout     = secondsBeforeTimeout * 1000;         // timeout is in milliseconds
            if (contentToSend.Length == 0)
            {
                req.Method = WebRequestMethods.Http.Get;
            }
            else
            {
                req.Method        = WebRequestMethods.Http.Post;
                req.ContentLength = contentToSend.Length;
                req.ContentType   = "text/plain";                // i'm not sure this is really what we want.  The other possibility is "application/x-www-form-urlencoded"
                using (var reqStream = req.GetRequestStream())
                {
                    reqStream.Write(contentToSend, 0, contentToSend.Length);
                }
            }


            HttpWebResponse     res;
            HgResumeApiResponse apiResponse;
            var stopwatch = new Stopwatch();

            stopwatch.Start();
            try
            {
                using (res = (HttpWebResponse)req.GetResponse())
                {
                    apiResponse = HandleResponse(res);
                }
            }
            catch (WebException e)
            {
                if (e.Status == WebExceptionStatus.ProtocolError)
                {
                    using (res = (HttpWebResponse)e.Response)
                    {
                        apiResponse = HandleResponse(res);
                    }
                }
                else if (e.Status == WebExceptionStatus.Timeout)
                {
                    apiResponse = null;
                }
                else
                {
                    throw;                     // throw for other types of network errors (see WebExceptionStatus for the full list of errors)
                }
            }
            finally
            {
                stopwatch.Stop();
            }
            if (apiResponse != null)
            {
                apiResponse.ResponseTimeInMilliseconds = stopwatch.ElapsedMilliseconds;
            }
            return(apiResponse);
        }
示例#9
0
        private PushResponse PushOneChunk(HgResumeApiParameters request, byte[] dataToPush)
        {
            var pushResponse = new PushResponse(PushStatus.Fail);
            try
            {
                HgResumeApiResponse response = _apiServer.Execute("pushBundleChunk", request, dataToPush, TimeoutInSeconds);
                if (response == null)
                {
                    _progress.WriteVerbose("API REQ: {0} Timeout");
                    pushResponse.Status = PushStatus.Timeout;
                    return pushResponse;
                }
                /* API returns the following HTTP codes:
                 * 200 OK (SUCCESS)
                 * 202 Accepted (RECEIVED)
                 * 412 Precondition Failed (RESEND)
                 * 400 Bad Request (FAIL, UNKNOWNID, and RESET)
                 */
                _progress.WriteVerbose("API REQ: {0} RSP: {1} in {2}ms", _apiServer.Url, response.HttpStatus, response.ResponseTimeInMilliseconds);
                if (response.HttpStatus == HttpStatusCode.ServiceUnavailable && response.Content.Length > 0)
                {
                    var msg = String.Format("Server temporarily unavailable: {0}",
                                            Encoding.UTF8.GetString(response.Content));
                    _progress.WriteError(msg);
                    pushResponse.Status = PushStatus.NotAvailable;
                    return pushResponse;
                }
                if (response.ResumableResponse.HasNote)
                {
                    _progress.WriteWarning(String.Format("Server replied: {0}", response.ResumableResponse.Note));
                }
                // the chunk was received successfully
                if (response.HttpStatus == HttpStatusCode.Accepted)
                {
                    pushResponse.StartOfWindow = response.ResumableResponse.StartOfWindow;
                    pushResponse.Status = PushStatus.Received;
                    pushResponse.ChunkSize = CalculateChunkSize(request.ChunkSize, response.ResponseTimeInMilliseconds);
                    return pushResponse;
                }

                // the final chunk was received successfully
                if (response.HttpStatus == HttpStatusCode.OK)
                {
                    pushResponse.Status = PushStatus.Complete;
                    return pushResponse;
                }

                if (response.HttpStatus == HttpStatusCode.BadRequest)
                {
                    if (response.ResumableResponse.Status == "UNKNOWNID")
                    {
                        _progress.WriteError("The server {0} does not have the project '{1}'", _targetLabel, _apiServer.ProjectId);
                        return pushResponse;
                    }
                    if (response.ResumableResponse.Status == "RESET")
                    {
                        _progress.WriteError("Push failed: All chunks were pushed to the server, but the unbundle operation failed.  Try again later.");
                        pushResponse.Status = PushStatus.Reset;
                        return pushResponse;
                    }
                    if (response.ResumableResponse.HasError)
                    {
                        if (response.ResumableResponse.Error == "invalid baseHash")
                        {
                            pushResponse.Status = PushStatus.InvalidHash;
                        }
                        else
                        {
                            _progress.WriteError("Server Error: {0}", response.ResumableResponse.Error);
                        }
                        return pushResponse;
                    }

                }
                _progress.WriteWarning("Invalid Server Response '{0}'", response.HttpStatus);
                return pushResponse;
            }
            catch (WebException e)
            {
                _progress.WriteWarning(String.Format("Push data chunk failed: {0}", e.Message));
                return pushResponse;
            }
        }
示例#10
0
 public HgResumeApiResponse Execute(string method, HgResumeApiParameters request, int secondsBeforeTimeout)
 {
     return(Execute(method, request, new byte[0], secondsBeforeTimeout));
 }
示例#11
0
        private PullResponse PullOneChunk(HgResumeApiParameters request)
        {
            var pullResponse = new PullResponse(PullStatus.Fail);
            try
            {

                HgResumeApiResponse response = _apiServer.Execute("pullBundleChunk", request, TimeoutInSeconds);
                if (response == null)
                {
                    _progress.WriteVerbose("API REQ: {0} Timeout", _apiServer.Url);
                    pullResponse.Status = PullStatus.Timeout;
                    return pullResponse;
                }
                /* API returns the following HTTP codes:
                 * 200 OK (SUCCESS)
                 * 304 Not Modified (NOCHANGE)
                 * 400 Bad Request (FAIL, UNKNOWNID)
                 */
                _progress.WriteVerbose("API REQ: {0} RSP: {1} in {2}ms", _apiServer.Url, response.HttpStatus, response.ResponseTimeInMilliseconds);
                if (response.ResumableResponse.HasNote)
                {
                    _progress.WriteMessage(String.Format("Server replied: {0}", response.ResumableResponse.Note));
                }

                if (response.HttpStatus == HttpStatusCode.ServiceUnavailable && response.Content.Length > 0)
                {
                    var msg = String.Format("Server temporarily unavailable: {0}",
                                            Encoding.UTF8.GetString(response.Content));
                    _progress.WriteError(msg);
                    pullResponse.Status = PullStatus.NotAvailable;
                    return pullResponse;
                }
                if (response.HttpStatus == HttpStatusCode.NotModified)
                {
                    pullResponse.Status = PullStatus.NoChange;
                    return pullResponse;
                }
                if (response.HttpStatus == HttpStatusCode.Accepted)
                {
                    pullResponse.Status = PullStatus.InProgress;
                    return pullResponse;
                }

                // chunk pulled OK
                if (response.HttpStatus == HttpStatusCode.OK)
                {
                    pullResponse.BundleSize = response.ResumableResponse.BundleSize;
                    pullResponse.Status = PullStatus.OK;
                    pullResponse.ChunkSize = CalculateChunkSize(request.ChunkSize, response.ResponseTimeInMilliseconds);

                    pullResponse.Chunk = response.Content;
                    return pullResponse;
                }
                if (response.HttpStatus == HttpStatusCode.BadRequest && response.ResumableResponse.Status == "UNKNOWNID")
                {
                    // this is not implemented currently (feb 2012 cjh)
                    _progress.WriteError("The server {0} does not have the project '{1}'", _targetLabel, request.RepoId);
                    return pullResponse;
                }
                if (response.HttpStatus == HttpStatusCode.BadRequest && response.ResumableResponse.Status == "RESET")
                {
                    pullResponse.Status = PullStatus.Reset;
                    return pullResponse;
                }
                if (response.HttpStatus == HttpStatusCode.BadRequest)
                {
                    if (response.ResumableResponse.HasError)
                    {
                        if (response.ResumableResponse.Error == "invalid baseHash")
                        {
                            pullResponse.Status = PullStatus.InvalidHash;
                        } else
                        {
                            _progress.WriteWarning("Server Error: {0}", response.ResumableResponse.Error);
                        }
                    }
                    return pullResponse;
                }
                if (response.HttpStatus == HttpStatusCode.Unauthorized)
                {
                    _progress.WriteWarning("There is an authorization problem accessing this project. Check the project ID as well as your username and password. Alternatively, you may not be authorized to access this project.");
                    pullResponse.Status =  PullStatus.Unauthorized;
                }
                _progress.WriteWarning("Invalid Server Response '{0}'", response.HttpStatus);
                return pullResponse;
            }
            catch (WebException e)
            {
                _progress.WriteWarning(String.Format("Pull data chunk failed: {0}", e.Message));
                return pullResponse;
            }
        }
示例#12
0
        public void Push()
        {
            var baseRevisions = GetCommonBaseHashesWithRemoteRepo();
            if (baseRevisions == null)
            {
                const string errorMessage = "Push failed: A common revision could not be found with the server.";
                _progress.WriteError(errorMessage);
                throw new HgResumeOperationFailed(errorMessage);
            }

            // create a bundle to push
            string tip = _repo.GetTip().Number.Hash;
            string bundleId = "";
            foreach (var revision in baseRevisions)
            {
                bundleId += String.Format("{0}-{1}", revision.Number.Hash, tip);
            }
            var bundleHelper = new PushStorageManager(PathToLocalStorage, bundleId);
            var bundleFileInfo = new FileInfo(bundleHelper.BundlePath);
            if (bundleFileInfo.Length == 0)
            {
                _progress.WriteStatus("Preparing data to send");
                bool bundleCreatedSuccessfully = _repo.MakeBundle(GetHashStringsFromRevisions(baseRevisions), bundleHelper.BundlePath);
                if (!bundleCreatedSuccessfully)
                {
                    // try again after clearing revision cache
                    LastKnownCommonBases = new List<Revision>();
                    baseRevisions = GetCommonBaseHashesWithRemoteRepo();
                    bundleCreatedSuccessfully = _repo.MakeBundle(GetHashStringsFromRevisions(baseRevisions), bundleHelper.BundlePath);
                    if (!bundleCreatedSuccessfully)
                    {
                        const string errorMessage = "Push failed: Unable to create local bundle.";
                        _progress.WriteError(errorMessage);
                        throw new HgResumeOperationFailed(errorMessage);
                    }
                }
                bundleFileInfo.Refresh();
                if (bundleFileInfo.Length == 0)
                {
                    bundleHelper.Cleanup();
                    _progress.WriteMessage("No changes to send.  Push operation completed");
                    return;
                }
            }

            var req = new HgResumeApiParameters
                      {
                          RepoId = _apiServer.ProjectId,
                          TransId = bundleHelper.TransactionId,
                          StartOfWindow = 0,
                          BundleSize = (int) bundleFileInfo.Length
                      };
            req.ChunkSize = (req.BundleSize < InitialChunkSize) ? req.BundleSize : InitialChunkSize;
            //            req.BaseHash = baseRevisions; <-- Unless I'm not reading the php right we don't need to set this on push.  JLN Aug-12
            _progress.ProgressIndicator.Initialize();

            _progress.WriteStatus("Sending data");
            int loopCtr = 0;
            do // loop until finished... or until the user cancels
            {
                loopCtr++;
                if (_progress.CancelRequested)
                {
                    throw new UserCancelledException();
                }

                int dataRemaining = req.BundleSize - req.StartOfWindow;
                if (dataRemaining < req.ChunkSize)
                {
                    req.ChunkSize = dataRemaining;
                }
                byte[] bundleChunk = bundleHelper.GetChunk(req.StartOfWindow, req.ChunkSize);

                /* API parameters
                 * $repoId, $baseHash, $bundleSize, $offset, $data, $transId
                 * */
                var response = PushOneChunk(req, bundleChunk);
                if (response.Status == PushStatus.NotAvailable)
                {
                    _progress.ProgressIndicator.Initialize();
                    _progress.ProgressIndicator.Finish();
                    return;
                }
                if (response.Status == PushStatus.Timeout)
                {
                    _progress.WriteWarning("Push operation timed out.  Retrying...");
                    continue;
                }
                if (response.Status == PushStatus.InvalidHash)
                {
                    // this should not happen...but sometimes it gets into a state where it remembers the wrong basehash of the server (CJH Feb-12)
                    _progress.WriteVerbose("Invalid basehash response received from server... clearing cache and retrying");
            //                    req.BaseHash = GetCommonBaseHashesWithRemoteRepo(false); <-- Unless I'm misreading the php we don't need to set this on a push. JLN Aug-12
                    continue;
                }
                if (response.Status == PushStatus.Fail)
                {
                    // This 'Fail' intentionally aborts the push attempt.  I think we can continue to go around the loop and retry. See Pull also. CP 2012-06
                    continue;
                    //var errorMessage = "Push operation failed";
                    //_progress.WriteError(errorMessage);
                    //throw new HgResumeOperationFailed(errorMessage);
                }
                if (response.Status == PushStatus.Reset)
                {
                    FinishPush(req.TransId);
                    bundleHelper.Cleanup();
                    const string errorMessage = "Push failed: Server reset.";
                    _progress.WriteError(errorMessage);
                    throw new HgResumeOperationFailed(errorMessage);
                }

                if (response.Status == PushStatus.Complete || req.StartOfWindow >= req.BundleSize)
                {
                    if (response.Status == PushStatus.Complete)
                    {
                        _progress.WriteMessage("Finished sending");
                    } else
                    {
                        _progress.WriteMessage("Finished sending. Server unpacking data");
                    }
                    _progress.ProgressIndicator.Finish();

                    // update our local knowledge of what the server has
                    LastKnownCommonBases = new List<Revision>(_repo.BranchingHelper.GetBranches()); // This may be a little optimistic, the server may still be unbundling the data.
                    //FinishPush(req.TransId);  // We can't really tell when the server has finished processing our pulled data.  The server cleans up after itself. CP 2012-07
                    bundleHelper.Cleanup();
                    return;
                }

                req.ChunkSize = response.ChunkSize;
                req.StartOfWindow = response.StartOfWindow;
                if (loopCtr == 1 && req.StartOfWindow > req.ChunkSize)
                {
                    string message = String.Format("Resuming push operation at {0} sent", GetHumanReadableByteSize(req.StartOfWindow));
                    _progress.WriteVerbose(message);
                }
                string eta = CalculateEstimatedTimeRemaining(req.BundleSize, req.ChunkSize, req.StartOfWindow);
                _progress.WriteStatus(string.Format("Sending {0} {1}", GetHumanReadableByteSize(req.BundleSize), eta));
                _progress.ProgressIndicator.PercentCompleted = (int)((long)req.StartOfWindow * 100 / req.BundleSize);
            } while (req.StartOfWindow < req.BundleSize);
        }
示例#13
0
        public bool Pull(string[] baseRevisions)
        {
            var tipRevision = _repo.GetTip();
            string localTip = "0";
            string errorMessage;
            if (tipRevision != null)
            {
                localTip = tipRevision.Number.Hash;
            }

            if (baseRevisions.Length == 0)
            {
                errorMessage = "Pull failed: No base revision.";
                _progress.WriteError(errorMessage);
                throw new HgResumeOperationFailed(errorMessage);
            }

            string bundleId = "";
            foreach (var revision in baseRevisions)
            {
                bundleId += revision + "_" + localTip + '-';
            }
            bundleId = bundleId.TrimEnd('-');
            var bundleHelper = new PullStorageManager(PathToLocalStorage, bundleId);
            var req = new HgResumeApiParameters
                      {
                          RepoId = _apiServer.ProjectId,
                          BaseHashes = baseRevisions,
                          TransId = bundleHelper.TransactionId,
                          StartOfWindow = bundleHelper.StartOfWindow,
                          ChunkSize = InitialChunkSize
                      };
            int bundleSize = 0;

            int loopCtr = 1;
            bool retryLoop;

            do
            {
                if (_progress.CancelRequested)
                {
                    throw new UserCancelledException();
                }
                retryLoop = false;
                var response = PullOneChunk(req);
                if (response.Status == PullStatus.Unauthorized)
                {
                    throw new UnauthorizedAccessException();
                }
                if (response.Status == PullStatus.NotAvailable)
                {
                    _progress.ProgressIndicator.Initialize();
                    _progress.ProgressIndicator.Finish();
                    return false;
                }
                if (response.Status == PullStatus.Timeout)
                {
                    _progress.WriteWarning("Pull operation timed out.  Retrying...");
                    retryLoop = true;
                    continue;
                }
                if (response.Status == PullStatus.NoChange)
                {
                    _progress.WriteMessage("No changes");
                    bundleHelper.Cleanup();
                    _progress.ProgressIndicator.Initialize();
                    _progress.ProgressIndicator.Finish();
                    return false;
                }
                if (response.Status == PullStatus.InProgress)
                {
                    _progress.WriteStatus("Preparing data on server");
                    retryLoop = true;
                    // advance the progress bar 2% to show that something is happening
                    _progress.ProgressIndicator.PercentCompleted = _progress.ProgressIndicator.PercentCompleted + 2;
                    continue;
                }
                if (response.Status == PullStatus.InvalidHash)
                {
                    // this should not happen...but sometimes it gets into a state where it remembers the wrong basehash of the server (CJH Feb-12)
                    retryLoop = true;
                    _progress.WriteVerbose("Invalid basehash response received from server... clearing cache and retrying");
                    req.BaseHashes = GetHashStringsFromRevisions(GetCommonBaseHashesWithRemoteRepo(false));
                    continue;
                }
                if (response.Status == PullStatus.Fail)
                {
                    // Not sure that we need to abort the attempts just because .Net web request says so.  See Push also. CP 2012-06
                    errorMessage = "Pull data chunk failed";
                    _progress.WriteError(errorMessage);
                    retryLoop = true;
                    req.StartOfWindow = bundleHelper.StartOfWindow;
                    //_progress.ProgressIndicator.Initialize();
                    //_progress.ProgressIndicator.Finish();
                    //throw new HgResumeOperationFailed(errorMessage);
                    continue;
                }
                if (response.Status == PullStatus.Reset)
                {
                    retryLoop = true;
                    bundleHelper.Reset();
                    _progress.WriteVerbose("Server's bundle cache has expired.  Restarting pull...");
                    req.StartOfWindow = bundleHelper.StartOfWindow;
                    continue;
                }

                //bundleSizeFromResponse = response.BundleSize;
                if (loopCtr == 1)
                {
                    _progress.ProgressIndicator.Initialize();
                    if (req.StartOfWindow != 0)
                    {
                        string message = String.Format("Resuming pull operation at {0} received", GetHumanReadableByteSize(req.StartOfWindow));
                        _progress.WriteVerbose(message);
                    }
                }

                bundleHelper.WriteChunk(req.StartOfWindow, response.Chunk);
                req.StartOfWindow = req.StartOfWindow + response.Chunk.Length;
                req.ChunkSize = response.ChunkSize;
                if (bundleSize == response.BundleSize && bundleSize != 0)
                {
                    _progress.ProgressIndicator.PercentCompleted = (int)((long)req.StartOfWindow * 100 / bundleSize);
                    string eta = CalculateEstimatedTimeRemaining(bundleSize, req.ChunkSize, req.StartOfWindow);
                    _progress.WriteStatus(string.Format("Receiving {0} {1}", GetHumanReadableByteSize(bundleSize), eta));
                }
                else
                {
                    // this is only useful when the bundle size is significantly large (like with a clone operation) such that
                    // the server takes a long time to create the bundle, and the bundleSize continues to rise as the chunks are received
                    bundleSize = response.BundleSize;
                    _progress.WriteStatus(string.Format("Preparing data on server (>{0})", GetHumanReadableByteSize(bundleSize)));
                }
                loopCtr++;

            } while (req.StartOfWindow < bundleSize || retryLoop);

            if (_repo.Unbundle(bundleHelper.BundlePath))
            {
                _progress.WriteMessage("Pull operation completed successfully");
                _progress.ProgressIndicator.Finish();
                _progress.WriteMessage("Finished Receiving");
                bundleHelper.Cleanup();
                var response = FinishPull(req.TransId);
                if (response == PullStatus.Reset)
                {
                    /* Calling Pull recursively to finish up another pull will mess up the ProgressIndicator.  This case is
                    // rare enough that it's not worth trying to get the progress indicator working for a recursive Pull()
                    */
                    _progress.WriteMessage("Remote repo has changed.  Initiating additional pull operation");
                    return Pull();
                }

                // REVIEW: I'm not sure why this was set to the server tip before, if we just pulled then won't our head
                // be the correct common base? Maybe not if a merge needs to happen,
                LastKnownCommonBases = new List<Revision>(_repo.BranchingHelper.GetBranches());
                return true;
            }
            _progress.WriteError("Received all data but local unbundle operation failed or resulted in multiple heads!");
            _progress.ProgressIndicator.Finish();
            bundleHelper.Cleanup();
            errorMessage = "Pull operation failed";
            _progress.WriteError(errorMessage);
            throw new HgResumeOperationFailed(errorMessage);
        }
示例#14
0
        public bool Pull(string[] baseRevisions)
        {
            var    tipRevision = _repo.GetTip();
            var    localTip    = "0";
            string errorMessage;

            if (tipRevision != null)
            {
                localTip = tipRevision.Number.Hash;
            }

            if (baseRevisions.Length == 0)
            {
                errorMessage = "Pull failed: No base revision.";
                _progress.WriteError(errorMessage);
                throw new HgResumeOperationFailed(errorMessage);
            }

            var bundleHelper = new PullStorageManager(PathToLocalStorage, GetBundleIdFilenameBase("pull", baseRevisions, localTip));
            var req          = new HgResumeApiParameters
            {
                RepoId        = _apiServer.ProjectId,
                BaseHashes    = baseRevisions,
                TransId       = bundleHelper.TransactionId,
                StartOfWindow = bundleHelper.StartOfWindow,
                ChunkSize     = InitialChunkSize
            };
            int bundleSize = 0;

            int  loopCtr = 1;
            bool retryLoop;

            do
            {
                if (_progress.CancelRequested)
                {
                    throw new UserCancelledException();
                }
                retryLoop = false;
                var response = PullOneChunk(req);
                if (response.Status == PullStatus.Unauthorized)
                {
                    throw new UnauthorizedAccessException();
                }
                if (response.Status == PullStatus.NotAvailable)
                {
                    _progress.ProgressIndicator.Initialize();
                    _progress.ProgressIndicator.Finish();
                    return(false);
                }
                if (response.Status == PullStatus.Timeout)
                {
                    _progress.WriteWarning("Pull operation timed out.  Retrying...");
                    retryLoop = true;
                    continue;
                }
                if (response.Status == PullStatus.NoChange)
                {
                    _progress.WriteMessage("No changes");
                    bundleHelper.Cleanup();
                    _progress.ProgressIndicator.Initialize();
                    _progress.ProgressIndicator.Finish();
                    return(false);
                }
                if (response.Status == PullStatus.InProgress)
                {
                    _progress.WriteStatus("Preparing data on server");
                    retryLoop = true;
                    // advance the progress bar 2% to show that something is happening
                    _progress.ProgressIndicator.PercentCompleted = _progress.ProgressIndicator.PercentCompleted + 2;
                    continue;
                }
                if (response.Status == PullStatus.InvalidHash)
                {
                    // this should not happen...but sometimes it gets into a state where it remembers the wrong basehash of the server (CJH Feb-12)
                    retryLoop = true;
                    _progress.WriteVerbose("Invalid basehash response received from server... clearing cache and retrying");
                    req.BaseHashes = GetHashStringsFromRevisions(GetCommonBaseHashesWithRemoteRepo(false));
                    continue;
                }
                if (response.Status == PullStatus.Fail)
                {
                    // No need to abort the attempts just because .Net web request says so. See Push also. CP 2012-06
                    _progress.WriteWarning("Pull data chunk failed");
                    retryLoop         = true;
                    req.StartOfWindow = bundleHelper.StartOfWindow;
                    continue;
                }
                if (response.Status == PullStatus.Reset)
                {
                    retryLoop = true;
                    bundleHelper.Reset();
                    _progress.WriteVerbose("Server's bundle cache has expired.  Restarting pull...");
                    req.StartOfWindow = bundleHelper.StartOfWindow;
                    continue;
                }

                //bundleSizeFromResponse = response.BundleSize;
                if (loopCtr == 1)
                {
                    _progress.ProgressIndicator.Initialize();
                    if (req.StartOfWindow != 0)
                    {
                        string message = String.Format("Resuming pull operation at {0} received", GetHumanReadableByteSize(req.StartOfWindow));
                        _progress.WriteVerbose(message);
                    }
                }

                bundleHelper.WriteChunk(req.StartOfWindow, response.Chunk);
                req.StartOfWindow = req.StartOfWindow + response.Chunk.Length;
                req.ChunkSize     = response.ChunkSize;
                if (bundleSize == response.BundleSize && bundleSize != 0)
                {
                    _progress.ProgressIndicator.PercentCompleted = (int)((long)req.StartOfWindow * 100 / bundleSize);
                    string eta = CalculateEstimatedTimeRemaining(bundleSize, req.ChunkSize, req.StartOfWindow);
                    _progress.WriteStatus(string.Format("Receiving {0} {1}", GetHumanReadableByteSize(bundleSize), eta));
                }
                else
                {
                    // this is only useful when the bundle size is significantly large (like with a clone operation) such that
                    // the server takes a long time to create the bundle, and the bundleSize continues to rise as the chunks are received
                    bundleSize = response.BundleSize;
                    _progress.WriteStatus(string.Format("Preparing data on server (>{0})", GetHumanReadableByteSize(bundleSize)));
                }
                loopCtr++;
            } while (req.StartOfWindow < bundleSize || retryLoop);

            if (_repo.Unbundle(bundleHelper.BundlePath))
            {
                _progress.WriteMessage("Pull operation completed successfully");
                _progress.ProgressIndicator.Finish();
                _progress.WriteMessage("Finished Receiving");
                bundleHelper.Cleanup();
                var response = FinishPull(req.TransId);
                if (response == PullStatus.Reset)
                {
                    /* Calling Pull recursively to finish up another pull will mess up the ProgressIndicator.  This case is
                     * // rare enough that it's not worth trying to get the progress indicator working for a recursive Pull()
                     */
                    _progress.WriteMessage("Remote repo has changed.  Initiating additional pull operation");
                    GetCommonBaseHashesWithRemoteRepo(false);                     //Rebuild cache with current server information
                    return(Pull());
                }

                // REVIEW: I'm not sure why this was set to the server tip before, if we just pulled then won't our head
                // be the correct common base? Maybe not if a merge needs to happen,
                LastKnownCommonBases = new List <Revision>(_repo.BranchingHelper.GetBranches());
                return(true);
            }
            _progress.WriteError("Received all data but local unbundle operation failed or resulted in multiple heads!");
            _progress.ProgressIndicator.Finish();
            bundleHelper.Cleanup();
            errorMessage = "Pull operation failed";
            _progress.WriteError(errorMessage);
            throw new HgResumeOperationFailed(errorMessage);
        }
示例#15
0
 public void ValidateParameters(string method, HgResumeApiParameters request, byte[] contentToSend, int secondsBeforeTimeout)
 {
     Assert.That(method, Is.Not.Empty);
     Assert.That(secondsBeforeTimeout, Is.GreaterThan(0));
     if (method == "getRevisions")
     {
         Assert.That(request.RepoId, Is.Not.Empty);
         Assert.That(request.Quantity, Is.GreaterThan(0));
         Assert.That(request.StartOfWindow, Is.GreaterThan(-1));
     }
     else if (method == "pullBundleChunk")
     {
         Assert.That(request.RepoId, Is.Not.Empty);
         Assert.That(request.BaseHashes, Is.Not.Empty);
         Assert.That(request.ChunkSize, Is.GreaterThan(0));
         Assert.That(request.StartOfWindow, Is.GreaterThanOrEqualTo(0));
         Assert.That(request.TransId, Is.Not.Empty);
     }
     else if (method == "pushBundleChunk")
     {
         Assert.That(request.RepoId, Is.Not.Empty);
         Assert.That(request.BundleSize, Is.GreaterThan(0));
         Assert.That(request.StartOfWindow, Is.GreaterThanOrEqualTo(0));
         Assert.That(request.TransId, Is.Not.Empty);
         Assert.That(contentToSend.Length, Is.GreaterThan(0));
     }
     else if (method == "finishPushBundle")
     {
         Assert.That(request.TransId, Is.Not.Empty);
     }
     else if (method == "finishPullBundle")
     {
         Assert.That(request.TransId, Is.Not.Empty);
     }
     else
     {
         throw new HgResumeException(String.Format("unknown method '{0}'", method));
     }
 }
示例#16
0
        private PullResponse PullOneChunk(HgResumeApiParameters request)
        {
            var pullResponse = new PullResponse(PullStatus.Fail);

            try
            {
                HgResumeApiResponse response = _apiServer.Execute("pullBundleChunk", request, TimeoutInSeconds);
                if (response == null)
                {
                    _progress.WriteVerbose("API REQ: {0} Timeout", _apiServer.Url);
                    pullResponse.Status = PullStatus.Timeout;
                    return(pullResponse);
                }

                /* API returns the following HTTP codes:
                 * 200 OK (SUCCESS)
                 * 304 Not Modified (NOCHANGE)
                 * 400 Bad Request (FAIL, UNKNOWNID)
                 */
                _progress.WriteVerbose("API REQ: {0} RSP: {1} in {2}ms", _apiServer.Url, response.HttpStatus, response.ResponseTimeInMilliseconds);
                if (response.ResumableResponse.HasNote)
                {
                    _progress.WriteMessage(String.Format("Server replied: {0}", response.ResumableResponse.Note));
                }

                if (response.HttpStatus == HttpStatusCode.ServiceUnavailable && response.Content.Length > 0)
                {
                    var msg = String.Format("Server temporarily unavailable: {0}",
                                            Encoding.UTF8.GetString(response.Content));
                    _progress.WriteError(msg);
                    pullResponse.Status = PullStatus.NotAvailable;
                    return(pullResponse);
                }
                if (response.HttpStatus == HttpStatusCode.NotModified)
                {
                    pullResponse.Status = PullStatus.NoChange;
                    return(pullResponse);
                }
                if (response.HttpStatus == HttpStatusCode.Accepted)
                {
                    pullResponse.Status = PullStatus.InProgress;
                    return(pullResponse);
                }

                // chunk pulled OK
                if (response.HttpStatus == HttpStatusCode.OK)
                {
                    pullResponse.BundleSize = response.ResumableResponse.BundleSize;
                    pullResponse.Status     = PullStatus.OK;
                    pullResponse.ChunkSize  = CalculateChunkSize(request.ChunkSize, response.ResponseTimeInMilliseconds);

                    pullResponse.Chunk = response.Content;
                    return(pullResponse);
                }
                if (response.HttpStatus == HttpStatusCode.BadRequest && response.ResumableResponse.Status == "UNKNOWNID")
                {
                    // this is not implemented currently (feb 2012 cjh)
                    _progress.WriteError("The server {0} does not have the project '{1}'", _targetLabel, request.RepoId);
                    return(pullResponse);
                }
                if (response.HttpStatus == HttpStatusCode.BadRequest && response.ResumableResponse.Status == "RESET")
                {
                    pullResponse.Status = PullStatus.Reset;
                    return(pullResponse);
                }
                if (response.HttpStatus == HttpStatusCode.BadRequest)
                {
                    if (response.ResumableResponse.HasError)
                    {
                        if (response.ResumableResponse.Error == "invalid baseHash")
                        {
                            pullResponse.Status = PullStatus.InvalidHash;
                        }
                        else
                        {
                            _progress.WriteWarning("Server Error: {0}", response.ResumableResponse.Error);
                        }
                    }
                    return(pullResponse);
                }
                if (response.HttpStatus == HttpStatusCode.Unauthorized)
                {
                    _progress.WriteWarning(Resources.ksHgTransptUnauthorized);
                    pullResponse.Status = PullStatus.Unauthorized;
                }
                _progress.WriteWarning("Invalid Server Response '{0}'", response.HttpStatus);
                return(pullResponse);
            }
            catch (WebException e)
            {
                _progress.WriteWarning(String.Format("Pull data chunk failed: {0}", e.Message));
                return(pullResponse);
            }
        }
        public HgResumeApiResponse Execute(string method, HgResumeApiParameters parameters, byte[] contentToSend, int secondsBeforeTimeout)
        {
            string queryString = parameters.BuildQueryString();
            _urlExecuted = String.Format("{0}://{1}/api/v{2}/{3}?{4}", _url.Scheme, _url.Host, APIVERSION, method, queryString);
            var req = WebRequest.Create(_urlExecuted) as HttpWebRequest;
            req.UserAgent = String.Format("HgResume v{0}", APIVERSION);
            req.PreAuthenticate = true;
            if (!_url.UserInfo.Contains(":"))
            {
                throw new HgResumeException("Username or password were not supplied in custom location");
            }
            req.Credentials = new NetworkCredential(UserName, Password);
            req.Timeout = secondsBeforeTimeout * 1000; // timeout is in milliseconds
            if (contentToSend.Length == 0)
            {
                req.Method = WebRequestMethods.Http.Get;
            }
            else
            {
                req.Method = WebRequestMethods.Http.Post;
                req.ContentLength = contentToSend.Length;
                req.ContentType = "text/plain";  // i'm not sure this is really what we want.  The other possibility is "application/x-www-form-urlencoded"
                using (var reqStream = req.GetRequestStream())
                {
                    reqStream.Write(contentToSend, 0, contentToSend.Length);
                }
            }

            HttpWebResponse res;
            HgResumeApiResponse apiResponse;
            var stopwatch = new Stopwatch();
            stopwatch.Start();
            try
            {
                using (res = (HttpWebResponse)req.GetResponse())
                {
                    apiResponse = HandleResponse(res);
                }

            }
            catch(WebException e)
            {
                if (e.Status == WebExceptionStatus.ProtocolError)
                {
                    using (res = (HttpWebResponse)e.Response)
                    {
                        apiResponse = HandleResponse(res);
                    }
                }
                else if (e.Status == WebExceptionStatus.Timeout)
                {
                    apiResponse = null;
                }
                else
                {
                    throw; // throw for other types of network errors (see WebExceptionStatus for the full list of errors)
                }
            }
            finally
            {
                stopwatch.Stop();
            }
            if (apiResponse != null)
            {
                apiResponse.ResponseTimeInMilliseconds = stopwatch.ElapsedMilliseconds;
            }
            return apiResponse;
        }