internal string UploadFileToVF(string zipFile, string swiftFilename, UploadPercent callback)
        {
            Login();
            string fileUploadInfo = SendGetRequest(ServerUrl + "/testbench/client_upload_url?filename=" + HttpUtility.UrlEncode(swiftFilename));
            if (fileUploadInfo == null)
            {
                // job creation failed
                //job.Status = Job.StatusEnum.Failed;
                return null;
            }
            int maxProgress = 0;
            object maxProgressLock = new object();
            dynamic fileUploadInfoJson = Newtonsoft.Json.Linq.JObject.Parse(fileUploadInfo);
            string fileUploadUrl = fileUploadInfoJson["url"].Value;
            try
            {
                try
                {
                    using (CookieAwareWebClient webClient = new CookieAwareWebClient())
                    {
                        webClient.cookies = this.AuthCookies;
                        webClient.Timeout = HTTP_WEB_TIMEOUT_BASE;
                        Semaphore progress = new Semaphore(0, int.MaxValue);
                        Semaphore completed = new Semaphore(0, 1);
                        UploadFileCompletedEventArgs completedData = null;
                        webClient.UploadFileCompleted += delegate(object sender, UploadFileCompletedEventArgs e)
                        {
                            completedData = e;
                            completed.Release();
                        };
                        webClient.UploadProgressChanged += delegate(object sender, UploadProgressChangedEventArgs e)
                        {
                            lock (maxProgressLock)
                            {
                                int percentProgress = (int)(((double)e.BytesSent / e.TotalBytesToSend) * 99.0);
                                if (percentProgress > maxProgress)
                                {
                                    // e.ProgressPercentage is not correct, as (almost) all the time is spent uploading
                                    maxProgress = percentProgress;
                                    callback(maxProgress);
                                }
                            }
                            progress.Release();
                        };
                        webClient.UploadFileAsync(new Uri(fileUploadUrl), "PUT", zipFile);
                        while (true)
                        {
                            WaitHandle[] handles = new WaitHandle[] { completed, progress };
                            int timeout = HTTP_WEB_TIMEOUT_BASE;
                            lock (maxProgressLock)
                            {
                                // Workaround for Fiddler proxy: Fiddler accepts all the data at once, then uploads to VF
                                if (maxProgress > 98)
                                {
                                    handles = new WaitHandle[] { completed };
                                    timeout = HTTP_WEB_REQUEST_TIMEOUT;
                                }
                            }
                            int handle = WaitHandle.WaitAny(handles, timeout);
                            if (handle == 0)
                            {
                                lock (maxProgressLock)
                                {
                                    maxProgress = 100;
                                    callback(maxProgress);
                                }
                                break;
                            }
                            if (handle == System.Threading.WaitHandle.WaitTimeout)
                            {
                                webClient.CancelAsync();
                                throw new WebException("Connection timed out");
                            }
                            if (handle != 1)
                                throw new Exception();
                        }

                        Trace.TraceInformation("Upload to S3 " + Encoding.UTF8.GetString(completedData.Result));
                        return fileUploadUrl;
                    }
                }
                catch (System.Reflection.TargetInvocationException ex)
                {
                    throw ex.InnerException;
                }

            }
            catch (WebException ex)
            {
                Trace.TraceError("Error uploading to {0}. Exception follows", fileUploadUrl);
                Trace.TraceError(ex.ToString());
            }
            return null;
        }