-
Notifications
You must be signed in to change notification settings - Fork 0
/
ExternalResourceFetcher.cs
153 lines (135 loc) · 3.73 KB
/
ExternalResourceFetcher.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
using System;
using System.Collections.Generic;
using System.Net;
using System.IO.Compression;
using System.IO;
using System.Web;
using System.Web.Caching;
using System.Threading;
namespace netzlib
{
internal class ExternalResourceFetcher
{
class RequestState
{
public WebRequest Request { get; set; }
public ExternalResource Resource { get; set; }
}
class WatchedDirectory
{
public FileSystemWatcher Watcher { get; set; }
public readonly Dictionary<string, ExternalResource> Resources =
new Dictionary<string, ExternalResource>();
}
private readonly Dictionary<string, WatchedDirectory> directories =
new Dictionary<string, WatchedDirectory>();
public void Fetch(ExternalResource resource)
{
if (resource != null)
{
var wr = WebRequest.Create(resource.Uri);
var rs = new RequestState { Request = wr, Resource = resource };
var ar = wr.BeginGetResponse(ResponseCallback, rs);
ThreadPool.RegisterWaitForSingleObject(ar.AsyncWaitHandle, TimeoutCallback,
rs, Settings.Default.ExternalResourceTimeout * 1000, true);
}
}
private void ResponseCallback(IAsyncResult ar)
{
var rs = ar.AsyncState as RequestState;
try
{
var response = rs.Request.EndGetResponse(ar);
var stream = response.GetResponseStream();
var enc = response.Headers["Content-Encoding"];
if (enc != null && enc.Contains("gzip"))
{
stream = new GZipStream(stream, CompressionMode.Decompress);
}
using (var reader = new StreamReader(stream))
{
rs.Resource.Content = reader.ReadToEnd();
}
}
catch (Exception ex)
{
rs.Resource.Content = string.Format("// {0}: {1}", rs.Request.RequestUri, ex.Message);
}
finally
{
CompleteFetch(rs.Resource);
}
}
private void TimeoutCallback(object state, bool timedOut)
{
if (timedOut)
{
var rs = state as RequestState;
rs.Request.Abort();
rs.Resource.Content = string.Format("// {0}: timed out", rs.Resource.Uri);
CompleteFetch(rs.Resource);
}
}
private void CompleteFetch(ExternalResource resource)
{
resource.Loaded.Set();
if(resource.Uri.IsFile)
{
// resource is a local file, setup a file watch
if (Settings.Default.WatchFiles)
{
WatchFile(resource);
}
}
else
{
// resource is a remote url, schedule a refetch
if (Settings.Default.ExternalResourceRefreshInterval > 0)
{
HttpRuntime.Cache.Add(Guid.NewGuid().ToString(), resource, null,
DateTime.Now.AddSeconds(Settings.Default.ExternalResourceRefreshInterval),
Cache.NoSlidingExpiration, CacheItemPriority.Default, Refetch);
}
}
}
private void WatchFile(ExternalResource resource)
{
var file = new FileInfo(resource.Uri.LocalPath);
lock (directories)
{
if (!directories.ContainsKey(file.DirectoryName))
{
var fsw = new FileSystemWatcher(file.DirectoryName);
fsw.NotifyFilter = NotifyFilters.LastWrite;
fsw.Changed += OnFileChanged;
directories.Add(file.DirectoryName, new WatchedDirectory { Watcher = fsw });
}
var dir = directories[file.DirectoryName];
if (!dir.Resources.ContainsKey(file.Name))
{
dir.Resources.Add(file.Name, resource);
dir.Watcher.EnableRaisingEvents = true;
}
}
}
private void Refetch(string key, object value, CacheItemRemovedReason reason)
{
Fetch(value as ExternalResource);
}
private void OnFileChanged(object sender, FileSystemEventArgs e)
{
if (e.ChangeType == WatcherChangeTypes.Changed)
{
var file = new FileInfo(e.FullPath);
if (directories.ContainsKey(file.DirectoryName))
{
var dir = directories[file.DirectoryName];
if (dir.Resources.ContainsKey(file.Name))
{
Fetch(dir.Resources[file.Name]);
}
}
}
}
}
}