Mercurial > hg > ucis.core
annotate Net/HTTP.cs @ 54:ba4e2cb031e0
Added general purpose tar archive reader class
author | Ivo Smits <Ivo@UCIS.nl> |
---|---|
date | Wed, 02 Oct 2013 21:17:30 +0200 |
parents | 8df7f4dc5615 |
children | 50d4aed66c67 |
rev | line source |
---|---|
0 | 1 ???using System; |
2 using System.Collections.Generic; | |
3 using System.IO; | |
3 | 4 using System.Net; |
10 | 5 using System.Net.Security; |
3 | 6 using System.Net.Sockets; |
10 | 7 using System.Security.Cryptography.X509Certificates; |
0 | 8 using System.Text; |
6 | 9 using UCIS.Net; |
3 | 10 using UCIS.Util; |
11 using HTTPHeader = System.Collections.Generic.KeyValuePair<string, string>; | |
0 | 12 |
13 namespace UCIS.Net.HTTP { | |
3 | 14 public class HTTPServer : TCPServer.IModule, IDisposable { |
15 public IHTTPContentProvider ContentProvider { get; set; } | |
16 public Boolean ServeFlashPolicyFile { get; set; } | |
10 | 17 public X509Certificate SSLCertificate { get; set; } |
3 | 18 private Socket Listener = null; |
0 | 19 |
3 | 20 public HTTPServer() { } |
0 | 21 |
3 | 22 public void Listen(int port) { |
23 Listen(new IPEndPoint(IPAddress.Any, port)); | |
0 | 24 } |
25 | |
3 | 26 public void Listen(EndPoint localep) { |
27 if (Listener != null) throw new InvalidOperationException("A listener exists"); | |
28 Listener = new Socket(localep.AddressFamily, SocketType.Stream, ProtocolType.Tcp); | |
29 Listener.Bind(localep); | |
30 Listener.Listen(5); | |
31 Listener.BeginAccept(AcceptCallback, null); | |
0 | 32 } |
33 | |
3 | 34 private void AcceptCallback(IAsyncResult ar) { |
35 try { | |
36 Socket socket = Listener.EndAccept(ar); | |
10 | 37 if (SSLCertificate != null) { |
38 SslStream ssl = new SslStream(new NetworkStream(socket, true)); | |
39 ssl.BeginAuthenticateAsServer(SSLCertificate, SslAuthenticationCallback, new Object[] { socket, ssl }); | |
40 } else { | |
41 new HTTPContext(this, socket); | |
42 } | |
3 | 43 } catch (Exception) { } |
44 try { | |
45 Listener.BeginAccept(AcceptCallback, null); | |
46 } catch (Exception) { } | |
0 | 47 } |
48 | |
10 | 49 private void SslAuthenticationCallback(IAsyncResult ar) { |
50 Object[] args = (Object[])ar.AsyncState; | |
51 Socket socket = (Socket)args[0]; | |
52 SslStream ssl = (SslStream)args[1]; | |
53 try { | |
54 ssl.EndAuthenticateAsServer(ar); | |
55 new HTTPContext(this, ssl, socket); | |
56 } catch (Exception) { } | |
57 } | |
58 | |
3 | 59 public void Dispose() { |
60 if (Listener != null) Listener.Close(); | |
0 | 61 } |
62 | |
45
8df7f4dc5615
HTTP Server: enable KeepAlive option on TCP sockets
Ivo Smits <Ivo@UCIS.nl>
parents:
16
diff
changeset
|
63 public void HandleClient(Socket client) { |
8df7f4dc5615
HTTP Server: enable KeepAlive option on TCP sockets
Ivo Smits <Ivo@UCIS.nl>
parents:
16
diff
changeset
|
64 new HTTPContext(this, client); |
8df7f4dc5615
HTTP Server: enable KeepAlive option on TCP sockets
Ivo Smits <Ivo@UCIS.nl>
parents:
16
diff
changeset
|
65 } |
8df7f4dc5615
HTTP Server: enable KeepAlive option on TCP sockets
Ivo Smits <Ivo@UCIS.nl>
parents:
16
diff
changeset
|
66 |
3 | 67 bool TCPServer.IModule.Accept(TCPStream stream) { |
68 new HTTPContext(this, stream); | |
69 return false; | |
0 | 70 } |
71 } | |
72 | |
73 public class HTTPContext { | |
3 | 74 public HTTPServer Server { get; private set; } |
75 public EndPoint LocalEndPoint { get; private set; } | |
76 public EndPoint RemoteEndPoint { get; private set; } | |
77 | |
78 public String RequestMethod { get; private set; } | |
79 public String RequestPath { get; private set; } | |
80 public String RequestQuery { get; private set; } | |
81 public int HTTPVersion { get; set; } | |
82 | |
83 public Socket Socket { get; private set; } | |
84 public Boolean SuppressStandardHeaders { get; set; } | |
6 | 85 public TCPStream TCPStream { get; private set; } |
3 | 86 |
87 private StreamWriter Writer; | |
6 | 88 private PrebufferingStream Reader; |
3 | 89 private List<HTTPHeader> RequestHeaders; |
90 private HTTPConnectionState State = HTTPConnectionState.Starting; | |
91 | |
10 | 92 |
3 | 93 private enum HTTPConnectionState { |
94 Starting = 0, | |
95 ReceivingRequest = 1, | |
96 ProcessingRequest = 2, | |
97 SendingHeaders = 3, | |
98 SendingContent = 4, | |
99 Closed = 5, | |
100 } | |
101 | |
10 | 102 public HTTPContext(HTTPServer server, TCPStream stream) : this(server, stream, stream.Socket) { } |
103 public HTTPContext(HTTPServer server, Socket socket) : this(server, null, socket) { } | |
104 public HTTPContext(HTTPServer server, Stream stream, Socket socket) { | |
3 | 105 this.Server = server; |
10 | 106 this.Socket = socket; |
107 if (socket != null) { | |
108 this.LocalEndPoint = socket.LocalEndPoint; | |
109 this.RemoteEndPoint = socket.RemoteEndPoint; | |
45
8df7f4dc5615
HTTP Server: enable KeepAlive option on TCP sockets
Ivo Smits <Ivo@UCIS.nl>
parents:
16
diff
changeset
|
110 if (socket.ProtocolType == ProtocolType.Tcp) socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); |
10 | 111 if (stream == null) stream = new NetworkStream(socket, true); |
112 } | |
6 | 113 Init(stream); |
3 | 114 } |
115 | |
6 | 116 private void Init(Stream Stream) { |
3 | 117 Writer = new StreamWriter(Stream, Encoding.ASCII); |
118 Writer.NewLine = "\r\n"; | |
119 Writer.AutoFlush = true; | |
6 | 120 Reader = new PrebufferingStream(Stream); |
121 Reader.BeginPrebuffering(PrebufferCallback, null); | |
3 | 122 } |
123 | |
124 private String ReadLine() { | |
125 StringBuilder s = new StringBuilder(); | |
126 while (true) { | |
6 | 127 int b = Reader.ReadByte(); |
3 | 128 if (b == -1) { |
4
9e2e6433f57a
Small fix for HTTP Server - Flash Cross Domain Policy File support
Ivo Smits <Ivo@UCIS.nl>
parents:
3
diff
changeset
|
129 if (s.Length == 0) return null; |
3 | 130 break; |
131 } else if (b == 13) { | |
4
9e2e6433f57a
Small fix for HTTP Server - Flash Cross Domain Policy File support
Ivo Smits <Ivo@UCIS.nl>
parents:
3
diff
changeset
|
132 } else if (b == 10 || b == 0) { |
3 | 133 break; |
134 } else { | |
135 s.Append((Char)b); | |
136 } | |
137 } | |
138 return s.ToString(); | |
139 } | |
140 | |
6 | 141 private void PrebufferCallback(IAsyncResult ar) { |
3 | 142 State = HTTPConnectionState.ReceivingRequest; |
143 try { | |
6 | 144 Reader.EndPrebuffering(ar); |
3 | 145 String line = ReadLine(); |
146 if (line == null) { | |
147 Close(); | |
148 return; | |
149 } | |
150 if (Server.ServeFlashPolicyFile && line[0] == '<') { //<policy-file-request/> | |
151 Writer.WriteLine("<cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"*\" /></cross-domain-policy>"); | |
6 | 152 Reader.WriteByte(0); |
3 | 153 Close(); |
154 return; | |
155 } | |
156 String[] request = line.Split(' '); | |
157 if (request.Length != 3) goto SendError400AndClose; | |
158 RequestMethod = request[0]; | |
159 String RequestAddress = request[1]; | |
160 switch (request[2]) { | |
161 case "HTTP/1.0": HTTPVersion = 10; break; | |
162 case "HTTP/1.1": HTTPVersion = 11; break; | |
163 default: goto SendError400AndClose; | |
164 } | |
165 request = RequestAddress.Split(new Char[] { '?' }); | |
166 RequestPath = Uri.UnescapeDataString(request[0]); | |
167 RequestQuery = request.Length > 1 ? request[1] : null; | |
168 RequestHeaders = new List<HTTPHeader>(); | |
169 while (true) { | |
170 line = ReadLine(); | |
171 if (line == null) goto SendError400AndClose; | |
172 if (line.Length == 0) break; | |
173 request = line.Split(new String[] { ": " }, 2, StringSplitOptions.None); | |
174 if (request.Length != 2) goto SendError400AndClose; | |
175 RequestHeaders.Add(new HTTPHeader(request[0], request[1])); | |
176 } | |
177 IHTTPContentProvider content = Server.ContentProvider; | |
178 if (content == null) goto SendError500AndClose; | |
179 State = HTTPConnectionState.ProcessingRequest; | |
180 content.ServeRequest(this); | |
181 } catch (Exception ex) { | |
182 Console.Error.WriteLine(ex); | |
183 switch (State) { | |
184 case HTTPConnectionState.ProcessingRequest: goto SendError500AndClose; | |
185 default: | |
186 Close(); | |
187 break; | |
188 } | |
189 } | |
190 return; | |
191 | |
54
ba4e2cb031e0
Added general purpose tar archive reader class
Ivo Smits <Ivo@UCIS.nl>
parents:
45
diff
changeset
|
192 SendError400AndClose: |
7
4b78cc5f116b
Fixes and improvements (some untested)
Ivo Smits <Ivo@UCIS.nl>
parents:
6
diff
changeset
|
193 State = HTTPConnectionState.ProcessingRequest; |
3 | 194 SendErrorAndClose(400); |
195 return; | |
54
ba4e2cb031e0
Added general purpose tar archive reader class
Ivo Smits <Ivo@UCIS.nl>
parents:
45
diff
changeset
|
196 SendError500AndClose: |
7
4b78cc5f116b
Fixes and improvements (some untested)
Ivo Smits <Ivo@UCIS.nl>
parents:
6
diff
changeset
|
197 State = HTTPConnectionState.ProcessingRequest; |
3 | 198 SendErrorAndClose(500); |
199 return; | |
200 } | |
201 | |
202 public String GetRequestHeader(String name) { | |
203 if (State != HTTPConnectionState.ProcessingRequest && State != HTTPConnectionState.SendingHeaders && State != HTTPConnectionState.SendingContent) throw new InvalidOperationException(); | |
204 foreach (HTTPHeader h in RequestHeaders) { | |
205 if (name.Equals(h.Key, StringComparison.OrdinalIgnoreCase)) return h.Value; | |
206 } | |
207 return null; | |
208 } | |
209 public String[] GetRequestHeaders(String name) { | |
210 if (State != HTTPConnectionState.ProcessingRequest && State != HTTPConnectionState.SendingHeaders && State != HTTPConnectionState.SendingContent) throw new InvalidOperationException(); | |
211 String[] items = new String[0]; | |
212 foreach (HTTPHeader h in RequestHeaders) { | |
213 if (name.Equals(h.Key, StringComparison.OrdinalIgnoreCase)) ArrayUtil.Add(ref items, h.Value); | |
214 } | |
215 return items; | |
216 } | |
217 | |
218 public void SendErrorAndClose(int state) { | |
219 try { | |
220 SendStatus(state); | |
221 GetResponseStream(); | |
222 } catch (Exception ex) { | |
223 Console.Error.WriteLine(ex); | |
224 } | |
225 Close(); | |
226 } | |
227 | |
228 public void SendStatus(int code) { | |
229 String message; | |
230 switch (code) { | |
231 case 101: message = "Switching Protocols"; break; | |
232 case 200: message = "OK"; break; | |
233 case 400: message = "Bad Request"; break; | |
234 case 404: message = "Not Found"; break; | |
235 case 500: message = "Internal Server Error"; break; | |
236 default: message = "Unknown Status"; break; | |
237 } | |
238 SendStatus(code, message); | |
239 } | |
240 public void SendStatus(int code, String message) { | |
241 if (State != HTTPConnectionState.ProcessingRequest) throw new InvalidOperationException(); | |
242 StringBuilder sb = new StringBuilder(); | |
243 sb.Append("HTTP/"); | |
244 switch (HTTPVersion) { | |
245 case 10: sb.Append("1.0"); break; | |
246 case 11: sb.Append("1.1"); break; | |
247 default: throw new ArgumentException("The HTTP version is not supported", "HTTPVersion"); | |
248 } | |
249 sb.Append(" "); | |
250 sb.Append(code); | |
251 sb.Append(" "); | |
252 sb.Append(message); | |
253 Writer.WriteLine(sb.ToString()); | |
254 State = HTTPConnectionState.SendingHeaders; | |
255 if (!SuppressStandardHeaders) { | |
256 SendHeader("Expires", "Expires: Sun, 1 Jan 2000 00:00:00 GMT"); | |
257 SendHeader("Cache-Control", "no-store, no-cache, must-revalidate"); | |
258 SendHeader("Cache-Control", "post-check=0, pre-check=0"); | |
259 SendHeader("Pragma", "no-cache"); | |
260 SendHeader("Server", "UCIS Webserver"); | |
261 SendHeader("Connection", "Close"); | |
262 } | |
263 } | |
264 public void SendHeader(String name, String value) { | |
265 if (State == HTTPConnectionState.ProcessingRequest) SendStatus(200); | |
266 if (State != HTTPConnectionState.SendingHeaders) throw new InvalidOperationException(); | |
267 Writer.WriteLine(name + ": " + value); | |
268 } | |
269 public Stream GetResponseStream() { | |
270 if (State == HTTPConnectionState.ProcessingRequest) SendStatus(200); | |
271 if (State == HTTPConnectionState.SendingHeaders) { | |
272 Writer.WriteLine(); | |
273 State = HTTPConnectionState.SendingContent; | |
274 } | |
275 if (State != HTTPConnectionState.SendingContent) throw new InvalidOperationException(); | |
6 | 276 return Reader; |
3 | 277 } |
278 | |
279 public void Close() { | |
280 if (State == HTTPConnectionState.Closed) return; | |
6 | 281 Reader.Close(); |
3 | 282 State = HTTPConnectionState.Closed; |
283 } | |
0 | 284 } |
285 | |
3 | 286 public interface IHTTPContentProvider { |
287 void ServeRequest(HTTPContext context); | |
288 } | |
12 | 289 public delegate void HTTPContentProviderDelegate(HTTPContext context); |
290 public class HTTPContentProviderFunction : IHTTPContentProvider { | |
291 public HTTPContentProviderDelegate Handler { get; private set; } | |
292 public HTTPContentProviderFunction(HTTPContentProviderDelegate handler) { | |
293 this.Handler = handler; | |
294 } | |
295 public void ServeRequest(HTTPContext context) { | |
296 Handler(context); | |
297 } | |
298 } | |
3 | 299 public class HTTPPathSelector : IHTTPContentProvider { |
300 private List<KeyValuePair<String, IHTTPContentProvider>> Prefixes; | |
301 private StringComparison PrefixComparison; | |
302 public HTTPPathSelector() : this(false) { } | |
303 public HTTPPathSelector(Boolean caseSensitive) { | |
304 Prefixes = new List<KeyValuePair<string, IHTTPContentProvider>>(); | |
305 PrefixComparison = caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; | |
306 } | |
307 public void AddPrefix(String prefix, IHTTPContentProvider contentProvider) { | |
308 Prefixes.Add(new KeyValuePair<string, IHTTPContentProvider>(prefix, contentProvider)); | |
309 } | |
310 public void DeletePrefix(String prefix) { | |
311 Prefixes.RemoveAll(delegate(KeyValuePair<string, IHTTPContentProvider> item) { return prefix.Equals(item.Key, PrefixComparison); }); | |
312 } | |
313 public void ServeRequest(HTTPContext context) { | |
314 KeyValuePair<string, IHTTPContentProvider> c = Prefixes.Find(delegate(KeyValuePair<string, IHTTPContentProvider> item) { return context.RequestPath.StartsWith(item.Key, PrefixComparison); }); | |
315 if (c.Value != null) { | |
316 c.Value.ServeRequest(context); | |
317 } else { | |
318 context.SendErrorAndClose(404); | |
319 } | |
320 } | |
321 } | |
7
4b78cc5f116b
Fixes and improvements (some untested)
Ivo Smits <Ivo@UCIS.nl>
parents:
6
diff
changeset
|
322 public class HTTPStaticContent : IHTTPContentProvider { |
4b78cc5f116b
Fixes and improvements (some untested)
Ivo Smits <Ivo@UCIS.nl>
parents:
6
diff
changeset
|
323 public ArraySegment<Byte> ContentBuffer { get; set; } |
4b78cc5f116b
Fixes and improvements (some untested)
Ivo Smits <Ivo@UCIS.nl>
parents:
6
diff
changeset
|
324 public String ContentType { get; set; } |
4b78cc5f116b
Fixes and improvements (some untested)
Ivo Smits <Ivo@UCIS.nl>
parents:
6
diff
changeset
|
325 public HTTPStaticContent() : this(new ArraySegment<Byte>()) { } |
4b78cc5f116b
Fixes and improvements (some untested)
Ivo Smits <Ivo@UCIS.nl>
parents:
6
diff
changeset
|
326 public HTTPStaticContent(ArraySegment<Byte> content) : this(content, "application/octet-stream") { } |
4b78cc5f116b
Fixes and improvements (some untested)
Ivo Smits <Ivo@UCIS.nl>
parents:
6
diff
changeset
|
327 public HTTPStaticContent(String content, String contentType) : this(Encoding.UTF8.GetBytes(content), contentType) { } |
4b78cc5f116b
Fixes and improvements (some untested)
Ivo Smits <Ivo@UCIS.nl>
parents:
6
diff
changeset
|
328 public HTTPStaticContent(String contentType) : this(new ArraySegment<Byte>(), contentType) { } |
4b78cc5f116b
Fixes and improvements (some untested)
Ivo Smits <Ivo@UCIS.nl>
parents:
6
diff
changeset
|
329 public HTTPStaticContent(Byte[] content, String contentType) : this(new ArraySegment<Byte>(content), contentType) { } |
4b78cc5f116b
Fixes and improvements (some untested)
Ivo Smits <Ivo@UCIS.nl>
parents:
6
diff
changeset
|
330 public HTTPStaticContent(ArraySegment<Byte> content, String contentType) { |
4b78cc5f116b
Fixes and improvements (some untested)
Ivo Smits <Ivo@UCIS.nl>
parents:
6
diff
changeset
|
331 this.ContentBuffer = content; |
4b78cc5f116b
Fixes and improvements (some untested)
Ivo Smits <Ivo@UCIS.nl>
parents:
6
diff
changeset
|
332 this.ContentType = contentType; |
4b78cc5f116b
Fixes and improvements (some untested)
Ivo Smits <Ivo@UCIS.nl>
parents:
6
diff
changeset
|
333 } |
4b78cc5f116b
Fixes and improvements (some untested)
Ivo Smits <Ivo@UCIS.nl>
parents:
6
diff
changeset
|
334 public void SetContent(Byte[] bytes) { ContentBuffer = new ArraySegment<byte>(bytes); } |
4b78cc5f116b
Fixes and improvements (some untested)
Ivo Smits <Ivo@UCIS.nl>
parents:
6
diff
changeset
|
335 public void SetContent(Byte[] bytes, int offset, int count) { ContentBuffer = new ArraySegment<byte>(bytes, offset, count); } |
4b78cc5f116b
Fixes and improvements (some untested)
Ivo Smits <Ivo@UCIS.nl>
parents:
6
diff
changeset
|
336 public void SetContent(String content, Encoding encoding) { SetContent(encoding.GetBytes(content)); } |
4b78cc5f116b
Fixes and improvements (some untested)
Ivo Smits <Ivo@UCIS.nl>
parents:
6
diff
changeset
|
337 public void SetContent(String content) { SetContent(content, Encoding.UTF8); } |
4b78cc5f116b
Fixes and improvements (some untested)
Ivo Smits <Ivo@UCIS.nl>
parents:
6
diff
changeset
|
338 public void ServeRequest(HTTPContext context) { |
4b78cc5f116b
Fixes and improvements (some untested)
Ivo Smits <Ivo@UCIS.nl>
parents:
6
diff
changeset
|
339 ArraySegment<Byte> content = ContentBuffer; |
4b78cc5f116b
Fixes and improvements (some untested)
Ivo Smits <Ivo@UCIS.nl>
parents:
6
diff
changeset
|
340 if (content.Array == null) { |
4b78cc5f116b
Fixes and improvements (some untested)
Ivo Smits <Ivo@UCIS.nl>
parents:
6
diff
changeset
|
341 context.SendErrorAndClose(404); |
4b78cc5f116b
Fixes and improvements (some untested)
Ivo Smits <Ivo@UCIS.nl>
parents:
6
diff
changeset
|
342 return; |
4b78cc5f116b
Fixes and improvements (some untested)
Ivo Smits <Ivo@UCIS.nl>
parents:
6
diff
changeset
|
343 } |
4b78cc5f116b
Fixes and improvements (some untested)
Ivo Smits <Ivo@UCIS.nl>
parents:
6
diff
changeset
|
344 String contentType = ContentType; |
4b78cc5f116b
Fixes and improvements (some untested)
Ivo Smits <Ivo@UCIS.nl>
parents:
6
diff
changeset
|
345 context.SendStatus(200); |
4b78cc5f116b
Fixes and improvements (some untested)
Ivo Smits <Ivo@UCIS.nl>
parents:
6
diff
changeset
|
346 if (contentType != null) context.SendHeader("Content-Type", contentType); |
4b78cc5f116b
Fixes and improvements (some untested)
Ivo Smits <Ivo@UCIS.nl>
parents:
6
diff
changeset
|
347 context.SendHeader("Content-Length", content.Count.ToString()); |
4b78cc5f116b
Fixes and improvements (some untested)
Ivo Smits <Ivo@UCIS.nl>
parents:
6
diff
changeset
|
348 Stream response = context.GetResponseStream(); |
4b78cc5f116b
Fixes and improvements (some untested)
Ivo Smits <Ivo@UCIS.nl>
parents:
6
diff
changeset
|
349 response.Write(content.Array, content.Offset, content.Count); |
4b78cc5f116b
Fixes and improvements (some untested)
Ivo Smits <Ivo@UCIS.nl>
parents:
6
diff
changeset
|
350 response.Close(); |
4b78cc5f116b
Fixes and improvements (some untested)
Ivo Smits <Ivo@UCIS.nl>
parents:
6
diff
changeset
|
351 } |
4b78cc5f116b
Fixes and improvements (some untested)
Ivo Smits <Ivo@UCIS.nl>
parents:
6
diff
changeset
|
352 } |
3 | 353 public class HTTPFileProvider : IHTTPContentProvider { |
354 public String FileName { get; private set; } | |
355 public String ContentType { get; private set; } | |
356 public HTTPFileProvider(String fileName) : this(fileName, "application/octet-stream") { } | |
357 public HTTPFileProvider(String fileName, String contentType) { | |
358 this.FileName = fileName; | |
359 this.ContentType = contentType; | |
360 } | |
361 public void ServeRequest(HTTPContext context) { | |
362 if (File.Exists(FileName)) { | |
363 using (FileStream fs = File.OpenRead(FileName)) { | |
364 context.SendStatus(200); | |
365 context.SendHeader("Content-Type", ContentType); | |
366 context.SendHeader("Content-Length", fs.Length.ToString()); | |
367 long left = fs.Length; | |
368 Stream response = context.GetResponseStream(); | |
369 byte[] buffer = new byte[1024 * 10]; | |
370 while (fs.CanRead) { | |
371 int len = fs.Read(buffer, 0, buffer.Length); | |
372 if (len <= 0) break; | |
373 left -= len; | |
374 response.Write(buffer, 0, len); | |
375 } | |
376 response.Close(); | |
377 } | |
378 } else { | |
379 context.SendErrorAndClose(404); | |
380 } | |
381 } | |
0 | 382 } |
3 | 383 public class HTTPUnTarchiveProvider : IHTTPContentProvider { |
384 public String TarFileName { get; private set; } | |
385 public HTTPUnTarchiveProvider(String tarFile) { | |
386 this.TarFileName = tarFile; | |
387 } | |
388 public void ServeRequest(HTTPContext context) { | |
389 if (!File.Exists(TarFileName)) { | |
390 context.SendErrorAndClose(404); | |
391 return; | |
392 } | |
393 String reqname1 = context.RequestPath; | |
54
ba4e2cb031e0
Added general purpose tar archive reader class
Ivo Smits <Ivo@UCIS.nl>
parents:
45
diff
changeset
|
394 if (reqname1.StartsWith("/")) reqname1 = reqname1.Substring(1); |
3 | 395 String reqname2 = reqname1; |
396 if (reqname2.Length > 0 && !reqname2.EndsWith("/")) reqname2 += "/"; | |
397 reqname2 += "index.htm"; | |
54
ba4e2cb031e0
Added general purpose tar archive reader class
Ivo Smits <Ivo@UCIS.nl>
parents:
45
diff
changeset
|
398 foreach (TarchiveEntry file in new TarchiveReader(TarFileName)) { |
ba4e2cb031e0
Added general purpose tar archive reader class
Ivo Smits <Ivo@UCIS.nl>
parents:
45
diff
changeset
|
399 if (!file.IsFile) continue; |
ba4e2cb031e0
Added general purpose tar archive reader class
Ivo Smits <Ivo@UCIS.nl>
parents:
45
diff
changeset
|
400 if (!reqname1.Equals(file.Name, StringComparison.OrdinalIgnoreCase) && !reqname2.Equals(file.Name, StringComparison.OrdinalIgnoreCase)) continue; |
ba4e2cb031e0
Added general purpose tar archive reader class
Ivo Smits <Ivo@UCIS.nl>
parents:
45
diff
changeset
|
401 context.SendStatus(200); |
ba4e2cb031e0
Added general purpose tar archive reader class
Ivo Smits <Ivo@UCIS.nl>
parents:
45
diff
changeset
|
402 context.SendHeader("Content-Length", file.Size.ToString()); |
ba4e2cb031e0
Added general purpose tar archive reader class
Ivo Smits <Ivo@UCIS.nl>
parents:
45
diff
changeset
|
403 String ctype = null; |
ba4e2cb031e0
Added general purpose tar archive reader class
Ivo Smits <Ivo@UCIS.nl>
parents:
45
diff
changeset
|
404 switch (Path.GetExtension(file.Name).ToLowerInvariant()) { |
ba4e2cb031e0
Added general purpose tar archive reader class
Ivo Smits <Ivo@UCIS.nl>
parents:
45
diff
changeset
|
405 case ".txt": ctype = "text/plain"; break; |
ba4e2cb031e0
Added general purpose tar archive reader class
Ivo Smits <Ivo@UCIS.nl>
parents:
45
diff
changeset
|
406 case ".htm": |
ba4e2cb031e0
Added general purpose tar archive reader class
Ivo Smits <Ivo@UCIS.nl>
parents:
45
diff
changeset
|
407 case ".html": ctype = "text/html"; break; |
ba4e2cb031e0
Added general purpose tar archive reader class
Ivo Smits <Ivo@UCIS.nl>
parents:
45
diff
changeset
|
408 case ".css": ctype = "text/css"; break; |
ba4e2cb031e0
Added general purpose tar archive reader class
Ivo Smits <Ivo@UCIS.nl>
parents:
45
diff
changeset
|
409 case ".js": ctype = "application/x-javascript"; break; |
ba4e2cb031e0
Added general purpose tar archive reader class
Ivo Smits <Ivo@UCIS.nl>
parents:
45
diff
changeset
|
410 case ".png": ctype = "image/png"; break; |
ba4e2cb031e0
Added general purpose tar archive reader class
Ivo Smits <Ivo@UCIS.nl>
parents:
45
diff
changeset
|
411 case ".jpg": |
ba4e2cb031e0
Added general purpose tar archive reader class
Ivo Smits <Ivo@UCIS.nl>
parents:
45
diff
changeset
|
412 case ".jpeg": ctype = "image/jpeg"; break; |
ba4e2cb031e0
Added general purpose tar archive reader class
Ivo Smits <Ivo@UCIS.nl>
parents:
45
diff
changeset
|
413 case ".gif": ctype = "image/gif"; break; |
ba4e2cb031e0
Added general purpose tar archive reader class
Ivo Smits <Ivo@UCIS.nl>
parents:
45
diff
changeset
|
414 case ".ico": ctype = "image/x-icon"; break; |
ba4e2cb031e0
Added general purpose tar archive reader class
Ivo Smits <Ivo@UCIS.nl>
parents:
45
diff
changeset
|
415 } |
ba4e2cb031e0
Added general purpose tar archive reader class
Ivo Smits <Ivo@UCIS.nl>
parents:
45
diff
changeset
|
416 if (ctype != null) context.SendHeader("Content-Type", ctype); |
ba4e2cb031e0
Added general purpose tar archive reader class
Ivo Smits <Ivo@UCIS.nl>
parents:
45
diff
changeset
|
417 using (Stream response = context.GetResponseStream(), source = file.GetStream()) { |
ba4e2cb031e0
Added general purpose tar archive reader class
Ivo Smits <Ivo@UCIS.nl>
parents:
45
diff
changeset
|
418 byte[] buffer = new byte[Math.Min(source.Length, 1024 * 10)]; |
ba4e2cb031e0
Added general purpose tar archive reader class
Ivo Smits <Ivo@UCIS.nl>
parents:
45
diff
changeset
|
419 while (source.CanRead) { |
ba4e2cb031e0
Added general purpose tar archive reader class
Ivo Smits <Ivo@UCIS.nl>
parents:
45
diff
changeset
|
420 int len = source.Read(buffer, 0, buffer.Length); |
ba4e2cb031e0
Added general purpose tar archive reader class
Ivo Smits <Ivo@UCIS.nl>
parents:
45
diff
changeset
|
421 if (len <= 0) break; |
ba4e2cb031e0
Added general purpose tar archive reader class
Ivo Smits <Ivo@UCIS.nl>
parents:
45
diff
changeset
|
422 response.Write(buffer, 0, len); |
3 | 423 } |
424 } | |
54
ba4e2cb031e0
Added general purpose tar archive reader class
Ivo Smits <Ivo@UCIS.nl>
parents:
45
diff
changeset
|
425 return; |
3 | 426 } |
427 context.SendErrorAndClose(404); | |
428 } | |
0 | 429 } |
430 } |