using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.ComponentModel;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text;
using System.Runtime.InteropServices;
using ICSharpCode.SharpZipLib.Zip;
using ICSharpCode.SharpZipLib.Core;
using IReaper.FileData;
namespace IReaper.Running{
public delegate void FileDownloadFinishedEventHandler(CourseFileData File);
public unsafe class CourseFileWorker
{
public static event FileDownloadFinishedEventHandler OnFileDownloaded;
const int BUFSIZE = 20480;
#region static
//AsyncOperation
static HybridDictionary userStateToLifetime;
static ApplicationException appException;
[ThreadStatic]
static StringBuilder sb;
[ThreadStatic]
static byte[] buffer;
[ThreadStatic]
static byte* nbuffer;
static CourseFileWorker()
{
userStateToLifetime = new HybridDictionary();
appException = new ApplicationException("not accepted response");
}
#endregion
#region fields
SendOrPostCallback downloadCompletionCallback;
SendOrPostCallback downloadPausedCallback;
SendOrPostCallback downloadDoNothingCallback;
CourseFileData cData;
HttpWebRequest request;
HttpWebResponse response;
AutoResetEvent finishedEventHandler;
ManualResetEvent errorWaitEventHandler;
Stream stream = null;
FileStream tempFile = null;
public Thread thread;
int errorCount;
public AsyncOperation async;
#endregion
/// <summary>
/// CourseFile
/// </summary>
/// <param name="data"></param>
public CourseFileWorker(CourseFileData data)
{
this.cData = data;
data.RunningWorker = this;
this.errorWaitEventHandler = new ManualResetEvent(false);
this.errorCount = 0;
this.InitilizeSendorPostCallback();
}
/// <summary>
///
/// </summary>
public void PauseDownload()
{
//thread
if (this.thread == null || !this.thread.IsAlive)
{
return;
}
this.cData.Speed = 0;
this.cData.RunState = RunningStatue.StopRequest;
this.errorWaitEventHandler.Set();
if (request != null)
{
request.Abort();
}
else if (thread != null)
{
thread.Abort();
}
}
/// <summary>
///
/// </summary>
public void WorkEntry(AutoResetEvent FinishedEventHandler)
{
this.thread = Thread.CurrentThread;
this.finishedEventHandler = FinishedEventHandler;
this.finishedEventHandler.Reset();
//
if (this.cData.RunState == RunningStatue.StopRequest)
{
this.cData.RunState = RunningStatue.Stopped;
this.OnDownloadPause(null);
return;
}
if (sb == null)
{
sb = new StringBuilder();
buffer = new byte[BUFSIZE];
nbuffer = (byte*)Marshal.AllocHGlobal(BUFSIZE);
}
this.cData.Message = "";
/*
* iReaper
*/
try
{
//while (CheckIfContinueLoop())
do
{
try
{
switch (this.cData.Storage.LifetimeStatue)
{
case LifetimePosition.DownloadFinished://
//
this.cData.RunState = RunningStatue.Running;
this.ChangeFileExtension();
break;
case LifetimePosition.LocalFileCreated://
case LifetimePosition.Downloading://
//
this.InitilizeDownload(this.cData.RemotePath);
this.DownloadBody();
break;
case LifetimePosition.DownloadProcessed://
this.cData.RunState = RunningStatue.Running;
this.UnZipFile();
break;
case LifetimePosition.TaskCreated://
//
this.InitilizeDownload(this.cData.RemotePath);
//
this.CreateLocalFile();
//
this.DownloadBody();
break;
case LifetimePosition.UnCompressing:
this.cData.RunState = RunningStatue.Running;
this.UnZipFile();
break;
case LifetimePosition.ExtensionChange:
this.cData.RunState = RunningStatue.Running;
this.UnZipFile();
break;
case LifetimePosition.DownloadCreated:
throw new ApplicationException("Invalid statue");
}
this.cData.RunState = RunningStatue.Finished;
}
catch (Exception e)
{
if (e is ThreadAbortException)
//
{
this.cData.RunState = RunningStatue.StopRequest;
}
else if (this.cData.RunState != RunningStatue.StopRequest)
{
this.cData.RunState = RunningStatue.Error;
this.errorCount++;
}
}
finally
{
//change the runstate statue
if (this.cData.RunState == RunningStatue.StopRequest)
{
this.cData.RunState = RunningStatue.Stopped;
}
if (response != null)
{
response.Close();
}
}
} while (CheckIfContinueLoop());
}
finally
{
this.OnDownloadPause(null);
}
}
private bool CheckIfContinueLoop()
{
bool isContinue = false;
if (this.cData.RunState == RunningStatue.Error)
{
//check if errorWaitEvent is set
if (this.errorWaitEventHandler.WaitOne(10, true))
{
isContinue = false;
}
//check
isContinue = this.errorCount < IReaper.Properties.Settings.Default.Retry;
}
else
{
isContinue = this.cData.RunState != RunningStatue.Stopped &&
this.cData.RunState != RunningStatue.Error &&
this.cData.RunState != RunningStatue.Finished;
}
//,false
if (!isContinue)
return false;
else
{
//
sb.Remove(0, sb.Length);
sb.Append(Properties.StringResources.Retry)
.Append(":")
.Append(this.errorCount);
this.cData.Message = sb.ToString();
//
return !this.errorWaitEventHandler.WaitOne(IReaper.Properties.Settings.Default.RetryInterval * 1000, true) && true;
}
}
/// <summary>
///
///
/// </summary>
/// <param name="createTmpFile"></param>
private void InitilizeDownload(string path)
{
//
request = (HttpWebRequest)WebRequest.Create(path);
//0
if (this.cData.LifetimeStatue == LifetimePosition.Downloading)
this.request.AddRange((int)this.cData.ReadSize);
this.cData.LifetimeStatue = LifetimePosition.DownloadCreated;
this.cData.RunState = RunningStatue.Running;//
request.Timeout = Properties.Settings.Default.ConnectTimeout;
request.AllowAutoRedirect = true;
request.Proxy = WebRequest.DefaultWebProxy;
request.Pipelined = true;
//
this.response = (HttpWebResponse)request.GetResponse();
}
private void RegisterForCounter()
{
ThreadPool.QueueUserWorkItem(new WaitCallback(delegate(object obj)
{
HttpWebRequest pvRequest = WebRequest.Create(Uri.EscapeUriString("http://c.microsoft.com/trans_pixel.asp?source=www&TYPE=PV&p=china_msdn_webcast&uri=china/msdn/webcast/download.aspx")) as HttpWebRequest;
if (pvRequest != null)
{
try
{
pvRequest.Pipelined = true;
pvRequest.Proxy = WebRequest.DefaultWebProxy;
pvRequest.Referer = "http://www.microsoft.com/china/msdn/webcast/download.aspx";
pvRequest.UserAgent = "iReaper";
pvRequest.Method = WebRequestMethods.Http.Get;
pvRequest.GetResponse().Close();
}
catch { }
}
//connect ireaper counter
string logUrl = "http://www.ireaper.net/log.aspx?CourseID={0}&UserID={1}&Type={2}";
string logPath = null;
Guid userID = Properties.Settings.Default.UserID;
logPath = string.Format(logUrl, this.cData.Owner.Id, userID.ToString(), this.cData.FileType);
HttpWebRequest logRequest = WebRequest.Create(logPath) as HttpWebRequest;
if (logRequest != null)
{
try
{
logRequest.GetResponse().Close();
}
catch { }
}
}));
}
/// <summary>
/// response
/// </summary>
private void VerifyNetworkResponse()
{
//check empty
if (this.response == null)
return;
//check if the content length is minus one
if (this.response.ContentLength < 0)
{
//get response url
Uri responseUri = this.response.ResponseUri;
if (responseUri == null)
throw appException;
else//valid response
{
if (responseUri.Scheme.ToLower() == "http")
{
this.InitilizeDownload(responseUri.AbsoluteUri);
if (this.response.ContentLength < 0)
{
this.response.Close();
throw appException;
}
}
else
throw appException;
}
}
}
/// <summary>
///
/// </summary>
private unsafe void CreateLocalFile()
{
//reponse
VerifyNetworkResponse();
//
this.cData.TotalSize = response.ContentLength;
////
Uri responseUri = response.ResponseUri;
////
string[] segments = responseUri.Segments;
if (segments.Length == 0)
throw new ApplicationException("Can't get file name from response");
string fileName = segments[segments.Length - 1];
this.cData.FileName = fileName;
//
if (!Directory.Exists(this.cData.RootPath))
{
Directory.CreateDirectory(this.cData.RootPath);
}
//
this.cData.FileName = fileName + ".wb";
//
FileStream file = null;
file = new FileStream(this.cData.FilePath, FileMode.Create);
//
file.SetLength(this.cData.TotalSize);
file.Close();
this.cData.LifetimeStatue = LifetimePosition.LocalFileCreated;
//
}
/// <summary>
///
/// </summary>
private void DownloadBody()
{
if (!request.HaveResponse)
{ return; }
//
try
{
//
tempFile = new FileStream(this.cData.FilePath,
FileMode.Open,
FileAccess.Write,
FileShare.Read,
BUFSIZE,
FileOptions.SequentialScan); //
tempFile.Position = this.cData.ReadSize;
//buffer
//
stream = response.GetResponseStream();
stream.ReadTimeout = IReaper.Properties.Settings.Default.ReadTimeout;
//
int iRead = 0;
//
//set flag
long interval = 0;
int* iWrite = stackalloc int[1];
this.cData.LifetimeStatue = LifetimePosition.Downloading;
this.cData.RunState = RunningStatue.Running;
System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
watch.Start();
while (this.cData.ReadSize < this.cData.TotalSize)
{
Thread.BeginCriticalRegion();
// Read and write
iRead = stream.Read(buffer, 0, BUFSIZE);
tempFile.Write(buffer, 0, iRead);
Thread.MemoryBarrier();
// mark
this.cData.ReadSize += iRead;
interval += iRead;
long elapse = watch.ElapsedMilliseconds;
if (elapse > 1000)
{
this.cData.Speed = (double)interval / watch.ElapsedMilliseconds;
watch.Reset();
watch.Start();
interval = 0;
this.cData.FlushToScreen();
}
this.errorCount = 0;
Thread.EndCriticalRegion();
}
watch.Stop();
//
this.cData.LifetimeStatue = LifetimePosition.DownloadFinished;
}
finally
{
if (tempFile != null)
{
//
this.cData.ReadSize = tempFile.Position;
tempFile.Close();
}
}
//
this.ChangeFileExtension();
}
/// <summary>
///
/// </summary>
private void ChangeFileExtension()
{
//
string newPath = this.cData.FileName.Remove(this.cData.FileName.Length - 3, 3);
string oldPath = this.cData.FileName;
//
File.Delete(Path.Combine(this.cData.RootPath, newPath));
//
File.Move(this.cData.FilePath, Path.Combine(this.cData.RootPath, newPath));
//
File.Delete(this.cData.FilePath);
//
this.cData.FileName = newPath;
//
this.cData.LifetimeStatue = LifetimePosition.ExtensionChange;
//
this.UnZipFile();
}
#region
/// <summary>
///
/// </summary>
private void UnZipFile()
{
//
ZipFile zFile = null;
try
{
if (!this.cData.FileName.ToLower().EndsWith(".zip"))
{
return;
}
//store the orignal zip file path.
string zipPath = this.cData.FilePath;
zFile = new ZipFile(zipPath);
//
bool isValidated = zFile.TestArchive(true);
//
if (isValidated)
{
//execute unzip
if (zFile.Count == 1 && //
zFile[0].IsFile && //
Properties.Settings.Default.AutoUnzip //
)
{
//
this.cData.LifetimeStatue = LifetimePosition.UnCompressing;
//ZipEntry
ZipEntry entry = zFile[0];
//
this.cData.FileName = entry.Name;
//
FileStream oStream = null;
//
Stream iStream = zFile.GetInputStream(entry);
try
{
oStream = File.Create(this.cData.FilePath, 1024, FileOptions.SequentialScan | FileOptions.WriteThrough);
int iRead = 0;
while ((iRead = iStream.Read(buffer, 0, BUFSIZE)) > 0)
{
oStream.Write(buffer, 0, iRead);
}
}
finally
{
if (oStream != null)
oStream.Close();
//zip
if (zFile != null)
{
zFile.Close();
}
File.Delete(zipPath);
}
}
}
else
{
throw new ApplicationException(Properties.StringResources.ZipError);//Zip File is broken.
}
}
catch
{
//
this.errorWaitEventHandler.Set();
if (zFile != null)
{
zFile.Close();
}
throw;
}
finally
{
this.onFinished();
}
}
private void onFinished()
{
this.cData.LifetimeStatue = LifetimePosition.DownloadProcessed;
this.cData.RunState = RunningStatue.Finished;
this.RegisterForCounter();
if (OnFileDownloaded != null)
{
OnFileDownloaded(this.cData);
}
}
#endregion
#region CallBack
private void InitilizeSendorPostCallback()
{
this.downloadCompletionCallback = new SendOrPostCallback(this.OnTaskCompletion);
this.downloadPausedCallback = new SendOrPostCallback(this.OnDownloadPause);
this.downloadDoNothingCallback = new SendOrPostCallback(this.OnDownloadProgress);
}
/// <summary>
///
/// </summary>
/// <param name="obj"></param>
private void OnTaskCompletion(object obj)
{
//TODO:
//,
this.cData.LifetimeStatue = LifetimePosition.DownloadProcessed;
}
/// <summary>
/// async
///
/// </summary>
/// <param name="obj"></param>
private void OnDownloadPause(object obj)
{
//make sure that reponse is closeed.
if (response != null)
response.Close();
//RunningWorker
this.cData.RunningWorker = null;
//flush to storage.
this.cData.Storage.Flush();
//
this.finishedEventHandler.Set();
//TODO:
}
/// async
/// </summary>
/// <param name="obj"></param>
private void OnDownloadProgress(object obj)
{
//do nothing
// this.cData.OnStatueChange(null);
}
#endregion
}
}
|