contentserver/ContentServer/ContentServer.cs

332 lines
10 KiB
C#

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
namespace ContentServer
{
class ContentItem
{
public String name;
public String filePath;
public ContentItem(string Name,string FilePath)
{
if(File.Exists(FilePath))
{
filePath = FilePath;
name = Name;
}
else
{
throw new FileNotFoundException();
}
}
}
class ContentClient
{
public ContentClient(ContentServer Server, Socket ClientSocket)
{
clientSock = ClientSocket;
baseServ = Server;
Server.Clients.Add(this);
baseServ.WriteDebugOutput("PSVita Connected @ " + clientSock.RemoteEndPoint.ToString());
ProcessRequests();
clientSock.Close();
}
private ContentServer baseServ;
private Socket clientSock;
private byte[] ReadData()
{
while (clientSock.Available < 1) { }
byte[] by = new byte[clientSock.Available];
clientSock.Receive(by);
return by;
}
private void SendString(string str)
{
byte[] response = Encoding.UTF8.GetBytes(str);
clientSock.Send(response);
}
private string GenerateHeaders(string path, long content_length = 0)
{
string headers = "";
if (path == "/")
{
headers += "HTTP/1.1 200 OK\r\n";
headers += "Content-Type: text/html\r\n";
headers += "Accept-Ranges: bytes\r\n";
headers += "Server: ContentServer\r\n";
headers += "Content-Length: " + content_length + "\r\n";
headers += "Cache-Control: max-age=3600\r\n";
headers += "Connection: keep-alive\r\n";
headers += "\r\n";
}
else if (File.Exists(path))
{
FileInfo info = new FileInfo(path);
long length = info.Length;
if (content_length != 0)
length = content_length;
headers += "HTTP/1.1 200 OK\r\n";
headers += "Content-Type: application/octet-stream\r\n";
headers += "Accept-Ranges: bytes\r\n";
headers += "Server: ContentServer\r\n";
headers += "Content-Length: " + length + "\r\n";
headers += "Cache-Control: max-age=3600\r\n";
headers += "Connection: keep-alive\r\n";
headers += "\r\n";
}
else
{
headers += "HTTP/1.1 404 Not Found\r\n";
headers += "Content-Type: text/plain\r\n";
headers += "Accept-Ranges: bytes\r\n";
headers += "Server: ContentServer\r\n";
headers += "Content-Length: " + content_length + "\r\n";
headers += "Cache-Control: max-age=3600\r\n";
headers += "Connection: keep-alive\r\n";
headers += "\r\n";
}
return headers;
}
private void RespondGet(string path, Dictionary<string, string> query)
{
baseServ.WriteDebugOutput("GET " + path);
string name = Path.GetFileName(path);
if (ContentItemExists(name))
{
ContentItem ci = GetContentItem(name);
FileStream fs = File.OpenRead(ci.filePath);
try
{
string requestStr = GenerateHeaders(ci.filePath, fs.Length - fs.Position);
SendString(requestStr);
while(fs.Position < fs.Length)
{
int BUFFER_SIZE = 0x8500000;
if(fs.Position + BUFFER_SIZE <= fs.Length)
{
byte[] buffer = new byte[BUFFER_SIZE];
fs.Read(buffer, 0x00, BUFFER_SIZE);
clientSock.Send(buffer);
}
else
{
byte[] buffer = new byte[fs.Length - fs.Position];
fs.Read(buffer, 0x00, buffer.Length);
clientSock.Send(buffer);
}
}
}
catch (Exception) {
fs.Close();
};
}
else
{
string body = GeneratePage(path);
string requestStr = GenerateHeaders(path, body.Length);
requestStr += body;
SendString(requestStr);
}
}
private void RespondHead(string path)
{
string name = Path.GetFileName(path);
baseServ.WriteDebugOutput("HEAD " + path);
if (ContentItemExists(name))
{
ContentItem ci = GetContentItem(name);
string requestStr = GenerateHeaders(ci.filePath);
SendString(requestStr);
}
else
{
string body = GeneratePage(path);
string requestStr = GenerateHeaders(path, body.Length);
SendString(requestStr);
}
}
private bool ContentItemExists(string name)
{
bool exists = false;
foreach (ContentItem ci in baseServ.Contents)
{
if (ci.name == name)
{
exists = true;
}
}
return exists;
}
private ContentItem GetContentItem(string name)
{
foreach (ContentItem ci in baseServ.Contents)
{
if (ci.name == name)
{
return ci;
}
}
throw new FileNotFoundException();
}
private string GeneratePage(string path)
{
if (path == "/")
{
string body = "Content Downloader Server.<br>Open this url in PSVita's \"Content Downloader\" To view avalible files.";
foreach (ContentItem content in baseServ.Contents)
{
body += "<a href=\"" + content.name + "\"></a>";
}
return body;
}
else
{
string body = "File not found.";
return body;
}
}
private string ExtractPath(string relativeUri)
{
int questionIndex = relativeUri.IndexOf("?");
if (questionIndex != -1)
return relativeUri.Substring(0, questionIndex);
else
return relativeUri;
}
private Dictionary<string,string> ExtractQuery(string relativeUri)
{
int questionIndex = relativeUri.IndexOf("?");
if (questionIndex != -1)
{
string[] queryStrList = relativeUri.Substring(questionIndex + 1).Split('&');
Dictionary<string,string> queryDict = new Dictionary<string, string>();
foreach(string queryStr in queryStrList)
{
string[] qStr = queryStr.Split('=');
queryDict.Add(qStr[0], qStr[1]);
}
return queryDict;
}
else
return new Dictionary<string, string>();
}
private string ExtractRelativeUrl(string header)
{
int slashIndex = header.IndexOf("/");
int httpLen = header.IndexOf(" HTTP/1.1") - slashIndex;
string path = header.Substring(slashIndex, httpLen);
return path;
}
private void ProcessRequests()
{
byte[] data = ReadData();
// Parse Request
string curReq = Encoding.UTF8.GetString(data);
curReq = curReq.Replace("\r\n", "\n");
string[] reqLines = curReq.Split('\n');
foreach (string line in reqLines)
{
if (line.StartsWith("GET"))
{
string relUrl = ExtractRelativeUrl(line);
string path = ExtractPath(relUrl);
Dictionary<string,string> query = ExtractQuery(relUrl);
RespondGet(path, query);
return;
}
else if (line.StartsWith("HEAD"))
{
string relUrl = ExtractRelativeUrl(line);
string path = ExtractPath(relUrl);
RespondHead(path);
return;
}
}
}
}
class ContentServer
{
public List<ContentItem> Contents = new List<ContentItem>();
public List<ContentClient> Clients = new List<ContentClient>();
public void WriteDebugOutput(string txt)
{
Program.MainForm.Invoke((Action)delegate
{
Program.MainForm.AppendToConsole(txt + "\r\n");
});
}
public ContentServer()
{
new Thread(() =>
{
WriteDebugOutput("Listening for connections on port 1337.");
IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Parse("0.0.0.0"), 1337);
Socket newsock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
newsock.Bind(localEndPoint);
newsock.Listen(20);
while(true)
{
Socket clientSock = newsock.Accept();
new Thread(() =>
{
ContentClient client = new ContentClient(this, clientSock);
Clients.Remove(client);
}).Start();
}
}).Start();
}
}
}