1
0
Fork 0
GalleryShare/GalleryShare/GalleryServer.cs

212 lines
6.2 KiB
C#

using System;
using System.Net;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.IO;
using System.Collections.Generic;
using System.Runtime.Remoting.Messaging;
using System.Threading;
using System.Xml;
using System.Reflection;
namespace GalleryShare
{
enum OutputFunction
{
None,
SpecialFile,
DirectoryListing,
SendFile
}
class GalleryServer
{
int port;
string servingDirectory = Environment.CurrentDirectory;
HttpListener server = new HttpListener();
string prefix;
public int Port { get { return port; } }
public string ServingDirectory { get { return servingDirectory; } }
public GalleryServer(string inServingDirectory, int inPort)
{
port = inPort;
servingDirectory = inServingDirectory;
string homeDir = Environment.GetEnvironmentVariable("HOME");
if (homeDir != null)
servingDirectory = servingDirectory.Replace("~", homeDir);
prefix = string.Format("http://*:{0}/", Port);
server.Prefixes.Add(prefix);
}
/// <summary>
/// Synchronously starrts the server listening for requests.
/// </summary>
public void StartSync()
{
Task.WaitAll(Start());
}
/// <summary>
/// Asynchronously starts the server listening for requests.
/// </summary>
public async Task Start()
{
server.Start();
Console.WriteLine("Listening for requests on {0}.", prefix);
Console.WriteLine("Serving from {0}. Browser url: http://localhost:{1}/", servingDirectory, Port);
while (true)
{
Utilities.ForgetTask(Handle(await server.GetContextAsync()));
}
}
/// <summary>
/// Handles the specified Http request.
/// </summary>
/// <param name="cycle">The Http request to handle.</param>
private async Task Handle(HttpListenerContext cycle)
{
OutputFunction outFunction = OutputFunction.None;
if (cycle.Request.RawUrl.StartsWith("/!"))
outFunction = OutputFunction.SpecialFile;
string requestedPath = GetFullReqestedPath(cycle.Request.RawUrl);
if (Directory.Exists(requestedPath))
outFunction = OutputFunction.DirectoryListing;
if (File.Exists(requestedPath))
outFunction = OutputFunction.SendFile;
switch(outFunction)
{
case OutputFunction.SpecialFile:
await sendSpecialFile(cycle);
break;
case OutputFunction.DirectoryListing:
cycle.Response.ContentType = "application/xml";
await sendDirectoryListing(cycle.Response.OutputStream, cycle.Request.RawUrl, requestedPath);
break;
case OutputFunction.SendFile:
break;
default:
await sendMessage(cycle, 404, "Error: File or directory '{0}' not found.", requestedPath);
break;
}
logCycle(cycle);
cycle.Response.Close();
}
private string GetFullReqestedPath(string rawUrl)
{
return Path.GetFullPath(Path.Combine(servingDirectory, "." + rawUrl));
}
private async Task sendMessage(HttpListenerContext cycle, int statusCode, string message, params object[] paramObjects)
{
StreamWriter responseData = new StreamWriter(cycle.Response.OutputStream);
cycle.Response.StatusCode = statusCode;
await responseData.WriteLineAsync(string.Format(message, paramObjects));
/*responseData.Close();
cycle.Response.Close();*/
}
private void logCycle(HttpListenerContext cycle)
{
Console.WriteLine("[{0}] [{1}] [{2}] {3} {4}",
DateTime.Now.ToString("hh:mm tt"),
cycle.Request.RemoteEndPoint,
cycle.Response.StatusCode,
cycle.Request.HttpMethod,
cycle.Request.RawUrl
);
}
private async Task sendDirectoryListing(Stream outgoingData, string rawUrl, string requestedPath)
{
List<string> dirFiles = new List<string>(Directory.GetFiles(requestedPath));
List<string> dirDirectories = new List<string>(Directory.GetDirectories(requestedPath));
XmlWriterSettings writerSettings = new XmlWriterSettings();
writerSettings.Async = true;
writerSettings.Indent = true;
writerSettings.IndentChars = "\t";
XmlWriter xmlData = XmlWriter.Create(outgoingData, writerSettings);
await xmlData.WriteStartDocumentAsync();
await xmlData.WriteProcessingInstructionAsync("xml-stylesheet", "type=\"text/xsl\" href=\"/!Transform-DirListing.xslt\"");
await xmlData.WriteStartElementAsync(null, "DirectoryListing", null);
await xmlData.WriteElementStringAsync(null, "CurrentDirectory", null, rawUrl);
await xmlData.WriteStartElementAsync(null, "Contents", null);
foreach (string directoryName in dirDirectories)
{
await xmlData.WriteStartElementAsync(null, "ListingEntry", null);
await xmlData.WriteAttributeStringAsync(null, "Type", null, "Directory");
await xmlData.WriteElementStringAsync(null, "Name", null, directoryName.Substring(servingDirectory.Length));
await xmlData.WriteElementStringAsync(null, "ItemCount", null, Directory.GetFileSystemEntries(directoryName).Length.ToString());
// TODO: Write out thumbnail url
await xmlData.WriteEndElementAsync();
}
foreach (string filename in dirFiles)
{
await xmlData.WriteStartElementAsync(null, "ListingEntry", null);
await xmlData.WriteAttributeStringAsync(null, "Type", null, "File");
await xmlData.WriteElementStringAsync(null, "Name", null, filename.Substring(servingDirectory.Length));
await xmlData.WriteEndElementAsync();
}
await xmlData.WriteEndDocumentAsync();
await xmlData.FlushAsync();
}
private async Task sendSpecialFile(HttpListenerContext cycle)
{
string specialFileName = cycle.Request.RawUrl.Substring(2);
string outputFileName = string.Empty;
switch(specialFileName)
{
case "Transform-DirListing.xslt":
outputFileName = @"GalleryShare.Embed.DirectoryListing.xslt";
break;
}
if (outputFileName == string.Empty)
{
await sendMessage(cycle, 404, "Error: Unknown special file '{0}' requested.", specialFileName);
return;
}
/*string[] resNames = Assembly.GetExecutingAssembly().GetManifestResourceNames();
foreach (string resName in resNames)
Console.WriteLine(resName);*/
cycle.Response.ContentType = "text/xsl";
byte[] xsltData = await Utilities.GetEmbeddedResourceContent(outputFileName);
await cycle.Response.OutputStream.WriteAsync(xsltData, 0, xsltData.Length);
}
private async Task sendFile(string requestedPath)
{
}
}
}