-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathReverseProxy.cs
207 lines (177 loc) · 7.09 KB
/
ReverseProxy.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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Net;
using System.IO;
using System.Text.RegularExpressions;
using System.Text;
namespace ReverseProxy
{
public class ReverseProxy : IHttpHandler
{
private static ReverseProxyConfiguration _configuration;
public ReverseProxyConfiguration Configuration
{
get
{
if (_configuration == null)
_configuration = ReverseProxyConfiguration.LoadSettings();
return _configuration;
}
}
bool IHttpHandler.IsReusable
{
get { return true; }
}
void IHttpHandler.ProcessRequest(HttpContext context)
{
MappingElement mapping = null;
Uri outgoingUri;
HttpWebResponse response;
//Check Configuration
if (Configuration == null)
throw new Exception("Unable to load Configuration");
// Search for a matching mapping
foreach (MappingElement map in Configuration.Mappings)
{
if (MatchUri(context.Request.Url, map.SourceURI, map.SourceRegexMatching))
{
mapping = map;
break;
}
}
// Return 404 if can't find mapping
if (mapping == null)
{
Utility.Return404(context);
return;
}
// Create destination mapping, this includes GET query as well
outgoingUri = CreateRemoteUri(mapping, GenerateTokensFromRequest(context.Request));
HttpWebRequest outgoing = (HttpWebRequest)WebRequest.Create(outgoingUri);
// Credentials
// TODO: Impliment Credential pass-through
//request.Credentials = CredentialCache.DefaultCredentials;
// Headers
Utility.CopyHeaders(context.Request, outgoing);
// Copy POST Data
if (mapping.SourceIncludePost && (context.Request.RequestType == "POST"))
{
outgoing.ContentLength = context.Request.ContentLength;
Utility.CopyStream(context.Request.InputStream, outgoing.GetRequestStream());
}
try
{
response = (HttpWebResponse)outgoing.GetResponse();
}
catch (WebException ex)
{
Utility.Return500(context, ex);
return;
}
Stream receiveStream = response.GetResponseStream();
//Copy some headers, not too many since I'm not against hiding internal details ;)
//TODO: copy cookies?
context.Response.ContentType = response.ContentType;
// Copy Content Encoding
//context.Response.ContentEncoding = Encoding.
//context.Response.
// Do any parsing of HTML (or anything with URLs) here
if (!string.IsNullOrEmpty(mapping.RewriteContent)
&& ((response.ContentType.ToLower().IndexOf("html") >= 0) || (response.ContentType.ToLower().IndexOf("javascript") >= 0))
)
{
string sResp = Utility.ConvertStream(receiveStream);
sResp = RewriteContent(sResp, mapping.RewriteContent);
context.Response.Write(sResp);
}
else
{
// Output without rewriting, this will offer the best performance and lowest memory useage.
Utility.CopyStream(receiveStream, context.Response.OutputStream);
}
response.Close();
context.Response.End();
}
private string RewriteContent(string content, string rewriteGroup)
{
//First get rewrite group
RewriteGroup rewriteGrp = Configuration.RewriteGroups.Get(rewriteGroup);
if (rewriteGrp == null)
throw new ArgumentOutOfRangeException("rewriteGroup", rewriteGroup, "Unknown Rewrite Group");
foreach (Rewrite rw in rewriteGrp)
{
if (rw.EnableRegEx)
{
//TODO: Regex rewriting has not been implimented
throw new NotImplementedException("Regex rewriting has not been implimented.");
}
else
{
content = content.Replace(rw.Match, rw.Replace);
}
}
return content;
}
private Uri CreateRemoteUri(MappingElement mapping, IDictionary<string, string> tokens)
{
Uri remoteUri;
string uri = ReplaceTokens(mapping.TargetURI, tokens);
remoteUri = new Uri(uri);
return remoteUri;
}
private bool MatchUri(Uri sourceUri, string targetUri, bool useRegex)
{
if (!useRegex)
{
return sourceUri.AbsolutePath.Equals(targetUri, StringComparison.OrdinalIgnoreCase);
}
else
{
Regex regex = new Regex(targetUri);
return regex.IsMatch(sourceUri.AbsolutePath);
}
}
private static string ReplaceTokens(string text, IDictionary<string, string> tokens)
{
Regex re = new Regex("#.*?#", RegexOptions.Compiled | RegexOptions.Singleline);
StringBuilder sb = new StringBuilder(text);
string replace;
foreach (Match m in re.Matches(text)) // assuming text is the text to search
{
// Replace any matching tokens
if (tokens.TryGetValue(m.Value, out replace))
sb.Replace(m.Value, replace);
}
return sb.ToString();
}
private static IDictionary<string, string> GenerateTokensFromRequest(HttpRequest request)
{
IDictionary<string, string> tokens = new Dictionary<string, string>();
string path;
string page;
GetDetailsFromPath(request.FilePath, out path, out page);
tokens.Add("#host#", request.Url.Host);
tokens.Add("#port#", request.Url.Port.ToString());
tokens.Add("#path#", path);
tokens.Add("#page#", page);
tokens.Add("#query#", request.PathInfo + request.Url.Query);
return tokens;
}
private static void GetDetailsFromPath(string fullPath, out string path, out string page)
{
int lastPos = fullPath.LastIndexOf('/') +1;
if (lastPos > 0)
{
path = fullPath.Substring(0, lastPos);
page = fullPath.Substring(lastPos);
}
else
{
path = string.Empty;
page = fullPath;
}
}
}
}