Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #region
- using System;
- using System.IO;
- using System.Linq;
- using System.Net;
- using System.Text;
- using System.Text.RegularExpressions;
- using System.Threading;
- using System.Web;
- using MimeTypes;
- using MyServer.Properties;
- #endregion
- namespace MyServer
- {
- internal class HttpServer : IDisposable
- {
- private const int Kb = 1024;
- private const int Mb = Kb*Kb;
- private const int PacketSize = Mb*4; // Buffer size for reading from files.
- private readonly string _fileDirectory;
- private readonly HttpListener _httpListener;
- private Thread _listenerThread;
- private State _serverState;
- public HttpServer(string rootFolder = @"Files\", bool relative = true)
- {
- rootFolder = rootFolder.EndsWith("\\") ? rootFolder : rootFolder + "\\";
- _serverState = State.Stopped;
- var serverDirectory = AppDomain.CurrentDomain.BaseDirectory;
- _fileDirectory = relative ? Path.Combine(serverDirectory, rootFolder) : rootFolder;
- _listenerThread = new Thread(ListenerThread);
- Log($"Server Directory: {serverDirectory}");
- Log($"File Directory: {_fileDirectory}");
- _httpListener = new HttpListener();
- _httpListener.Prefixes.Add(@"http://127.0.0.1:80/");
- }
- public void Dispose()
- {
- _httpListener.Close();
- }
- private void ListenerThread()
- {
- while (_httpListener.IsListening)
- {
- try
- {
- var context = _httpListener.GetContext();
- ThreadPool.QueueUserWorkItem(HandleRequest, context);
- }
- catch (Exception e)
- {
- Log($"[Error] {e.Message}");
- }
- GC.Collect();
- }
- }
- public void Start()
- {
- if (_serverState == State.Aborted) return;
- if (!_httpListener.IsListening)
- _httpListener.Start();
- try
- {
- if (!_listenerThread.IsAlive)
- _listenerThread = new Thread(ListenerThread);
- _listenerThread.Start();
- }
- catch (Exception e)
- {
- Log(e.Message);
- }
- _serverState = State.Running;
- Log("Starting Server.");
- }
- public void Stop()
- {
- if (_serverState == State.Aborted) return;
- if (_httpListener.IsListening)
- _httpListener.Stop();
- _serverState = State.Stopped;
- Log("Stopping Server.");
- }
- public void Abort()
- {
- if (_serverState == State.Aborted) return;
- _listenerThread.Abort();
- _httpListener.Abort();
- _serverState = State.Aborted;
- Log("Aborting Server.");
- }
- private static bool TryGetStream(string path, out Stream stream)
- {
- try
- {
- if (File.Exists(path))
- {
- stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
- return true;
- }
- stream = Stream.Null;
- return false;
- }
- catch (Exception e)
- {
- Log(e.Message);
- stream = Stream.Null;
- return false;
- }
- }
- private static bool TryGetDirectory(string directory, out DirectoryInfo info)
- {
- try
- {
- info = new DirectoryInfo(directory);
- return info.Exists;
- }
- catch (Exception e)
- {
- Log(e.Message);
- info = new DirectoryInfo(string.Empty);
- return false;
- }
- }
- private string ToUrl(string localPath)
- {
- if (localPath == null) return string.Empty;
- if (!localPath.StartsWith(_fileDirectory, StringComparison.CurrentCultureIgnoreCase)) return string.Empty;
- if (_fileDirectory.Equals(localPath, StringComparison.CurrentCultureIgnoreCase)) return string.Empty;
- if (localPath.Length < _fileDirectory.Length) return string.Empty;
- return HttpUtility.HtmlEncode(localPath.Substring(_fileDirectory.Length));
- }
- private static void Log(object data)
- {
- Console.WriteLine($"{DateTime.Now:G} | {data}");
- }
- private enum State
- {
- Running,
- Stopped,
- Aborted
- }
- #region RequestHandler
- private static readonly Regex RangeRegex = new Regex(@"bytes=?(?<start>\d+)?-(?<end>\d+)?",
- RegexOptions.Compiled);
- private void HandleRequest(object state)
- {
- var context = (HttpListenerContext) state;
- try
- {
- var request = context.Request;
- var response = context.Response;
- var host = request.UserHostName;
- var localRequest = HttpUtility.HtmlDecode(request.Url.LocalPath).Replace('/', '\\').Substring(1);
- var requestedPath = Path.Combine(_fileDirectory, localRequest);
- var outputStream = response.OutputStream;
- Log($@"[Request] Path: \{localRequest}");
- Stream stream;
- DirectoryInfo directoryInfo;
- var bytesToSend = new byte[0];
- if (localRequest.Equals("favicon.ico"))
- {
- var iconBytes = Resources.favicon;
- response.KeepAlive = false;
- response.ContentType = "image/x-icon";
- response.Headers.Add("Content-Type", "image/x-icon"); // Ask the system for the filetype.
- response.Headers.Add("Content-Disposition", "inline; filename=\"favicon.ico\"");
- response.Headers.Add("Date", $"{DateTime.Now:R}");
- response.ContentLength64 = iconBytes.LongLength;
- outputStream.Write(iconBytes, 0, iconBytes.Length);
- }
- else if (TryGetStream(requestedPath, out stream))
- {
- var fileInfo = new FileInfo(requestedPath);
- var fileName = fileInfo.Name;
- var fileExtension = fileInfo.Extension;
- var contentType = MimeTypeMap.GetMimeType(fileExtension);
- var totalLength = stream.Length;
- var range = request.Headers["Range"] ?? string.Empty;
- var match = RangeRegex.Match(range);
- var start = match.Groups["start"].Success ? long.Parse(match.Groups["start"].Value) : 0L;
- var finish = match.Groups["end"].Success ? long.Parse(match.Groups["end"].Value) : totalLength;
- response.KeepAlive = false;
- response.ContentType = contentType;
- response.Headers.Add("Content-Type", contentType); // Ask the system for the filetype.
- response.Headers.Add("Content-Disposition", $"inline; filename={fileName}");
- response.Headers.Add("Date", $"{DateTime.Now:R}");
- response.Headers.Add("Last-Modified", $"{fileInfo.LastWriteTime:R}");
- response.Headers.Add("Accept-Ranges", "bytes");
- response.Headers.Add("Content-Range", $"bytes {start}-{finish - 1}/{totalLength}");
- if (start == 0 && finish == totalLength)
- {
- response.StatusCode = 200;
- }
- else if(start >= 0 && finish <= totalLength)
- {
- response.StatusCode = 206;
- }
- else
- {
- response.StatusCode = 416;
- throw new Exception("Request range out of bounds.");
- }
- response.ContentLength64 = finish - start;
- stream.Seek(start, SeekOrigin.Begin);
- for (var i = 0; i < finish - start; i += PacketSize)
- {
- var buffer = new byte[PacketSize];
- var bytes = stream.Read(buffer, 0, PacketSize);
- var bytesToWrite = (int) Math.Min(finish - start - i, bytes);
- outputStream.Write(buffer, 0, bytesToWrite);
- }
- stream.Close();
- }
- else if (TryGetDirectory(requestedPath, out directoryInfo))
- {
- response.KeepAlive = false;
- response.ContentType = "text/html";
- response.Headers.Add("Content-Type", "text/html; charset=UTF-8");
- response.Headers.Add("Content-Language", "en");
- response.Headers.Add("Content-Disposition", $"inline; filename={directoryInfo.Name}.html");
- response.Headers.Add("Date", $"{DateTime.Now:R}");
- response.Headers.Add("Last-Modified", $"{directoryInfo.LastWriteTime:R}");
- response.StatusCode = 200;
- var sb = new StringBuilder();
- foreach (var directory in directoryInfo.EnumerateDirectories().OrderBy(s => s.Name))
- {
- sb.AppendLine(
- $"<tr><td class=\"name\"><a href=\"//{host}/{ToUrl(directory.FullName)}/\">/{directory.Name}/</a></td><td class=\"date\">{directory.LastWriteTime:G}</td><td class=\"size\">{directory.EnumerateFiles("*", SearchOption.AllDirectories).Sum(s => s.Length)/Kb} KB</td><tr>");
- }
- foreach (var file in directoryInfo.EnumerateFiles().OrderBy(s => s.Name))
- {
- sb.AppendLine(
- $"<tr><td class=\"name\"><a href=\"//{host}/{ToUrl(file.FullName)}\">/{file.Name}</a></td><td class=\"date\">{file.LastWriteTime:G}</td><td class=\"size\">{file.Length/Kb} KB</td></tr>");
- }
- var endString = string.Format(Resources.Folder, directoryInfo.Name, ToUrl(directoryInfo.FullName),
- host, ToUrl(directoryInfo.Parent.FullName), sb, $"{DateTime.Now:R}");
- bytesToSend = Encoding.UTF8.GetBytes(endString);
- }
- else
- {
- response.KeepAlive = false;
- response.ContentType = "text/html";
- response.Headers.Add("Content-Type", "text/html; charset=UTF-8");
- response.Headers.Add("Content-Language", "en");
- response.Headers.Add("Content-Disposition", $"inline; filename={directoryInfo.Name}.html");
- response.Headers.Add("Date", $"{DateTime.Now:R}");
- response.StatusCode = 200;
- bytesToSend = Encoding.UTF8.GetBytes(string.Format(Resources.Error, localRequest));
- }
- if (bytesToSend.Length > 0 && outputStream.CanWrite)
- {
- response.ContentEncoding = Encoding.UTF8;
- response.ContentLength64 = bytesToSend.LongLength;
- outputStream.Write(bytesToSend, 0, bytesToSend.Length);
- }
- outputStream.Flush();
- response.Close();
- }
- catch (HttpListenerException)
- {
- context.Response.Abort();
- }
- catch (Exception e)
- {
- context.Response.Abort();
- Log($"[Error] {e.Message}");
- }
- }
- #endregion RequestHandler
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement