Compare commits
13 Commits
master
...
work-in-pr
Author | SHA1 | Date |
---|---|---|
Harald Christian Joachim Wolff | 475fe6e4da | |
Harald Christian Joachim Wolff | 06a3283d40 | |
Harald Wolff | 8d1ea04d57 | |
Harald Wolff | ec3ec514de | |
Harald Wolff | 89ed4b3418 | |
Harald Christian Joachim Wolff | 2d5d20d3a5 | |
Harald Wolff | 9832676f27 | |
Harald Wolff | a824a500d0 | |
Harald Wolff | 4bac649872 | |
Harald Wolff | 96430f92c0 | |
Harald Wolff | a53341ba60 | |
Harald Wolff | 34edfcfcbc | |
Harald Wolff | 37462c7e20 |
19
Program.cs
19
Program.cs
|
@ -5,6 +5,7 @@ using System.Threading;
|
|||
using appsrv.resources;
|
||||
using System.IO;
|
||||
using appsrv.test;
|
||||
using System.Reflection;
|
||||
|
||||
namespace appsrv
|
||||
{
|
||||
|
@ -13,18 +14,20 @@ namespace appsrv
|
|||
public static void Main(string[] args)
|
||||
{
|
||||
ApplicationServer server = new ApplicationServer();
|
||||
//Application application = new Application(server, "../../../sharp-wawi/bin/Debug/WaWi.xml");
|
||||
Application application = new Application(server, "../../../netmanager/bin/Debug/netmanager.xml");
|
||||
//Application application = new Application(server, "test/TestApplication.xml");
|
||||
|
||||
Resource root = new DirectoryResource(new DirectoryInfo("./www"));
|
||||
server.AddRoot("localhost",root);
|
||||
|
||||
StaticClassResource staticClassResource = new StaticClassResource(typeof(StaticTest),root);
|
||||
|
||||
|
||||
Http http = new Http(server);
|
||||
server.Add(application);
|
||||
|
||||
Http http = new Http(server);
|
||||
http.Start();
|
||||
|
||||
Thread.Sleep(10000);
|
||||
bool exit = false;
|
||||
|
||||
while (!exit){
|
||||
Thread.Sleep(1000);
|
||||
};
|
||||
|
||||
http.Stop();
|
||||
|
||||
|
|
|
@ -1,83 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
|
||||
<ProjectGuid>{FD508FE5-5879-4C60-91D8-CA408E06361F}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>appsrv</RootNamespace>
|
||||
<AssemblyName>appsrv</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug</OutputPath>
|
||||
<DefineConstants>DEBUG;</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ExternalConsole>true</ExternalConsole>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release</OutputPath>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ExternalConsole>true</ExternalConsole>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="connector\Http.cs" />
|
||||
<Compile Include="server\ApplicationServer.cs" />
|
||||
<Compile Include="connector\Connector.cs" />
|
||||
<Compile Include="server\Application.cs" />
|
||||
<Compile Include="server\HttpRequest.cs" />
|
||||
<Compile Include="exceptions\IllegalRequestException.cs" />
|
||||
<Compile Include="resources\Resource.cs" />
|
||||
<Compile Include="resources\FileResource.cs" />
|
||||
<Compile Include="resources\DirectoryResource.cs" />
|
||||
<Compile Include="exceptions\ApplicationServerException.cs" />
|
||||
<Compile Include="exceptions\ResourceNotFoundException.cs" />
|
||||
<Compile Include="mime\MimeHelper.cs" />
|
||||
<Compile Include="http\HttpStatusCodes.cs" />
|
||||
<Compile Include="resources\ResourceLink.cs" />
|
||||
<Compile Include="resources\StaticClassResource.cs" />
|
||||
<Compile Include="attributes\WebCallable.cs" />
|
||||
<Compile Include="test\StaticTest.cs" />
|
||||
<Compile Include="http\QueryStringParameters.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="connector\" />
|
||||
<Folder Include="server\" />
|
||||
<Folder Include="exceptions\" />
|
||||
<Folder Include="resources\" />
|
||||
<Folder Include="www\" />
|
||||
<Folder Include="www\gfx\" />
|
||||
<Folder Include="mime\" />
|
||||
<Folder Include="http\" />
|
||||
<Folder Include="attributes\" />
|
||||
<Folder Include="test\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="www\index.html">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<ProjectExtensions>
|
||||
<MonoDevelop>
|
||||
<Properties>
|
||||
<Policies>
|
||||
<DotNetNamingPolicy ResourceNamePolicy="FileFormatDefault" DirectoryNamespaceAssociation="PrefixedHierarchical" />
|
||||
</Policies>
|
||||
</Properties>
|
||||
</MonoDevelop>
|
||||
</ProjectExtensions>
|
||||
</Project>
|
|
@ -0,0 +1,12 @@
|
|||
using System;
|
||||
namespace appsrv.attributes
|
||||
{
|
||||
public class HTMLEditorAttribute : Attribute
|
||||
{
|
||||
public bool Details { get; set; }
|
||||
|
||||
public HTMLEditorAttribute()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
using System;
|
||||
namespace appsrv.attributes
|
||||
{
|
||||
public class PublishedMemberAttribute : Attribute
|
||||
{
|
||||
|
||||
public PublishedMemberAttribute()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
using System;
|
||||
namespace appsrv.attributes
|
||||
{
|
||||
public enum Serialization {
|
||||
PLAIN,
|
||||
JSON,
|
||||
XML
|
||||
}
|
||||
|
||||
public class WebCallable : Attribute
|
||||
{
|
||||
public String Name { get; set; }
|
||||
public Serialization Serialization { get; set; } = Serialization.PLAIN;
|
||||
|
||||
public WebCallable()
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
using System;
|
||||
namespace appsrv.attributes
|
||||
{
|
||||
public enum SERIALIZATION {
|
||||
PLAIN,
|
||||
JSON,
|
||||
XML
|
||||
}
|
||||
|
||||
public class WebCallableAttribute : Attribute
|
||||
{
|
||||
public String Name { get; set; }
|
||||
public SERIALIZATION Serialization { get; set; } = SERIALIZATION.PLAIN;
|
||||
public bool MapPathToParameters { get; set; } = false;
|
||||
|
||||
public WebCallableAttribute()
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -4,6 +4,8 @@ using System.Net.Sockets;
|
|||
using appsrv.connector;
|
||||
using appsrv.server;
|
||||
using System.Threading;
|
||||
using appsrv.http;
|
||||
using System.IO;
|
||||
|
||||
namespace appsrv.protocol
|
||||
{
|
||||
|
@ -50,35 +52,43 @@ namespace appsrv.protocol
|
|||
private void acceptRequests(){
|
||||
try
|
||||
{
|
||||
|
||||
|
||||
while (true)
|
||||
{
|
||||
TcpClient client = this.tcpListener.AcceptTcpClient();
|
||||
|
||||
try
|
||||
{
|
||||
HttpRequest request = new HttpRequest(this.ApplicationServer, client);
|
||||
|
||||
Console.WriteLine("new request: {0}",request);
|
||||
|
||||
Thread t = new Thread(() => request.Handle());
|
||||
t.Start();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine("Exception: {0}", e);
|
||||
}
|
||||
|
||||
Thread requestThread = new Thread(() => ClientConnection(client));
|
||||
requestThread.Start();
|
||||
}
|
||||
} catch (SocketException e){
|
||||
Console.WriteLine("Http connector interupted");
|
||||
Console.WriteLine("Http connector interupted: {0}",e);
|
||||
}
|
||||
}
|
||||
|
||||
private void ClientConnection(TcpClient tcpClient)
|
||||
{
|
||||
try
|
||||
{
|
||||
HttpRequest httpRequest = new HttpRequest(this.ApplicationServer, tcpClient);
|
||||
|
||||
ApplicationServer.HandleRequest(httpRequest);
|
||||
|
||||
HttpResponse httpResponse = httpRequest.HttpResponse;
|
||||
|
||||
if (!httpRequest.Session.ID.ToString().Equals(httpRequest.GetRequestCookie("SAS_SID")))
|
||||
{
|
||||
httpResponse.SetCookie("SAS_SID", httpRequest.Session.ID.ToString());
|
||||
}
|
||||
httpRequest.Session.Touch();
|
||||
|
||||
httpResponse.Send();
|
||||
|
||||
tcpClient.Close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
Console.WriteLine("Exception: {0}", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
using System;
|
||||
using System.Xml;
|
||||
namespace appsrv.http
|
||||
{
|
||||
public class HtmlReader : XmlTextReader
|
||||
{
|
||||
public override bool CanResolveEntity => false;
|
||||
|
||||
public HtmlReader(String sourceFilename)
|
||||
:base(sourceFilename)
|
||||
{
|
||||
EntityHandling = EntityHandling.ExpandCharEntities;
|
||||
}
|
||||
|
||||
public override void ResolveEntity()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
using System;
|
||||
using System.Xml;
|
||||
using System.IO;
|
||||
namespace appsrv.http
|
||||
{
|
||||
public class HtmlResolver : XmlUrlResolver
|
||||
{
|
||||
public HtmlResolver()
|
||||
{
|
||||
}
|
||||
|
||||
public override Uri ResolveUri(Uri baseUri, string relativeUri)
|
||||
{
|
||||
return base.ResolveUri(baseUri, relativeUri);
|
||||
}
|
||||
|
||||
public override object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn)
|
||||
{
|
||||
return base.GetEntity(absoluteUri,role,ofObjectToReturn);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using appsrv.exceptions;
|
||||
using appsrv.http;
|
||||
using appsrv.server;
|
||||
using Newtonsoft.Json;
|
||||
using appsrv.sessions;
|
||||
using appsrv.mime;
|
||||
|
||||
namespace appsrv.http
|
||||
{
|
||||
public class HttpRequest : Request
|
||||
{
|
||||
private Stream stream;
|
||||
public HttpResponse HttpResponse { get; private set; }
|
||||
|
||||
public MIMEMessage MIME { get; protected set; }
|
||||
public override Response Response => HttpResponse;
|
||||
|
||||
private Dictionary<string, string> requestCookies = new Dictionary<string, string>();
|
||||
|
||||
public EndPoint LocalEndpoint { get; private set; }
|
||||
public EndPoint Client { get; private set; }
|
||||
|
||||
public String Method { get; private set; }
|
||||
|
||||
public String RequestURL { get; private set; }
|
||||
public String QueryString { get; protected set; }
|
||||
|
||||
public String Protocol { get; private set; }
|
||||
public String Hostname { get; private set; }
|
||||
public int Port { get; private set; }
|
||||
|
||||
public override string ROOT => String.Format("http://{0}:{1}", Hostname, Port);
|
||||
|
||||
|
||||
public QueryStringParameters Query { get; private set; }
|
||||
|
||||
public HttpRequest(ApplicationServer applicationServer, TcpClient client)
|
||||
:base(applicationServer)
|
||||
{
|
||||
this.LocalEndpoint = client.Client.LocalEndPoint;
|
||||
this.Client = client.Client.RemoteEndPoint;
|
||||
this.stream = client.GetStream();
|
||||
|
||||
String rLine = this.stream.ReadLine(Encoding.ASCII);
|
||||
if (rLine == null)
|
||||
throw new IOException("connection lost");
|
||||
|
||||
String[] rTokens = rLine.SplitWhitespace();
|
||||
|
||||
if (rTokens.Length != 3){
|
||||
throw new IllegalRequestException(rLine);
|
||||
}
|
||||
|
||||
this.Method = rTokens[0];
|
||||
this.RequestURL = rTokens[1];
|
||||
this.Protocol = rTokens[2];
|
||||
|
||||
MIME = new MIMEMessage(stream, true);
|
||||
|
||||
DecodeRequest();
|
||||
|
||||
HttpResponse = new HttpResponse(this,stream);
|
||||
}
|
||||
|
||||
private void DecodeRequest()
|
||||
{
|
||||
String[] host = MIME.Headers["Host"].Value.Split(':');
|
||||
|
||||
if (LocalEndpoint is IPEndPoint)
|
||||
{
|
||||
IPEndPoint iPEndPoint = (IPEndPoint)LocalEndpoint;
|
||||
Port = iPEndPoint.Port;
|
||||
} else if (host.Length > 1){
|
||||
Port = int.Parse(host[1]);
|
||||
}
|
||||
|
||||
Hostname = host[0];
|
||||
Path = RequestURL.ToMark('?');
|
||||
QueryString = RequestURL.FromMark('?');
|
||||
|
||||
Query = new QueryStringParameters(QueryString);
|
||||
|
||||
foreach (MimeHeader header in MIME.Headers.GetHeadersByName("COOKIE"))
|
||||
{
|
||||
string[] tokens = header.Value.Split(new char[] { '=' }, 2);
|
||||
if (tokens.Length == 2)
|
||||
{
|
||||
requestCookies.Add(tokens[0], tokens[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
requestCookies.Add(tokens[0], "");
|
||||
}
|
||||
}
|
||||
|
||||
Application = ApplicationServer.ApplicationByAlias(Hostname);
|
||||
|
||||
if (GetRequestCookie("SAS_SID") != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
ApplySession(Guid.Parse(GetRequestCookie("SAS_SID")));
|
||||
} catch (FormatException fe)
|
||||
{
|
||||
Console.WriteLine("Invalid SAS_SID: {0}",fe);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override MIMEMessage GetParameter(string name)
|
||||
{
|
||||
foreach (MIMEMessage part in MIME.Parts)
|
||||
{
|
||||
if (part.Headers.Contains("content-disposition") &&
|
||||
part.Headers.GetHeaderByName("content-disposition").ContainsParameter("NAME") &&
|
||||
name.Equals(part.Headers.GetHeaderByName("content-disposition").GetParameter("NAME"))
|
||||
)
|
||||
{
|
||||
return part;
|
||||
}
|
||||
}
|
||||
|
||||
return Query[name];
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("[HttpRequest: Client={0}, ApplicationServer={1}, Hostname={6} Port={7}, Method={3}, RequestURL={4}, Protocol={5} Query={8}]", Client, ApplicationServer, "", Method, RequestURL, Protocol, Hostname, Port,Query);
|
||||
}
|
||||
|
||||
public String GetRequestCookie(String name)
|
||||
{
|
||||
if (!requestCookies.ContainsKey(name))
|
||||
return null;
|
||||
|
||||
return requestCookies[name];
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
using System;
|
||||
using appsrv.server;
|
||||
using System.IO;
|
||||
using appsrv.mime;
|
||||
using System.Collections.Generic;
|
||||
namespace appsrv.http
|
||||
{
|
||||
public class HttpResponse : Response
|
||||
{
|
||||
Stream clientStream;
|
||||
public HttpRequest HttpRequest { get; protected set; }
|
||||
|
||||
Dictionary<string, string> cookies = new Dictionary<string, string>();
|
||||
|
||||
public HttpResponse(HttpRequest request,Stream clientStream)
|
||||
:base(request)
|
||||
{
|
||||
this.clientStream = clientStream;
|
||||
HttpRequest = request;
|
||||
}
|
||||
|
||||
public void SetCookie(string name,string value)
|
||||
{
|
||||
cookies.Add(name, value);
|
||||
}
|
||||
|
||||
public void Send()
|
||||
{
|
||||
Flush();
|
||||
|
||||
using (StreamWriter streamWriter = new StreamWriter(clientStream))
|
||||
{
|
||||
byte[] content = ContentBytes;
|
||||
|
||||
Headers.Set("Content-Length", content.Length.ToString());
|
||||
|
||||
streamWriter.WriteLine("{0} {1} {2}", HttpRequest.Protocol, StatusCode, HttpStatusCodes.GetStatusMessage(StatusCode));
|
||||
foreach (MimeHeader header in Headers)
|
||||
{
|
||||
streamWriter.WriteLine(header.ToString());
|
||||
}
|
||||
|
||||
foreach (string cn in cookies.Keys)
|
||||
{
|
||||
streamWriter.WriteLine("Set-Cookie: {0}={1}; Path=/", cn, cookies[cn]);
|
||||
}
|
||||
|
||||
streamWriter.WriteLine();
|
||||
streamWriter.Flush();
|
||||
|
||||
clientStream.Write(content, 0, content.Length);
|
||||
clientStream.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -3,15 +3,16 @@ using System.Collections;
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using appsrv.mime;
|
||||
namespace appsrv.http
|
||||
{
|
||||
public class QueryStringParameters : IDictionary<String,String>
|
||||
public class QueryStringParameters : IEnumerable<MIMEMessage>
|
||||
{
|
||||
Dictionary<string, string> parameters = new Dictionary<string, string>();
|
||||
List<MIMEMessage> MIMEMessages = new List<MIMEMessage>();
|
||||
|
||||
public QueryStringParameters(String query)
|
||||
{
|
||||
if (query.StartsWith("?"))
|
||||
if (query.StartsWith("?",StringComparison.InvariantCulture))
|
||||
query = query.Substring(1);
|
||||
|
||||
String[] pairs = query.Split(new char[] { '&' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
@ -21,45 +22,40 @@ namespace appsrv.http
|
|||
String[] kv = pair.Split(new char[] { '=' }, 2, StringSplitOptions.RemoveEmptyEntries);
|
||||
string key = Uri.UnescapeDataString(kv[0].Replace('+',' '));
|
||||
string value = kv.Length == 2 ? Uri.UnescapeDataString(kv[1].Replace('+',' ')) : "";
|
||||
if (!key.Equals(String.Empty)){
|
||||
parameters[key] = value;
|
||||
}
|
||||
|
||||
MIMEMessage param = new MIMEMessage();
|
||||
param.Value = value;
|
||||
param.Headers.Add(new MimeHeader("content-disposition", String.Format("query-data; name=\"{0}\"",key)));
|
||||
MIMEMessages.Add(param);
|
||||
}
|
||||
}
|
||||
|
||||
public string this[string key] {
|
||||
get => parameters[key];
|
||||
set => throw new NotImplementedException(); }
|
||||
public MIMEMessage this[string key] {
|
||||
get => Get(key);
|
||||
set => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public ICollection<string> Keys => parameters.Keys;
|
||||
public ICollection<string> Values => parameters.Values;
|
||||
public int Count => parameters.Count;
|
||||
public bool IsReadOnly => true;
|
||||
|
||||
public void Add(string key, string value) => throw new NotImplementedException();
|
||||
public void Add(KeyValuePair<string, string> item) => throw new NotImplementedException();
|
||||
public void Clear() => throw new NotImplementedException();
|
||||
public bool Remove(string key) => throw new NotImplementedException();
|
||||
public bool Remove(KeyValuePair<string, string> item) => throw new NotImplementedException();
|
||||
|
||||
public bool Contains(KeyValuePair<string, string> item) => parameters.Contains(item);
|
||||
public bool ContainsKey(string key) => parameters.ContainsKey(key);
|
||||
public void CopyTo(KeyValuePair<string, string>[] array, int arrayIndex) => ((IDictionary<string, string>)parameters).CopyTo(array, arrayIndex);
|
||||
public IEnumerator<KeyValuePair<string, string>> GetEnumerator() => parameters.GetEnumerator();
|
||||
public bool TryGetValue(string key, out string value) => parameters.TryGetValue(key, out value);
|
||||
IEnumerator IEnumerable.GetEnumerator() => parameters.GetEnumerator();
|
||||
|
||||
public override string ToString()
|
||||
public MIMEMessage Get(String key)
|
||||
{
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
stringBuilder.Append("[Query");
|
||||
foreach (String key in parameters.Keys){
|
||||
stringBuilder.AppendFormat(" {0}={1}", key, parameters[key]);
|
||||
foreach (MIMEMessage v in MIMEMessages)
|
||||
{
|
||||
if (v.Headers.Contains("content-disposition") &&
|
||||
v.Headers.GetHeaderByName("content-disposition").ContainsParameter("NAME") &&
|
||||
key.Equals(v.Headers.GetHeaderByName("content-disposition").GetParameter("NAME"))
|
||||
)
|
||||
return v;
|
||||
}
|
||||
stringBuilder.Append("]");
|
||||
return null;
|
||||
}
|
||||
|
||||
return stringBuilder.ToString();
|
||||
public IEnumerator<MIMEMessage> GetEnumerator()
|
||||
{
|
||||
return MIMEMessages.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable)MIMEMessages).GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
namespace appsrv.http
|
||||
{
|
||||
public static class StringExtensions
|
||||
{
|
||||
public static String[] SplitWhitespace(this String line)
|
||||
{
|
||||
LinkedList<String> tokens = new LinkedList<string>();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (int n = 0; n < line.Length;)
|
||||
{
|
||||
for (; n < line.Length && !Char.IsWhiteSpace(line[n]); n++)
|
||||
sb.Append(line[n]);
|
||||
|
||||
tokens.AddLast(sb.ToString());
|
||||
sb.Clear();
|
||||
|
||||
for (; n < line.Length && Char.IsWhiteSpace(line[n]); n++) { }
|
||||
}
|
||||
return tokens.ToArray();
|
||||
}
|
||||
|
||||
public static String ToMark(this string me, char mark)
|
||||
{
|
||||
int i = me.IndexOf(mark);
|
||||
if (i == -1)
|
||||
return me;
|
||||
return me.Substring(0,i);
|
||||
}
|
||||
public static String FromMark(this string me, char mark)
|
||||
{
|
||||
int i = me.IndexOf(mark);
|
||||
if (i == -1)
|
||||
return "";
|
||||
return me.Substring(i + 1);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections;
|
||||
using System.Runtime.InteropServices;
|
||||
namespace appsrv.mime
|
||||
{
|
||||
public static class ArrayExtensions
|
||||
{
|
||||
|
||||
public static int Find<T>(this T[] me, T[] pattern) where T : class
|
||||
{
|
||||
for (int n = 0; n < (me.Length - pattern.Length); n++)
|
||||
{
|
||||
for (int p = 0; p < pattern.Length; p++)
|
||||
{
|
||||
if (pattern[p] != me[n + p])
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static bool SequenceEquals(this byte[] me, byte[] cmp)
|
||||
{
|
||||
if (me.Length != cmp.Length)
|
||||
return false;
|
||||
|
||||
for (int n = 0; n < me.Length; n++)
|
||||
{
|
||||
if (me[n] != cmp[n])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static int Find(this byte[] me, byte[] pattern)
|
||||
{
|
||||
return Find(me, pattern, 0);
|
||||
}
|
||||
public static int Find(this byte[] me, byte[] pattern, int start)
|
||||
{
|
||||
for (int n = start; n < (me.Length - pattern.Length); n++)
|
||||
{
|
||||
int p;
|
||||
for (p = 0; p < pattern.Length; p++)
|
||||
{
|
||||
if (pattern[p] != me[n + p])
|
||||
break;
|
||||
}
|
||||
if (p == pattern.Length)
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static IEnumerable<byte[]> Split(this byte[] me,byte[] pattern)
|
||||
{
|
||||
return new ByteArrayEnumerable(me, pattern);
|
||||
}
|
||||
|
||||
class ByteArrayEnumerable : IEnumerable<byte[]>
|
||||
{
|
||||
public byte[] Source { get; }
|
||||
public byte[] Pattern { get; }
|
||||
|
||||
public ByteArrayEnumerable(byte[] me, byte[] pattern)
|
||||
{
|
||||
Source = me;
|
||||
Pattern = pattern;
|
||||
}
|
||||
|
||||
public IEnumerator<byte[]> GetEnumerator()
|
||||
{
|
||||
return new ByteArraySplitter(Source, Pattern);
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
|
||||
class ByteArraySplitter : IEnumerator<byte[]>
|
||||
{
|
||||
public byte[] Source { get; }
|
||||
public byte[] Pattern { get; }
|
||||
|
||||
public int CurrentIndex { get; protected set; }
|
||||
|
||||
public ByteArraySplitter(byte[] source,byte[] pattern)
|
||||
{
|
||||
Source = source;
|
||||
Pattern = pattern;
|
||||
CurrentIndex = 0;
|
||||
}
|
||||
|
||||
public byte[] Current { get; protected set; }
|
||||
object IEnumerator.Current => Current;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Current = null;
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (CurrentIndex >= Source.Length)
|
||||
return false;
|
||||
|
||||
int p = Source.Find(Pattern, CurrentIndex);
|
||||
|
||||
if (p == -1)
|
||||
{
|
||||
Current = new byte[Source.Length - CurrentIndex];
|
||||
} else {
|
||||
Current = new byte[p - CurrentIndex];
|
||||
}
|
||||
|
||||
Array.Copy(Source, CurrentIndex, Current, 0, Current.Length);
|
||||
CurrentIndex += Current.Length + Pattern.Length;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
CurrentIndex = 0;
|
||||
Current = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
namespace appsrv.mime
|
||||
{
|
||||
public class MimeHeaders : IEnumerable<MimeHeader>
|
||||
{
|
||||
List<MimeHeader> mimeHeaders = new List<MimeHeader>();
|
||||
|
||||
public MimeHeaders()
|
||||
{
|
||||
}
|
||||
|
||||
public MimeHeaders(Stream stream)
|
||||
{
|
||||
ReadHeaders(stream);
|
||||
}
|
||||
|
||||
private void ReadHeaders(Stream stream)
|
||||
{
|
||||
do
|
||||
{
|
||||
byte[] lineBytes = stream.ReadLine();
|
||||
|
||||
if (lineBytes == null)
|
||||
{
|
||||
Console.WriteLine("MIMEHeaders: ReadHeaders(): reached end of stream");
|
||||
break;
|
||||
}
|
||||
|
||||
string rawLine = Encoding.ASCII.GetString(lineBytes);
|
||||
if (rawLine == null)
|
||||
throw new EndOfStreamException("reached end of stream while reading MIME headers");
|
||||
|
||||
if (String.Empty.Equals(rawLine))
|
||||
break;
|
||||
|
||||
string[] kvp = rawLine.Split(new char[] { ':' }, 2);
|
||||
if (kvp.Length != 2)
|
||||
throw new FormatException(String.Format("invalid header line: {0}", rawLine));
|
||||
|
||||
string name = kvp[0].Trim().ToUpper();
|
||||
|
||||
MimeHeader mimeHeader = new MimeHeader(name);
|
||||
MimeReader.DecodeRawHeaderValue(mimeHeader,kvp[1]);
|
||||
|
||||
Add(mimeHeader);
|
||||
|
||||
} while (true);
|
||||
}
|
||||
|
||||
public MimeHeader this[string name] => GetHeaderByName(name);
|
||||
|
||||
|
||||
public bool Contains(string name)
|
||||
{
|
||||
name = name.ToUpper();
|
||||
foreach (MimeHeader header in mimeHeaders)
|
||||
{
|
||||
if (header.Name.Equals(name))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public MimeHeader GetHeaderByName(string name)
|
||||
{
|
||||
name = name.ToUpper();
|
||||
foreach (MimeHeader header in mimeHeaders)
|
||||
{
|
||||
if (header.Name.Equals(name))
|
||||
return header;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public MimeHeader[] GetHeadersByName(string name)
|
||||
{
|
||||
List<MimeHeader> headers = new List<MimeHeader>();
|
||||
|
||||
name = name.ToUpper();
|
||||
foreach (MimeHeader header in mimeHeaders)
|
||||
{
|
||||
if (header.Name.Equals(name))
|
||||
headers.Add(header);
|
||||
}
|
||||
|
||||
return headers.ToArray();
|
||||
}
|
||||
|
||||
public void Add(String name,String value)
|
||||
{
|
||||
Add(new MimeHeader(name, value));
|
||||
}
|
||||
|
||||
public void Add(MimeHeader mimeHeader)
|
||||
{
|
||||
mimeHeaders.Add(mimeHeader);
|
||||
}
|
||||
|
||||
public void Remove(MimeHeader header)
|
||||
{
|
||||
this.mimeHeaders.Remove(header);
|
||||
}
|
||||
public void Remove(string name)
|
||||
{
|
||||
Remove(name, false);
|
||||
}
|
||||
public void Remove(string name,bool removeAll)
|
||||
{
|
||||
foreach (MimeHeader header in mimeHeaders)
|
||||
{
|
||||
if (header.Name.Equals(name))
|
||||
{
|
||||
mimeHeaders.Remove(header);
|
||||
if (!removeAll)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void Add(IEnumerable<MimeHeader> headers)
|
||||
{
|
||||
foreach (MimeHeader mh in headers)
|
||||
Add(mh);
|
||||
}
|
||||
|
||||
public void Set(string name,string value)
|
||||
{
|
||||
Remove(name, true);
|
||||
Add(name, value);
|
||||
}
|
||||
public void Set(MimeHeader header)
|
||||
{
|
||||
Remove(header.Name, true);
|
||||
Add(header);
|
||||
}
|
||||
|
||||
|
||||
public MimeHeader[] ToArray()
|
||||
{
|
||||
return mimeHeaders.ToArray();
|
||||
}
|
||||
|
||||
public IEnumerator<MimeHeader> GetEnumerator()
|
||||
{
|
||||
return mimeHeaders.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable)mimeHeaders).GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
using System;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.Remoting.Messaging;
|
||||
using System.IO;
|
||||
namespace appsrv.mime
|
||||
{
|
||||
public class MIMEMessage
|
||||
{
|
||||
public MIMEMessage Container { get; protected set; }
|
||||
|
||||
public MimeHeaders Headers { get; }
|
||||
public byte[] RawContent { get; set; }
|
||||
|
||||
public Encoding Encoding { get; set; }
|
||||
|
||||
public MIMEMessage[] Parts => parts.ToArray();
|
||||
|
||||
List<MIMEMessage> parts = new List<MIMEMessage>();
|
||||
|
||||
public MimeType ContentType {
|
||||
get {
|
||||
return null;
|
||||
// return new MimeType(Headers.GetHeaderByName("CONTENT-TYPE").Value);
|
||||
}
|
||||
}
|
||||
|
||||
public MIMEMessage()
|
||||
{
|
||||
Container = null;
|
||||
Headers = new MimeHeaders();
|
||||
Encoding = Encoding.UTF8;
|
||||
}
|
||||
|
||||
public MIMEMessage(MIMEMessage container)
|
||||
{
|
||||
Container = container;
|
||||
Headers = new MimeHeaders();
|
||||
Encoding = Encoding.UTF8;
|
||||
}
|
||||
|
||||
public MIMEMessage(byte[] source)
|
||||
:this(new MemoryStream(source))
|
||||
{
|
||||
}
|
||||
|
||||
public MIMEMessage(Stream stream,bool httpMode = false)
|
||||
{
|
||||
Encoding = Encoding.ASCII;
|
||||
Headers = new MimeHeaders(stream);
|
||||
|
||||
ReadRawContent(stream,httpMode);
|
||||
|
||||
if (!Headers.Contains("content-type"))
|
||||
{
|
||||
Headers.Set("content-type", "text/plain");
|
||||
}
|
||||
|
||||
if (Headers.GetHeaderByName("content-type").Value.ToUpper().StartsWith("MULTIPART/", StringComparison.InvariantCulture))
|
||||
{
|
||||
DecodeMultipart();
|
||||
}
|
||||
else if (Headers.GetHeaderByName("content-type").Value.ToUpper().StartsWith("TEXT/", StringComparison.InvariantCulture))
|
||||
{
|
||||
MimeHeader ctype = Headers.GetHeaderByName("content-type");
|
||||
if (ctype.ContainsParameter("charset"))
|
||||
{
|
||||
Encoding = Encoding.GetEncoding(ctype.GetParameter("charset"));
|
||||
}
|
||||
else
|
||||
{
|
||||
//Encoding = Encoding.GetEncoding("ISO-8859-1");
|
||||
Encoding = Encoding.UTF8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadRawContent(Stream stream,bool httpMode)
|
||||
{
|
||||
if (Headers.Contains("content-length"))
|
||||
{
|
||||
int length = int.Parse(Headers.GetHeaderByName("content-length").Value);
|
||||
RawContent = new byte[length];
|
||||
stream.Read(RawContent, 0, length);
|
||||
} else if (!httpMode){
|
||||
MemoryStream memoryStream = new MemoryStream();
|
||||
stream.CopyTo(memoryStream);
|
||||
memoryStream.Position = 0;
|
||||
RawContent = new byte[memoryStream.Length];
|
||||
memoryStream.Read(RawContent, 0, RawContent.Length);
|
||||
} else {
|
||||
RawContent = new byte[0];
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void DecodeMultipart()
|
||||
{
|
||||
byte[] rawBoundary = Encoding.ASCII.GetBytes(
|
||||
String.Format("--{0}", Headers.GetHeaderByName("Content-Type").GetParameter("Boundary"))
|
||||
);
|
||||
byte[] boundary = Encoding.ASCII.GetBytes(
|
||||
String.Format("\r\n--{0}", Headers.GetHeaderByName("Content-Type").GetParameter("Boundary"))
|
||||
);
|
||||
|
||||
byte[] lastBoundary = new byte[] { 45, 45, 13, 10 };
|
||||
|
||||
Console.WriteLine("-----------[ RawContent ]-------------");
|
||||
Console.WriteLine(Encoding.UTF8.GetString(RawContent));
|
||||
Console.WriteLine("--------------------------------------");
|
||||
|
||||
|
||||
foreach (byte[] rawpart in RawContent.Split(boundary))
|
||||
{
|
||||
int p = rawpart.Find(rawBoundary);
|
||||
|
||||
if (rawpart.SequenceEquals(lastBoundary))
|
||||
break;
|
||||
|
||||
MemoryStream partStream = new MemoryStream(rawpart);
|
||||
|
||||
/*
|
||||
* First boundary may miss \r\n... so we skip it manually
|
||||
*/
|
||||
if (p != -1)
|
||||
{
|
||||
partStream.Seek(p + rawBoundary.Length, SeekOrigin.Current);
|
||||
}
|
||||
|
||||
Console.WriteLine("------------[ RAWPART ]---------------");
|
||||
Console.WriteLine(Encoding.UTF8.GetString(rawpart));
|
||||
Console.WriteLine("--------------------------------------");
|
||||
|
||||
partStream.ReadLine();
|
||||
|
||||
MIMEMessage part = new MIMEMessage(partStream);
|
||||
AddPart(part);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public String Value
|
||||
{
|
||||
get {
|
||||
return Encoding.GetString(RawContent);
|
||||
}
|
||||
set {
|
||||
RawContent = Encoding.GetBytes(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddPart(MIMEMessage part)
|
||||
{
|
||||
parts.Add(part);
|
||||
part.Container = this;
|
||||
}
|
||||
|
||||
public void RemovePart(MIMEMessage part)
|
||||
{
|
||||
parts.Remove(part);
|
||||
part.Container = null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
namespace appsrv.mime
|
||||
{
|
||||
public class MimeHeader
|
||||
{
|
||||
public String Name { get; set; }
|
||||
public String Value { get; set; }
|
||||
|
||||
|
||||
Dictionary<string, string> parameters = new Dictionary<string, string>();
|
||||
|
||||
public MimeHeader(String name)
|
||||
{
|
||||
Name = name;
|
||||
Value = "";
|
||||
}
|
||||
|
||||
public MimeHeader(String name, String value)
|
||||
:this(name,value,null)
|
||||
{
|
||||
}
|
||||
|
||||
public MimeHeader(String name,String value,IEnumerable<KeyValuePair<string,string>> parameters)
|
||||
{
|
||||
Name = name.ToUpper();
|
||||
|
||||
MimeReader.DecodeRawHeaderValue(this, value);
|
||||
|
||||
if (parameters != null)
|
||||
{
|
||||
foreach (KeyValuePair<string, string> kvp in parameters)
|
||||
this.parameters.Add(kvp.Key,kvp.Value);
|
||||
}
|
||||
}
|
||||
|
||||
public string this[string key]
|
||||
{
|
||||
get => GetParameter(key);
|
||||
set => SetParameter(key, value);
|
||||
}
|
||||
|
||||
public string GetParameter(string key)
|
||||
{
|
||||
return this.parameters[key.ToUpper()];
|
||||
}
|
||||
public void SetParameter(string key,string value)
|
||||
{
|
||||
this.parameters[key.ToUpper()] = value;
|
||||
}
|
||||
public bool ContainsParameter(string key)
|
||||
{
|
||||
return this.parameters.ContainsKey(key.ToUpper());
|
||||
}
|
||||
|
||||
public string RawValue {
|
||||
get {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
stringBuilder.Append(Value);
|
||||
foreach (KeyValuePair<string, string> kvp in parameters)
|
||||
{
|
||||
stringBuilder.AppendFormat(";{0}={1}", kvp.Key, kvp.Value);
|
||||
}
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
stringBuilder.AppendFormat("{0}: {1}", Name, Value);
|
||||
foreach (KeyValuePair<string,string> kvp in parameters)
|
||||
{
|
||||
stringBuilder.AppendFormat(";{0}={1}", kvp.Key, kvp.Value);
|
||||
}
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -9,7 +9,9 @@ namespace appsrv.mime
|
|||
{"html","text/html"},
|
||||
{"htm","text/html"},
|
||||
{"xml","text/xml"},
|
||||
{"txt","text/plain"}
|
||||
{"txt","text/plain"},
|
||||
{"css","text/css"},
|
||||
{"js","text/javascript"}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,181 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
namespace appsrv.mime
|
||||
{
|
||||
[Flags]
|
||||
public enum MimeHeaderReadOptions {
|
||||
NONE,
|
||||
CONCAT_DUPLICATE_KEYS
|
||||
}
|
||||
|
||||
public class MimeReader
|
||||
{
|
||||
public MimeStreamReader Reader { get; }
|
||||
|
||||
public MimeReader(TextReader reader)
|
||||
{
|
||||
Reader = new MimeStreamReader(reader);
|
||||
}
|
||||
|
||||
public MimeReader(Stream stream){
|
||||
Reader = new MimeStreamReader(stream);
|
||||
}
|
||||
|
||||
public MIMEMessage ReadMessage()
|
||||
{
|
||||
MIMEMessage message = new MIMEMessage();
|
||||
IEnumerable<MimeHeader> headers = ReadHeaders(MimeHeaderReadOptions.NONE);
|
||||
|
||||
message.Headers.Add(headers);
|
||||
|
||||
if (message.ContentType.Type.Equals("MULTIPART"))
|
||||
{
|
||||
ReadParts(message);
|
||||
}
|
||||
else if (message.Headers.Contains("Content-Length"))
|
||||
{
|
||||
int length = int.Parse(message.Headers.GetHeaderByName("Content-Length").Value);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
public void ReadParts(MIMEMessage message)
|
||||
{
|
||||
string b = message.Headers.GetHeaderByName("Content-Type").GetParameter("BOUNDARY");
|
||||
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
string endofline = null;
|
||||
|
||||
do
|
||||
{
|
||||
String part = ReadPart(b);
|
||||
|
||||
Console.WriteLine("PART: __|{0}|__", part);
|
||||
|
||||
endofline = Reader.ReadLine();
|
||||
} while ((endofline != null) && (!endofline.Equals("--")));
|
||||
|
||||
}
|
||||
|
||||
private string ReadPart(String boundary)
|
||||
{
|
||||
string lookup = String.Format("\r\n--{0}", boundary);
|
||||
return Reader.ReadToBoundary(lookup);
|
||||
}
|
||||
|
||||
public IEnumerable<MimeHeader> ReadHeaders()
|
||||
{
|
||||
return ReadHeaders(MimeHeaderReadOptions.NONE);
|
||||
}
|
||||
public IEnumerable<MimeHeader> ReadHeaders(MimeHeaderReadOptions readOptions)
|
||||
{
|
||||
Dictionary<string, MimeHeader> readHeaders = new Dictionary<string, MimeHeader>();
|
||||
|
||||
do
|
||||
{
|
||||
string rawLine = Reader.ReadLine();
|
||||
if (rawLine == null)
|
||||
throw new EndOfStreamException("reached end of stream while reading MIME headers");
|
||||
|
||||
if (String.Empty.Equals(rawLine))
|
||||
break;
|
||||
|
||||
string[] kvp = rawLine.Split(new char[] { ':' }, 2);
|
||||
if (kvp.Length != 2)
|
||||
throw new FormatException(String.Format("invalid header line: {0}",rawLine));
|
||||
|
||||
string name = kvp[0].Trim().ToUpper();
|
||||
string[] valueTokens = kvp[1].Split(new char[] { ';' });
|
||||
|
||||
string value = valueTokens[0];
|
||||
|
||||
MimeHeader mimeHeader = new MimeHeader(name, value);
|
||||
|
||||
if (((readOptions & MimeHeaderReadOptions.CONCAT_DUPLICATE_KEYS) == MimeHeaderReadOptions.CONCAT_DUPLICATE_KEYS) && readHeaders.ContainsKey(name))
|
||||
{
|
||||
mimeHeader = readHeaders[name];
|
||||
mimeHeader.Value = String.Format("{0},{1}", mimeHeader.Value, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
mimeHeader = new MimeHeader(name, value);
|
||||
}
|
||||
|
||||
for (int n = 1; n < valueTokens.Length;n++)
|
||||
{
|
||||
string[] partokens = valueTokens[n].Split(new char[] { '=' }, 2);
|
||||
mimeHeader.SetParameter(partokens[0].Trim().ToUpper(), partokens[1].Trim());
|
||||
}
|
||||
|
||||
} while (true);
|
||||
|
||||
return readHeaders.Values;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static void DecodeRawHeaderValue(MimeHeader mimeHeader,string rawValue)
|
||||
{
|
||||
string[] rawTokens = rawValue.Split(new char[] { ';' });
|
||||
mimeHeader.Value = rawTokens[0].Trim();
|
||||
|
||||
for (int n = 1; n < rawTokens.Length; n++)
|
||||
{
|
||||
string[] partokens = rawTokens[n].Split(new char[] { '=' }, 2);
|
||||
if (partokens.Length == 2)
|
||||
{
|
||||
string pval = partokens[1].Trim();
|
||||
if (pval.StartsWith("\"") && pval.EndsWith("\""))
|
||||
pval = pval.Substring(1, pval.Length - 2);
|
||||
mimeHeader.SetParameter(partokens[0].Trim().ToUpper(), pval);
|
||||
}
|
||||
else
|
||||
{
|
||||
mimeHeader.SetParameter(partokens[0].Trim().ToUpper(), "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static readonly int CR = 0x0d;
|
||||
static readonly int LF = 0x0a;
|
||||
|
||||
public static string ReadLineFromStream(Stream stream,Encoding encoding)
|
||||
{
|
||||
byte[] bytes = ReadLineFromStream(stream);
|
||||
if (bytes != null)
|
||||
return encoding.GetString(bytes);
|
||||
return null;
|
||||
}
|
||||
public static byte[] ReadLineFromStream(Stream stream)
|
||||
{
|
||||
List<byte> line = new List<byte>();
|
||||
int rb = 0;
|
||||
|
||||
while (rb != LF)
|
||||
{
|
||||
rb = stream.ReadByte();
|
||||
if (rb < 0){
|
||||
|
||||
if (line.Count == 0)
|
||||
return null;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
line.Add((byte)rb);
|
||||
}
|
||||
|
||||
return line.ToArray();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
namespace appsrv.mime
|
||||
{
|
||||
public class MimeStreamReader
|
||||
{
|
||||
public Stream Stream { get; }
|
||||
public TextReader TextReader { get; }
|
||||
|
||||
Stack<char> pushbacks = new Stack<char>();
|
||||
|
||||
public MimeStreamReader(Stream stream)
|
||||
{
|
||||
Stream = stream;
|
||||
TextReader = new StreamReader(Stream, Encoding.ASCII);
|
||||
}
|
||||
|
||||
public MimeStreamReader(TextReader textReader)
|
||||
{
|
||||
Stream = null;
|
||||
TextReader = textReader;
|
||||
}
|
||||
|
||||
|
||||
public int Read()
|
||||
{
|
||||
if (pushbacks.Count > 0)
|
||||
{
|
||||
return pushbacks.Pop();
|
||||
}
|
||||
return TextReader.Read();
|
||||
}
|
||||
|
||||
public void Push(char ch)
|
||||
{
|
||||
pushbacks.Push(ch);
|
||||
}
|
||||
|
||||
public string ReadToBoundary(string boundary)
|
||||
{
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
char[] buffer = new char[boundary.Length];
|
||||
|
||||
for (int n = 0; n < buffer.Length; n++)
|
||||
{
|
||||
int ch = Read();
|
||||
if (ch == -1)
|
||||
{
|
||||
for (int p = 0; p < n; p++)
|
||||
{
|
||||
stringBuilder.Append(buffer[p]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
buffer[n] = (char)ch;
|
||||
|
||||
if (buffer[n] != boundary[n])
|
||||
{
|
||||
while (n > 0)
|
||||
{
|
||||
Push(buffer[n--]);
|
||||
}
|
||||
stringBuilder.Append(buffer[n--]);
|
||||
}
|
||||
}
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
public string ReadLine()
|
||||
{
|
||||
return ReadToBoundary("\r\n");
|
||||
}
|
||||
|
||||
public string Read(int n)
|
||||
{
|
||||
char[] buffer = new char[n];
|
||||
TextReader.ReadBlock(buffer, 0, n);
|
||||
return new string(buffer);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
using System;
|
||||
namespace appsrv.mime
|
||||
{
|
||||
public class MimeType
|
||||
{
|
||||
public string Type { get; }
|
||||
public string SubType { get; }
|
||||
|
||||
public MimeType(string contentType)
|
||||
{
|
||||
string[] t = contentType.Split(new char[] { '/' }, 2);
|
||||
Type = t[0].ToUpper();
|
||||
SubType = t[1].ToUpper();
|
||||
}
|
||||
public MimeType(string type,string subtype)
|
||||
{
|
||||
Type = type;
|
||||
SubType = subtype;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
namespace appsrv.mime
|
||||
{
|
||||
public static class StreamExtensions
|
||||
{
|
||||
static readonly int CR = 0x0d;
|
||||
static readonly int LF = 0x0a;
|
||||
|
||||
public static string ReadLine(this Stream stream, Encoding encoding)
|
||||
{
|
||||
byte[] bytes = ReadLine(stream);
|
||||
if (bytes != null)
|
||||
return encoding.GetString(bytes);
|
||||
return null;
|
||||
}
|
||||
public static byte[] ReadLine(this Stream stream)
|
||||
{
|
||||
List<byte> line = new List<byte>();
|
||||
int rb = 0;
|
||||
|
||||
while (rb != LF)
|
||||
{
|
||||
rb = stream.ReadByte();
|
||||
if (rb < 0)
|
||||
{
|
||||
|
||||
if (line.Count == 0)
|
||||
return null;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if ((rb != CR) && (rb != LF))
|
||||
line.Add((byte)rb);
|
||||
}
|
||||
|
||||
return line.ToArray();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Newtonsoft.Json" version="11.0.2" targetFramework="net461" />
|
||||
</packages>
|
|
@ -3,13 +3,14 @@ using System.IO;
|
|||
using appsrv.server;
|
||||
using System.Collections.Generic;
|
||||
using appsrv.exceptions;
|
||||
using System.Text;
|
||||
using appsrv.templates;
|
||||
|
||||
namespace appsrv.resources
|
||||
{
|
||||
public class DirectoryResource : Resource
|
||||
{
|
||||
public DirectoryInfo DirectoryInfo { get; }
|
||||
public bool IndexingEnabled { get; set; }
|
||||
|
||||
public DirectoryResource(DirectoryInfo directoryInfo)
|
||||
:this(directoryInfo,null)
|
||||
|
@ -19,38 +20,74 @@ namespace appsrv.resources
|
|||
:base(directoryInfo.Name,container)
|
||||
{
|
||||
DirectoryInfo = directoryInfo;
|
||||
Console.WriteLine("RES: + {0}", this);
|
||||
}
|
||||
|
||||
public override void Hit(Stack<string> requestPath, HttpRequest request)
|
||||
public override Resource Lookup(String name){
|
||||
Resource resource = base.Lookup(name);
|
||||
if (resource != null)
|
||||
{
|
||||
return resource;
|
||||
}
|
||||
|
||||
String newName = System.IO.Path.Combine(DirectoryInfo.FullName, name);
|
||||
|
||||
if (Directory.Exists(newName))
|
||||
{
|
||||
return new DirectoryResource(new DirectoryInfo(newName), this);
|
||||
}
|
||||
|
||||
if (File.Exists(newName))
|
||||
{
|
||||
FileInfo fileInfo = new FileInfo(newName);
|
||||
if (fileInfo.Extension.Equals(".hfrm")){
|
||||
return new Template(fileInfo.FullName, this);
|
||||
} else {
|
||||
return new FileResource(fileInfo, this);
|
||||
}
|
||||
}
|
||||
//throw new ResourceNotFoundException(Path, name);
|
||||
return null;
|
||||
}
|
||||
public override string[] List()
|
||||
{
|
||||
List<String> result = new List<string>();
|
||||
|
||||
result.AddRange(base.List());
|
||||
|
||||
result.AddRange(Directory.GetFiles(DirectoryInfo.FullName));
|
||||
result.AddRange(Directory.GetDirectories(DirectoryInfo.FullName));
|
||||
|
||||
String[] list = new string[result.Count];
|
||||
|
||||
for (int n = 0; n < list.Length;n++)
|
||||
{
|
||||
list[n] = System.IO.Path.GetFileName(result[n]);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public override void Hit(Stack<string> requestPath, Request request)
|
||||
{
|
||||
if (requestPath.Count > 0)
|
||||
{
|
||||
String nextResourceName = requestPath.Pop();
|
||||
Resource nextResource = Lookup(nextResourceName);
|
||||
|
||||
DirectoryInfo[] directoryInfos = DirectoryInfo.GetDirectories(nextResourceName);
|
||||
if (directoryInfos.Length == 1){
|
||||
DirectoryResource directoryResource = new DirectoryResource(directoryInfos[0],this);
|
||||
directoryResource.Request(requestPath, request);
|
||||
} else {
|
||||
FileInfo[] fileInfos = DirectoryInfo.GetFiles(nextResourceName);
|
||||
if (fileInfos.Length == 1){
|
||||
FileResource fileResource = new FileResource(fileInfos[0],this);
|
||||
fileResource.Request(requestPath, request);
|
||||
} else {
|
||||
throw new ResourceNotFoundException(Path, nextResourceName);
|
||||
}
|
||||
}
|
||||
if (nextResource == null)
|
||||
throw new FileNotFoundException();
|
||||
|
||||
nextResource.Request(requestPath, request);
|
||||
} else {
|
||||
if (IndexingEnabled){
|
||||
|
||||
// ToDo: Create index...
|
||||
|
||||
} else {
|
||||
base.Hit(requestPath, request);
|
||||
}
|
||||
base.Hit(requestPath, request);
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format("[DirectoryResource Path={0}]",DirectoryInfo.FullName);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,17 +18,17 @@ namespace appsrv.resources
|
|||
FileInfo = fileInfo;
|
||||
}
|
||||
|
||||
public override void Hit(Stack<string> requestPath, HttpRequest request)
|
||||
public override void Hit(Stack<string> requestPath, Request request)
|
||||
{
|
||||
if ((requestPath.Count > 0) && !DiscardRequestPath){
|
||||
throw new ApplicationServerException(String.Format("No resources below {0}",Path));
|
||||
} else {
|
||||
|
||||
request.SetResponseHeader("Content-Type", MimeHelper.GuessMIMEFromFilename(FileInfo.Name));
|
||||
Response response = request.Response;
|
||||
response.Headers.Add("Content-type", MimeHelper.GuessMIMEFromFilename(FileInfo.Name));
|
||||
|
||||
using (FileStream fileStream = new FileStream(FileInfo.FullName,FileMode.Open))
|
||||
{
|
||||
fileStream.CopyTo(request.ResponseStream);
|
||||
fileStream.CopyTo(response.Stream);
|
||||
fileStream.Close();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,16 +4,25 @@ using System.Collections.Generic;
|
|||
using System.Dynamic;
|
||||
using appsrv.exceptions;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using appsrv.templates;
|
||||
|
||||
namespace appsrv.resources
|
||||
{
|
||||
public abstract class Resource
|
||||
{
|
||||
private Application application;
|
||||
|
||||
public Resource Container { get; }
|
||||
public String Name { get; }
|
||||
public Resource DefaultResource { get; set; }
|
||||
|
||||
public bool IndexingEnabled { get; set; }
|
||||
|
||||
public virtual DateTime LastModification { get; protected set; } = DateTime.Now;
|
||||
|
||||
Dictionary<String, Resource> resources = new Dictionary<string, Resource>();
|
||||
|
||||
|
||||
public Resource(String name)
|
||||
{
|
||||
Name = name;
|
||||
|
@ -26,6 +35,32 @@ namespace appsrv.resources
|
|||
container.Add(this);
|
||||
}
|
||||
|
||||
public Application Application {
|
||||
get {
|
||||
if (Container != null)
|
||||
return Container.Application;
|
||||
return application;
|
||||
}
|
||||
set {
|
||||
application = value;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual Resource Lookup(String name)
|
||||
{
|
||||
if (resources.ContainsKey(name))
|
||||
return resources[name];
|
||||
|
||||
if (Container != null)
|
||||
return Container.Lookup(name);
|
||||
|
||||
return null;
|
||||
}
|
||||
public virtual String[] List()
|
||||
{
|
||||
return resources.Keys.ToArray();
|
||||
}
|
||||
|
||||
protected virtual void Add(Resource resource){
|
||||
resources.Add(resource.Name, resource);
|
||||
}
|
||||
|
@ -35,16 +70,16 @@ namespace appsrv.resources
|
|||
}
|
||||
}
|
||||
|
||||
public bool Contains(String resName)
|
||||
public virtual bool Contains(String resName)
|
||||
{
|
||||
return resources.ContainsKey(resName);
|
||||
return Lookup(resName) != null;
|
||||
}
|
||||
public bool Contains(Resource resource)
|
||||
public virtual bool Contains(Resource resource)
|
||||
{
|
||||
return resources.ContainsValue(resource);
|
||||
return (resource.Container == this) && (Lookup(resource.Name) == resource);
|
||||
}
|
||||
|
||||
public ISet<Resource> Resources { get => new HashSet<Resource>(resources.Values); }
|
||||
public virtual ISet<Resource> Resources => new HashSet<Resource>(resources.Values);
|
||||
|
||||
public Resource Root {
|
||||
get {
|
||||
|
@ -74,35 +109,52 @@ namespace appsrv.resources
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public virtual Resource this[string name]{
|
||||
get => resources[name];
|
||||
public T ThrowIfNull<T,E>(T value) where T: class where E: Exception
|
||||
{
|
||||
if (value == null)
|
||||
throw Activator.CreateInstance<E>();
|
||||
return value;
|
||||
}
|
||||
|
||||
public virtual void Request(Stack<String> requestPath, HttpRequest request)
|
||||
public virtual Resource this[string name]{
|
||||
get => ThrowIfNull<Resource, KeyNotFoundException>(Lookup(name));
|
||||
}
|
||||
|
||||
public virtual void Request(Stack<String> requestPath, Request request)
|
||||
{
|
||||
try
|
||||
{
|
||||
if ((requestPath.Count > 0) && (Contains(requestPath.Peek())))
|
||||
{
|
||||
this[requestPath.Pop()].Request(requestPath, request);
|
||||
Lookup(requestPath.Pop()).Request(requestPath, request);
|
||||
}
|
||||
else
|
||||
{
|
||||
Hit(requestPath, request);
|
||||
if ((requestPath.Count == 0) && (DefaultResource != null))
|
||||
{
|
||||
DefaultResource.Request(requestPath, request);
|
||||
}
|
||||
else
|
||||
{
|
||||
Hit(requestPath, request);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ApplicationServerException ase)
|
||||
{
|
||||
HandleException(ase);
|
||||
HandleException(ase,request);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Hit(Stack<String> requestPath, HttpRequest request){
|
||||
public virtual void Hit(Stack<String> requestPath, Request request){
|
||||
if (requestPath.Count > 0){
|
||||
throw new ResourceNotFoundException(Path, requestPath.Peek());
|
||||
} else {
|
||||
throw new ApplicationServerException("unimplemented resource has been hit");
|
||||
if (IndexingEnabled){
|
||||
CreateIndex(request);
|
||||
} else {
|
||||
throw new ApplicationServerException("unimplemented resource has been hit");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -132,10 +184,39 @@ namespace appsrv.resources
|
|||
|
||||
|
||||
|
||||
protected void HandleException(ApplicationServerException ase){
|
||||
Console.WriteLine("ASE: " + ase.ToString());
|
||||
protected void HandleException(ApplicationServerException ase,Request request){
|
||||
Response response = request.Response;
|
||||
|
||||
response.StatusCode = 500;
|
||||
response.Headers.Add("Content-type", "text/html");
|
||||
response.Writer.WriteLine("<html><head><title>Exception caught: {0}</title></head><body>",ase);
|
||||
response.Writer.WriteLine("<h1>Exception caught: {0}</h1><p>{1}</p>",ase,ase.Message);
|
||||
response.Writer.WriteLine("<p><code>{0}</code></p></body></html>",ase.StackTrace);
|
||||
}
|
||||
|
||||
protected void CreateIndex(Request request){
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
Response response = request.Response;
|
||||
|
||||
stringBuilder.AppendFormat("<html><head><title>{0}</title></head>", Path);
|
||||
stringBuilder.AppendFormat("<body><h1>Index of {0}</h1>", Path);
|
||||
foreach (String name in List())
|
||||
{
|
||||
String p = Path;
|
||||
if (p.Equals("/"))
|
||||
p = "";
|
||||
|
||||
stringBuilder.AppendFormat("<a href=\"{0}/{1}\">{1}</a><br/>", p, name);
|
||||
}
|
||||
stringBuilder.Append("</body></html>");
|
||||
|
||||
response.Headers.Add("Content-type", "text/html");
|
||||
response.Writer.Write(stringBuilder.ToString());
|
||||
}
|
||||
|
||||
public virtual void ApplyParameters(Dictionary<String,String> parameters)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace appsrv.resources
|
|||
throw new ArgumentOutOfRangeException("This resource can't have children");
|
||||
}
|
||||
|
||||
public override void Request(Stack<string> requestPath, HttpRequest request)
|
||||
public override void Request(Stack<string> requestPath, Request request)
|
||||
{
|
||||
Target.Request(requestPath, request);
|
||||
}
|
||||
|
|
|
@ -1,92 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using appsrv.attributes;
|
||||
using appsrv.server;
|
||||
using System.Text;
|
||||
|
||||
namespace appsrv.resources
|
||||
{
|
||||
public class StaticClassResource : Resource
|
||||
{
|
||||
Type Type { get; }
|
||||
|
||||
|
||||
Dictionary<string, FieldInfo> callableFields = new Dictionary<string, FieldInfo>();
|
||||
|
||||
public StaticClassResource(Type type,Resource container)
|
||||
:base(type.Name,container)
|
||||
{
|
||||
Type = type;
|
||||
|
||||
foreach (MethodInfo methodInfo in Type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)){
|
||||
WebCallable webCallable = methodInfo.GetCustomAttribute<WebCallable>();
|
||||
if (webCallable != null){
|
||||
new CallableMethodResource(methodInfo, this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
class CallableMethodResource : Resource
|
||||
{
|
||||
MethodInfo MethodInfo { get; }
|
||||
WebCallable WebCallable { get; }
|
||||
|
||||
public CallableMethodResource(MethodInfo methodInfo,Resource container)
|
||||
:base(
|
||||
methodInfo.GetCustomAttribute<WebCallable>() != null ?
|
||||
(methodInfo.GetCustomAttribute<WebCallable>().Name != null) ? methodInfo.GetCustomAttribute<WebCallable>().Name : methodInfo.Name
|
||||
:methodInfo.Name,
|
||||
container
|
||||
)
|
||||
{
|
||||
MethodInfo = methodInfo;
|
||||
WebCallable = methodInfo.GetCustomAttribute<WebCallable>();
|
||||
}
|
||||
|
||||
private object[] ReadParameters(HttpRequest request)
|
||||
{
|
||||
ParameterInfo[] parameters = MethodInfo.GetParameters();
|
||||
object[] p = new object[ parameters.Length ];
|
||||
|
||||
for (int n = 0; n < parameters.Length;n++)
|
||||
{
|
||||
ParameterInfo parameterInfo = parameters[n];
|
||||
object pvalue = request.Query[parameterInfo.Name];
|
||||
p[n] = Convert.ChangeType(pvalue, parameterInfo.ParameterType );
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
private void SerializeResult(HttpRequest request,object result){
|
||||
switch (WebCallable.Serialization){
|
||||
case Serialization.PLAIN:
|
||||
SerializePlain(request, result);
|
||||
break;
|
||||
case Serialization.JSON:
|
||||
break;
|
||||
case Serialization.XML:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void SerializePlain(HttpRequest request,object result){
|
||||
request.SetResponseHeader("Content-Type", "text/plain");
|
||||
byte[] plain = Encoding.UTF8.GetBytes(result.ToString());
|
||||
request.ResponseStream.Write(plain, 0, plain.Length);
|
||||
}
|
||||
|
||||
public override void Hit(Stack<string> requestPath, HttpRequest request)
|
||||
{
|
||||
object[] p = ReadParameters(request);
|
||||
object result = MethodInfo.Invoke(null, p);
|
||||
SerializeResult(request, result);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using appsrv.server;
|
||||
using appsrv.templates;
|
||||
using System.Reflection;
|
||||
using appsrv.mime;
|
||||
namespace appsrv.resources.reflection
|
||||
{
|
||||
public partial class ReflectiveResource
|
||||
{
|
||||
class ReflectiveEdit
|
||||
{
|
||||
public ReflectiveResource Resource { get; }
|
||||
public Guid ID { get; } = Guid.NewGuid();
|
||||
|
||||
public object Value { get; }
|
||||
public Template Template { get; }
|
||||
|
||||
public DateTime LastUsage { get; protected set; }
|
||||
public bool TimedOut => (DateTime.Now - LastUsage) > TimeSpan.FromSeconds(300);
|
||||
|
||||
public ReflectiveEdit(ReflectiveResource resource, object value, Template template)
|
||||
{
|
||||
Resource = resource;
|
||||
Value = value;
|
||||
Template = template;
|
||||
|
||||
Touch();
|
||||
}
|
||||
|
||||
public void Touch()
|
||||
{
|
||||
LastUsage = DateTime.Now;
|
||||
}
|
||||
|
||||
public void Render(Request request)
|
||||
{
|
||||
Touch();
|
||||
|
||||
ApplyUpdates(request);
|
||||
|
||||
Dictionary<string, object> parameters = new Dictionary<string, object>();
|
||||
parameters["REDIT_PATH"] = String.Format("{0}/${1}", Resource.Path, ID.ToString());
|
||||
|
||||
Template.Render(request, Value, parameters);
|
||||
}
|
||||
|
||||
public void ApplyUpdates(Request request)
|
||||
{
|
||||
foreach (FieldInfo fieldInfo in Value.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public))
|
||||
{
|
||||
string formKey = String.Format("this_{0}", fieldInfo.Name);
|
||||
MIMEMessage p = request.GetParameter(formKey);
|
||||
if (p != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
object fieldValue = Convert.ChangeType(p.Value, fieldInfo.FieldType);
|
||||
fieldInfo.SetValue(Value, fieldValue);
|
||||
}
|
||||
catch (InvalidCastException ice)
|
||||
{
|
||||
Console.WriteLine("ReflectiveEdit: Invalid Cast for {0} assigning value {1}", fieldInfo, p.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (PropertyInfo propertyInfo in Value.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
|
||||
{
|
||||
if (!propertyInfo.CanWrite)
|
||||
continue;
|
||||
|
||||
string formKey = String.Format("this_{0}", propertyInfo.Name);
|
||||
MIMEMessage p = request.GetParameter(formKey);
|
||||
if (p != null)
|
||||
{
|
||||
object fieldValue = Convert.ChangeType(p.Value, propertyInfo.PropertyType);
|
||||
propertyInfo.SetValue(Value, fieldValue);
|
||||
}
|
||||
}
|
||||
|
||||
MIMEMessage mSubmit = request.GetParameter("REDIT_SUBMIT");
|
||||
if (mSubmit != null)
|
||||
{
|
||||
if (mSubmit.Value.Equals("save"))
|
||||
{
|
||||
if (Resource.MethodSafe != null)
|
||||
{
|
||||
Resource.MethodSafe.Invoke(Resource.NativeInstance, new object[] { Value });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
namespace appsrv.resources.reflection
|
||||
{
|
||||
public partial class ReflectiveResource
|
||||
{
|
||||
class ReflectiveEditStore
|
||||
{
|
||||
public ReflectiveResource ReflectiveResource { get; }
|
||||
|
||||
Dictionary<Guid, ReflectiveEdit> edits = new Dictionary<Guid, ReflectiveEdit>();
|
||||
|
||||
public ReflectiveEditStore(ReflectiveResource resource)
|
||||
{
|
||||
ReflectiveResource = resource;
|
||||
}
|
||||
|
||||
public ReflectiveEdit Find(Guid id)
|
||||
{
|
||||
Cleanup();
|
||||
|
||||
if (edits.ContainsKey(id))
|
||||
return edits[id];
|
||||
|
||||
throw new KeyNotFoundException();
|
||||
}
|
||||
public void Add(ReflectiveEdit edit)
|
||||
{
|
||||
edits.Add(edit.ID, edit);
|
||||
}
|
||||
|
||||
public void Cleanup()
|
||||
{
|
||||
foreach (Guid id in edits.Keys.ToArray())
|
||||
{
|
||||
if (edits[id].TimedOut)
|
||||
edits.Remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,205 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.CodeDom;
|
||||
using appsrv.server;
|
||||
using System.IO;
|
||||
using appsrv.templates;
|
||||
using appsrv.templates.token;
|
||||
using System.Reflection;
|
||||
using appsrv.mime;
|
||||
using System.Collections;
|
||||
using Newtonsoft.Json;
|
||||
namespace appsrv.resources.reflection
|
||||
{
|
||||
public partial class ReflectiveResource : Resource
|
||||
{
|
||||
Dictionary<Type, ReflectiveResourceCrawler> crawlers = new Dictionary<Type, ReflectiveResourceCrawler>();
|
||||
Dictionary<String, Template> templates = new Dictionary<string, Template>();
|
||||
|
||||
public object NativeInstance { get; protected set; }
|
||||
public Type NativeType { get; protected set; }
|
||||
|
||||
public String TemplatesPath { get; protected set; }
|
||||
|
||||
public MethodInfo MethodSafe { get; protected set; }
|
||||
|
||||
|
||||
public ReflectiveResource(Type type, Resource container)
|
||||
: this(type, container, type.Name)
|
||||
{
|
||||
}
|
||||
|
||||
public ReflectiveResource(Type type, Resource container,String name)
|
||||
: base(name, container)
|
||||
{
|
||||
NativeType = type;
|
||||
TemplatesPath = Application.BaseDirectory.FullName;
|
||||
}
|
||||
|
||||
public ReflectiveResource(object instance, Resource container)
|
||||
:this(instance,container,instance.GetType().Name)
|
||||
{
|
||||
}
|
||||
|
||||
public ReflectiveResource(object instance,Resource container,String name)
|
||||
:base(name,container)
|
||||
{
|
||||
NativeInstance = instance;
|
||||
NativeType = instance.GetType();
|
||||
|
||||
TemplatesPath = Application.BaseDirectory.FullName;
|
||||
}
|
||||
|
||||
public ReflectiveResourceCrawler CrawlerForType(Type type)
|
||||
{
|
||||
if (!crawlers.ContainsKey(type))
|
||||
{
|
||||
crawlers.Add(type, new ReflectiveResourceCrawler(this,type));
|
||||
}
|
||||
return crawlers[type];
|
||||
}
|
||||
|
||||
public object Crawl(Request request,Stack<String> pathStack)
|
||||
{
|
||||
return Crawl(NativeInstance, NativeType, request, pathStack);
|
||||
}
|
||||
|
||||
public object Crawl(object intermediate,Type type,Request request,Stack<String> pathStack)
|
||||
{
|
||||
if ((intermediate != null) && (intermediate.GetType() != type))
|
||||
{
|
||||
throw new ArgumentException(String.Format("intermediate <{0}> needs to be of type <{1}> but is of type <{2}>",intermediate,type,intermediate.GetType()));
|
||||
}
|
||||
|
||||
ReflectiveResourceCrawler crawler = CrawlerForType(type);
|
||||
|
||||
object next = crawler.Crawl(intermediate, request, pathStack);
|
||||
if (pathStack.Count > 0)
|
||||
{
|
||||
return Crawl(next, next.GetType(), request, pathStack);
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
public override Resource Lookup(string name)
|
||||
{
|
||||
Resource resource = base.Lookup(name);
|
||||
if (resource == null)
|
||||
{
|
||||
resource = GetTemplate(name);
|
||||
}
|
||||
return resource;
|
||||
}
|
||||
|
||||
public override void Hit(Stack<string> requestPath, Request request)
|
||||
{
|
||||
ReflectiveEdit edit;
|
||||
ReflectiveEditStore store;
|
||||
|
||||
if (requestPath.Peek().EndsWith(".hfrm"))
|
||||
{
|
||||
// TODO: How to handle external template requests...
|
||||
} else if (requestPath.Peek()[0] == '$')
|
||||
{
|
||||
Guid editID = Guid.Parse(requestPath.Pop().Substring(1));
|
||||
|
||||
if (request.Session.ContainsKey<ReflectiveEditStore>(Path))
|
||||
{
|
||||
store = request.Session.Get<ReflectiveEditStore>(Path);
|
||||
edit = store.Find(editID);
|
||||
edit.Render(request);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new KeyNotFoundException();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
object resourceObject = Crawl(request, requestPath);
|
||||
|
||||
|
||||
|
||||
if (resourceObject.GetType().IsPrimitive || (resourceObject is string))
|
||||
{
|
||||
Response response = request.Response;
|
||||
response.Headers.Add("Content-type", "text/html");
|
||||
response.Reference = resourceObject;
|
||||
response.Writer.Write(resourceObject);
|
||||
} else if (resourceObject is IEnumerable)
|
||||
{
|
||||
Response response = request.Response;
|
||||
response.Headers.Add("Content-Type", "application/json");
|
||||
response.Reference = resourceObject;
|
||||
response.Writer.Write(JsonConvert.SerializeObject(resourceObject));
|
||||
} else
|
||||
{
|
||||
|
||||
string tmplName = String.Format("{0}.hfrm", resourceObject.GetType().FullName);
|
||||
|
||||
Template template = GetTemplate(tmplName);
|
||||
|
||||
edit = new ReflectiveEdit(this, resourceObject, template);
|
||||
|
||||
if (!request.Session.ContainsKey<ReflectiveEditStore>(Path))
|
||||
{
|
||||
store = new ReflectiveEditStore(this);
|
||||
request.Session.Set<ReflectiveEditStore>(Path, store);
|
||||
}
|
||||
else
|
||||
{
|
||||
store = request.Session.Get<ReflectiveEditStore>(Path);
|
||||
}
|
||||
|
||||
store.Add(edit);
|
||||
|
||||
edit.Render(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Template GetTemplate(String tmplFileName){
|
||||
if (!templates.ContainsKey(tmplFileName))
|
||||
{
|
||||
try
|
||||
{
|
||||
string tmplPath = String.Format("{0}/{1}", TemplatesPath, tmplFileName);
|
||||
if (File.Exists(tmplPath))
|
||||
templates.Add(tmplFileName, new Template(tmplPath, this));
|
||||
else
|
||||
return null;
|
||||
} catch (FileNotFoundException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return templates[tmplFileName];
|
||||
}
|
||||
|
||||
|
||||
public override void ApplyParameters(Dictionary<string, string> parameters)
|
||||
{
|
||||
if (parameters.ContainsKey("templates"))
|
||||
{
|
||||
TemplatesPath = parameters["templates"];
|
||||
if (!System.IO.Path.IsPathRooted(TemplatesPath))
|
||||
TemplatesPath = System.IO.Path.Combine(Application.BaseDirectory.FullName, TemplatesPath);
|
||||
}
|
||||
if (parameters.ContainsKey("instantiate"))
|
||||
{
|
||||
bool instantiate = (bool)Convert.ChangeType(parameters["instantiate"],typeof(bool));
|
||||
if (instantiate)
|
||||
{
|
||||
NativeInstance = Activator.CreateInstance(NativeType);
|
||||
}
|
||||
}
|
||||
if (parameters.ContainsKey("method_save"))
|
||||
{
|
||||
MethodSafe = NativeType.GetMethod(parameters["method_save"]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using appsrv.server;
|
||||
using System.Reflection;
|
||||
using appsrv.exceptions;
|
||||
using appsrv.attributes;
|
||||
using System.Runtime.Remoting.Metadata.W3cXsd2001;
|
||||
namespace appsrv.resources.reflection
|
||||
{
|
||||
public class ReflectiveResourceCrawler
|
||||
{
|
||||
/* Instance Members */
|
||||
public ReflectiveResource Resource { get; }
|
||||
public Type NativeType { get; }
|
||||
|
||||
public ReflectiveResourceCrawler(ReflectiveResource resource,Type type)
|
||||
{
|
||||
Resource = resource;
|
||||
NativeType = type;
|
||||
}
|
||||
|
||||
public object Crawl(object instance,Request request,Stack<String> pathStack)
|
||||
{
|
||||
if (pathStack.Count == 0)
|
||||
return instance;
|
||||
|
||||
String next = pathStack.Pop();
|
||||
|
||||
foreach (FieldInfo fieldInfo in NativeType.GetFields())
|
||||
{
|
||||
if (fieldInfo.GetCustomAttribute<PublishedMemberAttribute>()!=null)
|
||||
{
|
||||
if (fieldInfo.Name.Equals(next))
|
||||
{
|
||||
return FixupIntermediate(fieldInfo.GetValue(instance), request, pathStack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (PropertyInfo propertyInfo in NativeType.GetProperties())
|
||||
{
|
||||
if (propertyInfo.GetCustomAttribute<PublishedMemberAttribute>() != null)
|
||||
{
|
||||
if (propertyInfo.Name.Equals(next))
|
||||
{
|
||||
return FixupIntermediate(propertyInfo.GetValue(instance), request, pathStack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (MethodInfo methodInfo in NativeType.GetMethods())
|
||||
{
|
||||
if (methodInfo.GetCustomAttribute<PublishedMemberAttribute>() != null)
|
||||
{
|
||||
if (methodInfo.Name.Equals(next))
|
||||
{
|
||||
object[] parameters = PopParameters(methodInfo,pathStack,request);
|
||||
object i = methodInfo.IsStatic ? null : instance;
|
||||
return FixupIntermediate( methodInfo.Invoke(i,parameters), request, pathStack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new KeyNotFoundException(next);
|
||||
}
|
||||
|
||||
private object[] PopParameters(MethodInfo methodInfo,Stack<String> pathStack,Request request)
|
||||
{
|
||||
ParameterInfo[] parameterInfos = methodInfo.GetParameters();
|
||||
object[] p = new object[parameterInfos.Length];
|
||||
|
||||
for (int n = 0; n < parameterInfos.Length;n++)
|
||||
{
|
||||
string nextparm = null;
|
||||
|
||||
if (request.HasParameter(parameterInfos[n].Name))
|
||||
{
|
||||
nextparm = request.GetParameterValue(parameterInfos[n].Name);
|
||||
} else
|
||||
{
|
||||
if (pathStack.Count > 0)
|
||||
{
|
||||
nextparm = pathStack.Pop();
|
||||
}
|
||||
}
|
||||
|
||||
p[n] = Convert.ChangeType(nextparm, parameterInfos[n].ParameterType);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
private object FixupIntermediate(object intermediate,Request request, Stack<String> pathStack)
|
||||
{
|
||||
if (intermediate.GetType().IsArray)
|
||||
return FixupIntermediateArray((Array)intermediate, request, pathStack);
|
||||
|
||||
return intermediate;
|
||||
}
|
||||
|
||||
private object FixupIntermediateArray(Array intermediate, Request request, Stack<String> pathStack)
|
||||
{
|
||||
if (pathStack.Count == 0)
|
||||
return intermediate;
|
||||
|
||||
int index = int.Parse(pathStack.Pop());
|
||||
return intermediate.GetValue(index);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
namespace appsrv.security
|
||||
{
|
||||
public class AccessMask
|
||||
{
|
||||
private ISet<AccessMask> children = new HashSet<AccessMask>();
|
||||
|
||||
public AccessMask Container { get; }
|
||||
public String Key { get; }
|
||||
public long Mask { get; }
|
||||
|
||||
public AccessMask(AccessMask container,long mask)
|
||||
{
|
||||
Container = container;
|
||||
Mask = mask;
|
||||
|
||||
if (container != null)
|
||||
container.children.Add(this);
|
||||
}
|
||||
|
||||
public AccessMask this[string key]{
|
||||
get {
|
||||
foreach (AccessMask child in children)
|
||||
{
|
||||
if (child.Key.Equals(key))
|
||||
return child;
|
||||
}
|
||||
throw new IndexOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
using System;
|
||||
namespace appsrv.security
|
||||
{
|
||||
public class Group
|
||||
{
|
||||
public String Name { get; }
|
||||
|
||||
public Group()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
using System;
|
||||
namespace appsrv.security
|
||||
{
|
||||
public interface ISecurityProvider
|
||||
{
|
||||
String[] GetUserList();
|
||||
UserInfo Lookup(string login);
|
||||
|
||||
SecureUser Authenticate(String login, String passwd);
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
using System;
|
||||
using System.Security.Policy;
|
||||
namespace appsrv.security
|
||||
{
|
||||
public class SecureUser : UserInfo
|
||||
{
|
||||
public String Login { get; }
|
||||
public String FullName { get; }
|
||||
|
||||
public string[] CustomKeys => throw new NotImplementedException();
|
||||
|
||||
public SecureUser(String username)
|
||||
{
|
||||
Login = username;
|
||||
}
|
||||
|
||||
public string GetCustomKey(string key)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
namespace appsrv.security
|
||||
{
|
||||
public class SecurityEntity
|
||||
{
|
||||
private ISet<SecurityEntity> securityGroups = new HashSet<SecurityEntity>();
|
||||
|
||||
public String Name { get; }
|
||||
public ISet<SecurityEntity> MemberOf => new HashSet<SecurityEntity>(securityGroups);
|
||||
|
||||
public SecurityEntity(String name,IEnumerable<SecurityEntity> memberOf)
|
||||
{
|
||||
Name = name;
|
||||
AddSecurityGroups(memberOf);
|
||||
}
|
||||
|
||||
private void AddSecurityGroups(IEnumerable<SecurityEntity> secGroups)
|
||||
{
|
||||
foreach (SecurityEntity group in secGroups)
|
||||
{
|
||||
if (securityGroups.Add(group))
|
||||
{
|
||||
AddSecurityGroups(group.MemberOf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
using System;
|
||||
namespace appsrv.security
|
||||
{
|
||||
public interface UserInfo
|
||||
{
|
||||
String Login { get; }
|
||||
String FullName { get; }
|
||||
|
||||
String[] CustomKeys { get; }
|
||||
String GetCustomKey(string key);
|
||||
}
|
||||
}
|
|
@ -1,13 +1,130 @@
|
|||
using System;
|
||||
using appsrv.resources;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using appsrv.resources.reflection;
|
||||
|
||||
namespace appsrv.server
|
||||
{
|
||||
public class Application
|
||||
{
|
||||
public ApplicationServer ApplicationServer { get; private set; }
|
||||
public String ConfigurationFileName { get; }
|
||||
|
||||
public DirectoryInfo BaseDirectory { get; }
|
||||
|
||||
public Application(ApplicationServer applicationServer)
|
||||
public String ApplicationName { get; protected set; }
|
||||
public String[] Aliases => aliases.ToArray();
|
||||
|
||||
public Resource RootResource { get; protected set; }
|
||||
|
||||
public ISet<Assembly> Assemblies { get; } = new HashSet<Assembly>();
|
||||
|
||||
|
||||
List<string> aliases = new List<string>();
|
||||
|
||||
public Application(ApplicationServer applicationServer,String filename)
|
||||
{
|
||||
this.ApplicationServer = applicationServer;
|
||||
ApplicationServer = applicationServer;
|
||||
ConfigurationFileName = filename;
|
||||
|
||||
BaseDirectory = new FileInfo(filename).Directory;
|
||||
|
||||
LoadConfiguration();
|
||||
}
|
||||
|
||||
private void LoadConfiguration()
|
||||
{
|
||||
XmlDocument xmlDocument = new XmlDocument();
|
||||
xmlDocument.Load(ConfigurationFileName);
|
||||
|
||||
ApplicationName = xmlDocument.DocumentElement["ApplicationName"].InnerText.Trim();
|
||||
|
||||
foreach (XmlNode nAssembly in xmlDocument.DocumentElement.SelectNodes("Assembly"))
|
||||
{
|
||||
string assemblyfilename = Path.Combine(BaseDirectory.FullName, nAssembly.InnerText);
|
||||
Assemblies.Add(Assembly.LoadFrom(assemblyfilename));
|
||||
}
|
||||
|
||||
LoadResource(xmlDocument.DocumentElement["Resource"]);
|
||||
}
|
||||
|
||||
public bool IdentifiedBy(string alias)
|
||||
{
|
||||
if (ApplicationName.Equals(alias, StringComparison.InvariantCultureIgnoreCase))
|
||||
return true;
|
||||
|
||||
foreach (string a in aliases)
|
||||
{
|
||||
if (a.Equals(alias, StringComparison.InvariantCultureIgnoreCase))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public Type GetType(String typeName)
|
||||
{
|
||||
foreach (Assembly asm in Assemblies)
|
||||
{
|
||||
Type type = asm.GetType(typeName);
|
||||
if (type != null)
|
||||
return type;
|
||||
}
|
||||
|
||||
return this.GetType().Assembly.GetType(typeName);
|
||||
}
|
||||
|
||||
private void LoadResource(XmlNode xmlResource,Resource container = null){
|
||||
String providerName = xmlResource["Provider"].GetAttribute("Name");
|
||||
String providerKey = xmlResource["Provider"].InnerText.Trim();
|
||||
|
||||
Resource resource = null;
|
||||
|
||||
switch (providerName)
|
||||
{
|
||||
case "StaticDirectory":
|
||||
resource = new DirectoryResource(new DirectoryInfo(Path.Combine(BaseDirectory.FullName,providerKey)),container);
|
||||
break;
|
||||
/* case "StaticClass":
|
||||
resource = new StaticClassResource(GetType(providerKey), container);
|
||||
break;
|
||||
case "ClassInstance":
|
||||
resource = new ClassInstanceResource(GetType(providerKey), container);
|
||||
break;
|
||||
*/
|
||||
case "ReflectiveResource":
|
||||
resource = new ReflectiveResource(GetType(providerKey), container);
|
||||
break;
|
||||
}
|
||||
|
||||
Dictionary<String, String> parameters = new Dictionary<string, string>();
|
||||
foreach (XmlNode pnode in xmlResource.SelectNodes("Parameter"))
|
||||
{
|
||||
parameters.Add(pnode.Attributes["Name"].Value, pnode.InnerText);
|
||||
}
|
||||
|
||||
resource.ApplyParameters(parameters);
|
||||
|
||||
if (container == null)
|
||||
{
|
||||
RootResource = resource;
|
||||
RootResource.Application = this;
|
||||
}
|
||||
|
||||
foreach (XmlNode subResource in xmlResource.SelectNodes("Resource"))
|
||||
{
|
||||
LoadResource(subResource, resource);
|
||||
}
|
||||
|
||||
|
||||
if (xmlResource.Attributes["Default"] != null){
|
||||
resource.DefaultResource = resource.FindByPath(xmlResource.Attributes["Default"].InnerText);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,40 +2,93 @@
|
|||
using System.Collections.Generic;
|
||||
using appsrv.resources;
|
||||
using System.Linq;
|
||||
using appsrv.sessions;
|
||||
using sharp.logging;
|
||||
namespace appsrv.server
|
||||
{
|
||||
public class ApplicationServer
|
||||
{
|
||||
public Resource DefaultRoot { get; set; }
|
||||
public Application DefaultApplication => applications[0];
|
||||
public Application[] Applications => applications.ToArray();
|
||||
|
||||
|
||||
Dictionary<String, Resource> roots = new Dictionary<string, Resource>();
|
||||
List<Application> applications = new List<Application>();
|
||||
Dictionary<Guid, Session> sessions = new Dictionary<Guid, Session>();
|
||||
|
||||
public ApplicationServer()
|
||||
{
|
||||
}
|
||||
|
||||
public void AddRoot(String name,Resource rootResource){
|
||||
roots.Add(name, rootResource);
|
||||
if (DefaultRoot == null){
|
||||
DefaultRoot = rootResource;
|
||||
public void Add(Application application)
|
||||
{
|
||||
if (!applications.Contains(application))
|
||||
applications.Add(application);
|
||||
}
|
||||
public void Remove(Application application)
|
||||
{
|
||||
applications.Remove(application);
|
||||
}
|
||||
|
||||
public Application ApplicationByAlias(String alias)
|
||||
{
|
||||
foreach (Application application in applications)
|
||||
{
|
||||
if (application.IdentifiedBy(alias))
|
||||
return application;
|
||||
}
|
||||
return DefaultApplication;
|
||||
}
|
||||
|
||||
|
||||
public void HandleRequest(Request request){
|
||||
|
||||
Resource rootResource = request.Application.RootResource;
|
||||
Stack<String> requestPath = new Stack<string>(request.SplittedPath.Reverse());
|
||||
|
||||
Console.WriteLine("ApplicationServer.HandleRequest: {0}", request);
|
||||
|
||||
try
|
||||
{
|
||||
rootResource.Request(requestPath, request);
|
||||
} catch (Exception e)
|
||||
{
|
||||
Logger.Default.Log(e);
|
||||
}
|
||||
}
|
||||
|
||||
public Resource FindRoot(String rootName)
|
||||
public Session FindSession(Guid id)
|
||||
{
|
||||
if (roots.ContainsKey(rootName))
|
||||
return roots[rootName];
|
||||
|
||||
return DefaultRoot;
|
||||
lock (sessions)
|
||||
{
|
||||
if (sessions.ContainsKey(id))
|
||||
return sessions[id];
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void HandleRequest(HttpRequest request){
|
||||
Resource rootResource = FindRoot(request.Hostname);
|
||||
Stack<String> requestPath = new Stack<string>(request.URI.AbsolutePath.Split(new char[] { '/' },StringSplitOptions.RemoveEmptyEntries).Reverse());
|
||||
|
||||
rootResource.Request(requestPath, request);
|
||||
public void AddSession(Session session)
|
||||
{
|
||||
lock (sessions)
|
||||
{
|
||||
if (!sessions.ContainsKey(session.ID))
|
||||
sessions.Add(session.ID, session);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: Call from somewhere...
|
||||
*/
|
||||
private void CleanupSessions()
|
||||
{
|
||||
lock (sessions)
|
||||
{
|
||||
foreach (Session session in sessions.Values.ToArray())
|
||||
{
|
||||
if (session.TimedOut)
|
||||
sessions.Remove(session.ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,229 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using appsrv.exceptions;
|
||||
using appsrv.http;
|
||||
|
||||
namespace appsrv.server
|
||||
{
|
||||
public class HttpRequest
|
||||
{
|
||||
private Stream stream;
|
||||
private StreamReader streamReader;
|
||||
private List<HttpRequest> currentRequests = new List<HttpRequest>();
|
||||
|
||||
private MemoryStream responseStream;
|
||||
private StreamWriter responseWriter;
|
||||
|
||||
Dictionary<String, String> requestHeaders = new Dictionary<string, string>();
|
||||
Dictionary<String, String> responseHeaders = new Dictionary<string, string>();
|
||||
|
||||
public EndPoint Client { get; private set; }
|
||||
|
||||
public ApplicationServer ApplicationServer { get; private set; }
|
||||
public Uri URI { get; private set; }
|
||||
|
||||
public String Method { get; private set; }
|
||||
public String RequestURL { get; private set; }
|
||||
public String Protocol { get; private set; }
|
||||
|
||||
public String Hostname { get; private set; }
|
||||
public int Port { get; private set; }
|
||||
|
||||
public QueryStringParameters Query { get; private set; }
|
||||
|
||||
|
||||
public int StatusCode { get; set; } = 200;
|
||||
|
||||
public HttpRequest(ApplicationServer applicationServer, TcpClient client)
|
||||
{
|
||||
this.ApplicationServer = applicationServer;
|
||||
|
||||
this.Client = client.Client.RemoteEndPoint;
|
||||
this.stream = client.GetStream();
|
||||
this.streamReader = new StreamReader(this.stream);
|
||||
|
||||
String rLine = this.streamReader.ReadLine();
|
||||
String[] rTokens = SplitWhitespace(
|
||||
rLine
|
||||
);
|
||||
Console.WriteLine("Tokens: {0}", rTokens);
|
||||
|
||||
if (rTokens.Length != 3){
|
||||
throw new IllegalRequestException(rLine);
|
||||
}
|
||||
|
||||
this.Method = rTokens[0];
|
||||
this.RequestURL = rTokens[1];
|
||||
this.Protocol = rTokens[2];
|
||||
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("[HttpRequest: Client={0}, ApplicationServer={1}, Hostname={6} Port={7} URI={2}, Method={3}, RequestURL={4}, Protocol={5} Query={8}]", Client, ApplicationServer, URI, Method, RequestURL, Protocol, Hostname, Port,Query);
|
||||
}
|
||||
|
||||
public String GetRequestHeader(String name, String def = "")
|
||||
{
|
||||
name = name.ToLowerInvariant();
|
||||
|
||||
if (requestHeaders.ContainsKey(name))
|
||||
return requestHeaders[name];
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
public String GetResponseHeader(String name, String def = "")
|
||||
{
|
||||
name = name.ToLowerInvariant();
|
||||
|
||||
if (responseHeaders.ContainsKey(name))
|
||||
return responseHeaders[name];
|
||||
|
||||
return def;
|
||||
}
|
||||
public void SetResponseHeader(String name,String value){
|
||||
name = name.ToLowerInvariant();
|
||||
if (value == null)
|
||||
{
|
||||
this.responseHeaders.Remove(name);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.responseHeaders[name] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public Stream ResponseStream
|
||||
{
|
||||
get {
|
||||
if (responseStream == null)
|
||||
responseStream = new MemoryStream();
|
||||
|
||||
return responseStream;
|
||||
}
|
||||
}
|
||||
|
||||
public TextWriter ResponseWriter
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.responseWriter == null)
|
||||
{
|
||||
this.responseWriter = new StreamWriter(this.responseStream);
|
||||
}
|
||||
return this.responseWriter;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private String[] SplitWhitespace(String line){
|
||||
LinkedList<String> tokens = new LinkedList<string>();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (int n = 0; n < line.Length;)
|
||||
{
|
||||
for (; n < line.Length && !Char.IsWhiteSpace(line[n]); n++)
|
||||
sb.Append(line[n]);
|
||||
|
||||
tokens.AddLast(sb.ToString());
|
||||
sb.Clear();
|
||||
|
||||
for (; n < line.Length && Char.IsWhiteSpace(line[n]); n++) {}
|
||||
}
|
||||
return tokens.ToArray();
|
||||
}
|
||||
|
||||
private void ReadHttpHeaders(){
|
||||
String headerLine = this.streamReader.ReadLine();
|
||||
String hName = null;
|
||||
|
||||
while (!headerLine.Equals(String.Empty)){
|
||||
if (Char.IsWhiteSpace(headerLine[0]) && hName != null)
|
||||
{
|
||||
requestHeaders[hName] = requestHeaders[hName] + "," + headerLine.Trim();
|
||||
} else {
|
||||
String[] split = headerLine.Split(new char[] { ':' }, 2);
|
||||
if (split.Length != 2){
|
||||
throw new IllegalRequestException("malformed header");
|
||||
}
|
||||
|
||||
hName = split[0].ToLowerInvariant();
|
||||
requestHeaders[hName] = split[1];
|
||||
}
|
||||
|
||||
headerLine = this.streamReader.ReadLine();
|
||||
}
|
||||
|
||||
foreach (String hname in requestHeaders.Keys.ToArray()){
|
||||
requestHeaders[hname] = requestHeaders[hname].Trim();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void InterpretRequestHeaders()
|
||||
{
|
||||
String host = GetRequestHeader("host");
|
||||
String[] hostTokens = host.Split(':');
|
||||
Hostname = hostTokens[0];
|
||||
if (hostTokens.Length > 1){
|
||||
Port = int.Parse(hostTokens[1]);
|
||||
}
|
||||
|
||||
URI = new Uri(String.Format("http://{0}:{1}/{2}", Hostname, Port, RequestURL));
|
||||
|
||||
Query = new QueryStringParameters(URI.Query);
|
||||
|
||||
}
|
||||
|
||||
private void SendResponse()
|
||||
{
|
||||
using (StreamWriter writer = new StreamWriter(this.stream))
|
||||
{
|
||||
ResponseStream.Position = 0;
|
||||
SetResponseHeader("Content-Length", responseStream.Length.ToString());
|
||||
|
||||
writer.WriteLine("{0} {1} {2}", Protocol, StatusCode, HttpStatusCodes.GetStatusMessage(StatusCode));
|
||||
foreach (String rhName in responseHeaders.Keys){
|
||||
writer.WriteLine("{0}: {1}", rhName, responseHeaders[rhName]);
|
||||
}
|
||||
writer.WriteLine();
|
||||
writer.Flush();
|
||||
|
||||
responseStream.CopyTo(this.stream);
|
||||
}
|
||||
}
|
||||
|
||||
public void Handle(){
|
||||
this.ReadHttpHeaders();
|
||||
this.InterpretRequestHeaders();
|
||||
|
||||
Console.WriteLine("Request Handled: {0}",this);
|
||||
|
||||
foreach (String key in this.requestHeaders.Keys){
|
||||
Console.WriteLine("HH: {0} = {1}",key,this.requestHeaders[key]);
|
||||
}
|
||||
|
||||
ApplicationServer.HandleRequest(this);
|
||||
|
||||
SendResponse();
|
||||
|
||||
this.streamReader.Close();
|
||||
this.stream.Close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
using System;
|
||||
using appsrv.sessions;
|
||||
using System.Reflection;
|
||||
using appsrv.mime;
|
||||
namespace appsrv.server
|
||||
{
|
||||
public abstract class Request
|
||||
{
|
||||
private Session _session = null;
|
||||
|
||||
public ApplicationServer ApplicationServer { get; }
|
||||
public Application Application { get; protected set; }
|
||||
|
||||
public Session Session
|
||||
{
|
||||
get => (_session == null) ? CreateSession() : _session;
|
||||
protected set => _session = value;
|
||||
}
|
||||
|
||||
public String Path { get; protected set; }
|
||||
public String[] SplittedPath => Path.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
public abstract string ROOT { get; }
|
||||
|
||||
public byte[] RawBody { get; protected set; }
|
||||
public object Body { get; protected set; }
|
||||
|
||||
public Request(ApplicationServer applicationServer)
|
||||
{
|
||||
ApplicationServer = applicationServer;
|
||||
}
|
||||
|
||||
public abstract Response Response { get; }
|
||||
public abstract MIMEMessage GetParameter(String name);
|
||||
|
||||
public virtual bool HasParameter(String name){
|
||||
return GetParameter(name) != null;
|
||||
}
|
||||
public virtual String GetParameterValue(String name){
|
||||
MIMEMessage pm = GetParameter(name);
|
||||
if (pm != null)
|
||||
{
|
||||
return pm.Value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Session CreateSession()
|
||||
{
|
||||
_session = new Session(ApplicationServer);
|
||||
return _session;
|
||||
}
|
||||
|
||||
protected void ApplySession(Guid SID)
|
||||
{
|
||||
Session = ApplicationServer.FindSession(SID);
|
||||
}
|
||||
|
||||
public Response SubRequest(string path)
|
||||
{
|
||||
InternalRequest internalRequest = new InternalRequest(this,path);
|
||||
ApplicationServer.HandleRequest(internalRequest);
|
||||
return internalRequest.Response;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class InternalRequest : Request
|
||||
{
|
||||
InternalResponse response;
|
||||
|
||||
public override string ROOT => "";
|
||||
|
||||
public InternalRequest(Request baseRequest,string path)
|
||||
:base(baseRequest.ApplicationServer)
|
||||
{
|
||||
Application = baseRequest.Application;
|
||||
response = new InternalResponse(this);
|
||||
Path = path;
|
||||
}
|
||||
|
||||
public override Response Response => response;
|
||||
public override MIMEMessage GetParameter(string name)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using appsrv.mime;
|
||||
namespace appsrv.server
|
||||
{
|
||||
public abstract class Response
|
||||
{
|
||||
public Request Request { get; }
|
||||
|
||||
public int StatusCode { get; set; }
|
||||
|
||||
public MimeHeaders Headers { get; protected set; }
|
||||
public Stream Stream { get; protected set; }
|
||||
public TextWriter Writer { get; protected set; }
|
||||
|
||||
public object Reference { get; set; }
|
||||
|
||||
public byte[] ContentBytes => ((MemoryStream)Stream).ToArray();
|
||||
|
||||
private Dictionary<string, string> cookies = new Dictionary<string, string>();
|
||||
|
||||
public Response(Request request)
|
||||
{
|
||||
Request = request;
|
||||
Headers = new MimeHeaders();
|
||||
|
||||
Stream = new MemoryStream();
|
||||
Writer = new StreamWriter(Stream);
|
||||
}
|
||||
|
||||
public void Flush()
|
||||
{
|
||||
Writer.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
class InternalResponse : Response
|
||||
{
|
||||
public InternalResponse(Request request)
|
||||
:base(request)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
using System;
|
||||
using appsrv.server;
|
||||
using System.Collections.Generic;
|
||||
namespace appsrv.sessions
|
||||
{
|
||||
public class Session
|
||||
{
|
||||
public ApplicationServer ApplicationServer { get; }
|
||||
|
||||
public Guid ID { get; }
|
||||
public DateTime Created { get; }
|
||||
public DateTime LastUsage { get; protected set; }
|
||||
|
||||
public bool TimedOut => (DateTime.Now - LastUsage) > TimeSpan.FromSeconds(1200);
|
||||
|
||||
Dictionary<string, object> storage = new Dictionary<string, object>();
|
||||
Dictionary<Type, Dictionary<string, object>> typedStorage = new Dictionary<Type, Dictionary<string, object>>();
|
||||
|
||||
public Session(ApplicationServer applicationServer)
|
||||
{
|
||||
ApplicationServer = applicationServer;
|
||||
Created = DateTime.Now;
|
||||
LastUsage = DateTime.Now;
|
||||
ID = Guid.NewGuid();
|
||||
}
|
||||
|
||||
public object this[string key]
|
||||
{
|
||||
get => Get(key);
|
||||
set => Set(key, value);
|
||||
}
|
||||
|
||||
public void Touch()
|
||||
{
|
||||
LastUsage = DateTime.Now;
|
||||
ApplicationServer.AddSession(this);
|
||||
}
|
||||
|
||||
public object Get(string key)
|
||||
{
|
||||
if (storage.ContainsKey(key))
|
||||
return storage[key];
|
||||
return null;
|
||||
}
|
||||
public void Set(String key,object value)
|
||||
{
|
||||
storage[key] = value;
|
||||
}
|
||||
public bool ContainsKey(String key)
|
||||
{
|
||||
return storage.ContainsKey(key);
|
||||
}
|
||||
|
||||
|
||||
public bool ContainsKey<T>(String key)
|
||||
{
|
||||
return (typedStorage.ContainsKey(typeof(T))) && typedStorage[typeof(T)].ContainsKey(key);
|
||||
|
||||
}
|
||||
public T Get<T>(string key)
|
||||
{
|
||||
if (typedStorage.ContainsKey(typeof(T)))
|
||||
{
|
||||
if (typedStorage[typeof(T)].ContainsKey(key))
|
||||
return (T)typedStorage[typeof(T)][key];
|
||||
}
|
||||
throw new KeyNotFoundException();
|
||||
}
|
||||
public void Set<T>(String key,T value)
|
||||
{
|
||||
if (!typedStorage.ContainsKey(typeof(T)))
|
||||
{
|
||||
typedStorage.Add(typeof(T), new Dictionary<string, object>());
|
||||
}
|
||||
typedStorage[typeof(T)][key] = value;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,179 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
|
||||
<ProjectGuid>{FD508FE5-5879-4C60-91D8-CA408E06361F}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>appsrv</RootNamespace>
|
||||
<AssemblyName>appsrv</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug</OutputPath>
|
||||
<DefineConstants>DEBUG;</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ExternalConsole>true</ExternalConsole>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release</OutputPath>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ExternalConsole>true</ExternalConsole>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="Newtonsoft.Json">
|
||||
<HintPath>..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="connector\Http.cs" />
|
||||
<Compile Include="server\ApplicationServer.cs" />
|
||||
<Compile Include="connector\Connector.cs" />
|
||||
<Compile Include="server\Application.cs" />
|
||||
<Compile Include="exceptions\IllegalRequestException.cs" />
|
||||
<Compile Include="resources\Resource.cs" />
|
||||
<Compile Include="resources\FileResource.cs" />
|
||||
<Compile Include="resources\DirectoryResource.cs" />
|
||||
<Compile Include="exceptions\ApplicationServerException.cs" />
|
||||
<Compile Include="exceptions\ResourceNotFoundException.cs" />
|
||||
<Compile Include="mime\MimeHelper.cs" />
|
||||
<Compile Include="http\HttpStatusCodes.cs" />
|
||||
<Compile Include="resources\ResourceLink.cs" />
|
||||
<Compile Include="attributes\WebCallableAttribute.cs" />
|
||||
<Compile Include="test\StaticTest.cs" />
|
||||
<Compile Include="http\QueryStringParameters.cs" />
|
||||
<Compile Include="templates\Template.cs" />
|
||||
<Compile Include="templates\FormElement.cs" />
|
||||
<Compile Include="http\HttpRequest.cs" />
|
||||
<Compile Include="templates\FormContext.cs" />
|
||||
<Compile Include="templates\elements\Head.cs" />
|
||||
<Compile Include="templates\elements\Var.cs" />
|
||||
<Compile Include="templates\elements\Set.cs" />
|
||||
<Compile Include="templates\elements\Iterate.cs" />
|
||||
<Compile Include="templates\elements\If.cs" />
|
||||
<Compile Include="templates\elements\Include.cs" />
|
||||
<Compile Include="http\HtmlResolver.cs" />
|
||||
<Compile Include="http\HtmlReader.cs" />
|
||||
<Compile Include="templates\elements\Entity.cs" />
|
||||
<Compile Include="templates\elements\Text.cs" />
|
||||
<Compile Include="templates\token\Token.cs" />
|
||||
<Compile Include="templates\token\PathToken.cs" />
|
||||
<Compile Include="templates\token\KeywordToken.cs" />
|
||||
<Compile Include="templates\elements\Framed.cs" />
|
||||
<Compile Include="templates\Scanner.cs" />
|
||||
<Compile Include="templates\token\NumberToken.cs" />
|
||||
<Compile Include="templates\token\OperatorToken.cs" />
|
||||
<Compile Include="streams\PeekableStream.cs" />
|
||||
<Compile Include="streams\CharStream.cs" />
|
||||
<Compile Include="streams\TokenStream.cs" />
|
||||
<Compile Include="templates\token\ObjectPathToken.cs" />
|
||||
<Compile Include="templates\token\CallToken.cs" />
|
||||
<Compile Include="sessions\Session.cs" />
|
||||
<Compile Include="security\SecureUser.cs" />
|
||||
<Compile Include="security\Group.cs" />
|
||||
<Compile Include="security\SecurityEntity.cs" />
|
||||
<Compile Include="security\ISecurityProvider.cs" />
|
||||
<Compile Include="security\UserInfo.cs" />
|
||||
<Compile Include="security\AccessMask.cs" />
|
||||
<Compile Include="attributes\PublishedMember.cs" />
|
||||
<Compile Include="resources\reflection\ReflectiveResourceCrawler.cs" />
|
||||
<Compile Include="resources\reflection\ReflectiveResource.cs" />
|
||||
<Compile Include="test\RTest.cs" />
|
||||
<Compile Include="templates\editors\HTMLEditor.cs" />
|
||||
<Compile Include="templates\editors\StringEditor.cs" />
|
||||
<Compile Include="templates\elements\Editor.cs" />
|
||||
<Compile Include="templates\editors\IntegerEditor.cs" />
|
||||
<Compile Include="templates\editors\DoubleEditor.cs" />
|
||||
<Compile Include="mime\MimeReader.cs" />
|
||||
<Compile Include="mime\MimeHeader.cs" />
|
||||
<Compile Include="mime\MIMEMessage.cs" />
|
||||
<Compile Include="mime\MIMEHeaders.cs" />
|
||||
<Compile Include="mime\MimeType.cs" />
|
||||
<Compile Include="mime\MimeStream.cs" />
|
||||
<Compile Include="mime\StreamExtensions.cs" />
|
||||
<Compile Include="mime\ArrayExtensions.cs" />
|
||||
<Compile Include="http\StringExtensions.cs" />
|
||||
<Compile Include="server\Request.cs" />
|
||||
<Compile Include="server\Response.cs" />
|
||||
<Compile Include="http\HttpResponse.cs" />
|
||||
<Compile Include="templates\editors\MultiLineEditor.cs" />
|
||||
<Compile Include="resources\reflection\ReflectiveResource.ReflectiveEditStore.cs" />
|
||||
<Compile Include="resources\reflection\ReflectiveResource.ReflectiveEdit.cs" />
|
||||
<Compile Include="attributes\HTMLEditorAttribute.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="connector\" />
|
||||
<Folder Include="server\" />
|
||||
<Folder Include="exceptions\" />
|
||||
<Folder Include="resources\" />
|
||||
<Folder Include="mime\" />
|
||||
<Folder Include="http\" />
|
||||
<Folder Include="attributes\" />
|
||||
<Folder Include="test\" />
|
||||
<Folder Include="test\www\" />
|
||||
<Folder Include="test\form\" />
|
||||
<Folder Include="test\www\static\" />
|
||||
<Folder Include="templates\" />
|
||||
<Folder Include="templates\elements\" />
|
||||
<Folder Include="templates\token\" />
|
||||
<Folder Include="templates\expressions\" />
|
||||
<Folder Include="streams\" />
|
||||
<Folder Include="sessions\" />
|
||||
<Folder Include="security\" />
|
||||
<Folder Include="resources\reflection\" />
|
||||
<Folder Include="test\rtest.templates\" />
|
||||
<Folder Include="templates\editors\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="test\TestApplication.xml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="test\www\index.html">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="packages.config" />
|
||||
<None Include="test\www\scripts.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="test\form\index.hfrm">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="test\www\static\scripts.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="test\form\frame.hfrm">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="test\www\static\style.css" />
|
||||
<None Include="test\rtest.templates\appsrv.test.AClass.hfrm">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\sharp-logging\sharp.logging.csproj">
|
||||
<Project>{D471A566-9FB6-41B2-A777-3C32874ECD0E}</Project>
|
||||
<Name>sharp.logging</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<ProjectExtensions>
|
||||
<MonoDevelop>
|
||||
<Properties>
|
||||
<Policies>
|
||||
<DotNetNamingPolicy ResourceNamePolicy="FileFormatDefault" DirectoryNamespaceAssociation="PrefixedHierarchical" />
|
||||
</Policies>
|
||||
</Properties>
|
||||
</MonoDevelop>
|
||||
</ProjectExtensions>
|
||||
</Project>
|
|
@ -0,0 +1,20 @@
|
|||
using System;
|
||||
using System.Text;
|
||||
namespace appsrv.streams
|
||||
{
|
||||
public class CharStream : PeekAbleStream<char>
|
||||
{
|
||||
public CharStream(char[] buffer)
|
||||
:base(buffer)
|
||||
{
|
||||
}
|
||||
public CharStream(byte[] byteBuffer, Encoding encoding)
|
||||
: this(encoding.GetChars(byteBuffer))
|
||||
{
|
||||
}
|
||||
public CharStream(byte[] byteBuffer)
|
||||
:this(Encoding.UTF8.GetChars(byteBuffer))
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
using System.Text;
|
||||
namespace appsrv.streams
|
||||
{
|
||||
public class PeekAbleStream<T>
|
||||
{
|
||||
private T[] buffer;
|
||||
private int position;
|
||||
|
||||
public int Position => position;
|
||||
public bool EndOfBuffer => (position >= buffer.Length);
|
||||
|
||||
public int MarkedPosition { get; set; }
|
||||
|
||||
public PeekAbleStream(T[] buffer)
|
||||
{
|
||||
this.buffer = buffer;
|
||||
}
|
||||
|
||||
public T Read(){
|
||||
if (position < buffer.Length)
|
||||
return buffer[position++];
|
||||
|
||||
throw new IndexOutOfRangeException("Tried to read after end of buffer");
|
||||
}
|
||||
|
||||
public T Peek(int shift = 0)
|
||||
{
|
||||
if ((position + shift) < buffer.Length)
|
||||
return buffer[position + shift];
|
||||
|
||||
throw new IndexOutOfRangeException("Tried to peek after end of buffer");
|
||||
}
|
||||
|
||||
public void Mark()
|
||||
{
|
||||
MarkedPosition = position;
|
||||
}
|
||||
|
||||
public T[] GetMarkedIntervall()
|
||||
{
|
||||
T[] intervall = new T[position - MarkedPosition];
|
||||
Array.Copy(buffer, MarkedPosition, intervall, 0, intervall.Length);
|
||||
return intervall;
|
||||
}
|
||||
|
||||
public int Remaining => buffer.Length - position;
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
using System;
|
||||
using appsrv.templates.token;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
namespace appsrv.streams
|
||||
{
|
||||
public class TokenStream : PeekAbleStream<Token>
|
||||
{
|
||||
public TokenStream(Token[] tokens)
|
||||
:base(tokens)
|
||||
{
|
||||
}
|
||||
|
||||
public TokenStream(IEnumerable<Token> tokens)
|
||||
:base(tokens.ToArray())
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,256 @@
|
|||
using System;
|
||||
using appsrv.server;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
using System.Linq;
|
||||
using System.ComponentModel;
|
||||
using appsrv.templates.token;
|
||||
using appsrv.resources;
|
||||
using System.Resources;
|
||||
namespace appsrv.templates
|
||||
{
|
||||
public class FormContext
|
||||
{
|
||||
private Dictionary<string, object> vars = new Dictionary<string, object>();
|
||||
protected MemoryStream contentStream, headStream;
|
||||
|
||||
public Template SharpForm { get; }
|
||||
public Request Request { get; }
|
||||
|
||||
public Stream ContentStream => contentStream;
|
||||
public TextWriter ContentWriter { get; protected set; }
|
||||
|
||||
public Stream HeadStream => headStream;
|
||||
public TextWriter HeadWriter { get; protected set; }
|
||||
|
||||
public FormContext Parent { get; }
|
||||
public bool UsesClonedVars { get; }
|
||||
|
||||
public bool IsRootContext => Parent == null;
|
||||
|
||||
public FormContext SubFrameContext { get; protected set; }
|
||||
|
||||
public FormContext(Template sharpForm,Request request,object o)
|
||||
{
|
||||
Parent = null;
|
||||
|
||||
SharpForm = sharpForm;
|
||||
Request = request;
|
||||
|
||||
contentStream = new MemoryStream();
|
||||
ContentWriter = new StreamWriter(contentStream);
|
||||
|
||||
headStream = new MemoryStream();
|
||||
HeadWriter = new StreamWriter(headStream);
|
||||
|
||||
vars["o"] = o;
|
||||
vars["this"] = o;
|
||||
vars["StrMax"] = GetType().GetMethod("StrMax");
|
||||
}
|
||||
|
||||
public FormContext(FormContext parent,bool cloneVars = false,bool independentContent = false,FormContext subFrameContext = null)
|
||||
{
|
||||
Parent = parent;
|
||||
|
||||
SharpForm = parent.SharpForm;
|
||||
Request = parent.Request;
|
||||
|
||||
contentStream = independentContent ? new MemoryStream() : parent.contentStream;
|
||||
ContentWriter = new StreamWriter(ContentStream);
|
||||
|
||||
headStream = parent.headStream;
|
||||
HeadWriter = parent.HeadWriter;
|
||||
|
||||
UsesClonedVars = cloneVars;
|
||||
|
||||
SubFrameContext = subFrameContext == null ? parent.SubFrameContext : subFrameContext;
|
||||
|
||||
if (cloneVars){
|
||||
foreach (KeyValuePair<string,object> var in parent.vars)
|
||||
{
|
||||
this.vars.Add(var.Key, var.Value);
|
||||
}
|
||||
}
|
||||
|
||||
parent.ContentWriter.Flush();
|
||||
}
|
||||
|
||||
public byte[] ContentBytes
|
||||
{
|
||||
get
|
||||
{
|
||||
ContentWriter.Flush();
|
||||
return contentStream.ToArray();
|
||||
}
|
||||
}
|
||||
public byte[] HeadBytes
|
||||
{
|
||||
get
|
||||
{
|
||||
HeadWriter.Flush();
|
||||
return headStream.ToArray();
|
||||
}
|
||||
}
|
||||
public String Content => Encoding.UTF8.GetString(ContentBytes);
|
||||
public String Head => Encoding.UTF8.GetString(HeadBytes);
|
||||
|
||||
public void Insert(FormContext context, bool allToHead = false)
|
||||
{
|
||||
if (context.headStream != headStream)
|
||||
{
|
||||
HeadWriter.Flush();
|
||||
byte[] headBytes = context.HeadBytes;
|
||||
headStream.Write(headBytes, 0, headBytes.Length);
|
||||
}
|
||||
|
||||
ContentWriter.Flush();
|
||||
byte[] contentBytes = context.ContentBytes;
|
||||
|
||||
if (allToHead)
|
||||
headStream.Write(contentBytes, 0, contentBytes.Length);
|
||||
else
|
||||
contentStream.Write(contentBytes, 0, contentBytes.Length);
|
||||
}
|
||||
|
||||
public bool HasVar(String name)
|
||||
{
|
||||
if (vars.ContainsKey(name))
|
||||
return true;
|
||||
if (!UsesClonedVars && (Parent != null))
|
||||
return Parent.HasVar(name);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public object Get(String name){
|
||||
if (vars.ContainsKey(name))
|
||||
return vars[name];
|
||||
else if (Parent != null)
|
||||
return Parent.Get(name);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Set(String name,object value){
|
||||
if (!UsesClonedVars && (Parent != null) && (Parent.HasVar(name)))
|
||||
{
|
||||
Parent.Set(name, value);
|
||||
} else {
|
||||
this.vars[name] = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public object EvaluateExpression(Token[] tokens)
|
||||
{
|
||||
if (tokens.Length == 0)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
Stack<Token> tokenStack = new Stack<Token>(tokens);
|
||||
object currentValue = null;
|
||||
Token currentToken = null;
|
||||
|
||||
while (tokenStack.Count > 0){
|
||||
currentToken = tokenStack.Pop();
|
||||
|
||||
if (currentToken is PathToken)
|
||||
{
|
||||
PathToken pathToken = (PathToken)currentToken;
|
||||
Response response = Request.SubRequest(pathToken.Path);
|
||||
if (response.Reference != null)
|
||||
currentValue = response.Reference;
|
||||
else
|
||||
currentValue = Encoding.UTF8.GetString(response.ContentBytes);
|
||||
} else if (currentToken is KeywordToken)
|
||||
{
|
||||
KeywordToken keywordToken = (KeywordToken)currentToken;
|
||||
currentValue = keywordToken.Evaluate(this);
|
||||
}
|
||||
|
||||
if (tokenStack.Count > 0){
|
||||
currentToken = tokenStack.Pop();
|
||||
throw new NotImplementedException("Currently no expression operators are implemented, sorry");
|
||||
}
|
||||
|
||||
}
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
public object EvaluateValue(String sValue)
|
||||
{
|
||||
Stack<String> vpath = new Stack<string>(sValue.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries).Reverse());
|
||||
|
||||
if (vpath.Count > 0)
|
||||
{
|
||||
object value = null;
|
||||
string key = vpath.Pop();
|
||||
|
||||
value = Get(key);
|
||||
|
||||
while (vpath.Count > 0)
|
||||
{
|
||||
key = vpath.Pop();
|
||||
value = GetObjectFieldOrProperty(value, key);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool EvaluateBoolean(String expression)
|
||||
{
|
||||
bool invert = (expression[0] == '!');
|
||||
if (invert)
|
||||
expression = expression.Substring(1);
|
||||
|
||||
object value = EvaluateValue(expression);
|
||||
if (value != null)
|
||||
{
|
||||
if (
|
||||
((value is bool) && (((bool)value))) ||
|
||||
((value is string) && (!String.Empty.Equals(value))) ||
|
||||
((value is int) && (((int)value) != 0)) ||
|
||||
((value is long) && (((long)value) != 0)) ||
|
||||
((value is float) && (((float)value) != 0)) ||
|
||||
((value is double) && (((double)value) != 0))
|
||||
)
|
||||
{
|
||||
return !invert;
|
||||
}
|
||||
}
|
||||
return invert;
|
||||
}
|
||||
|
||||
private object GetObjectFieldOrProperty(object o, string name)
|
||||
{
|
||||
if (o == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
FieldInfo fieldInfo = o.GetType().GetField(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
|
||||
if (fieldInfo != null)
|
||||
{
|
||||
return fieldInfo.GetValue(o);
|
||||
}
|
||||
PropertyInfo propertyInfo = o.GetType().GetProperty(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
|
||||
if (propertyInfo != null)
|
||||
{
|
||||
return propertyInfo.GetValue(o);
|
||||
}
|
||||
|
||||
throw new KeyNotFoundException(String.Format("object of type {0} has no field/property named {1}", o.GetType().FullName, name));
|
||||
}
|
||||
|
||||
public static string StrMax(string s,int len,string suffix = "")
|
||||
{
|
||||
if (len < s.Length)
|
||||
return s.Substring(0, len) + suffix;
|
||||
return s;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
using System;
|
||||
using System.Xml;
|
||||
using System.Collections.Generic;
|
||||
using appsrv.server;
|
||||
using appsrv.templates.elements;
|
||||
|
||||
namespace appsrv.templates
|
||||
{
|
||||
public abstract class FormElement
|
||||
{
|
||||
/* Instance Members */
|
||||
public FormElement Container { get; }
|
||||
|
||||
List<FormElement> children = new List<FormElement>();
|
||||
|
||||
public FormElement(FormElement container)
|
||||
{
|
||||
Container = container;
|
||||
if (container != null)
|
||||
{
|
||||
container.children.Add(this);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void Run(FormContext context);
|
||||
|
||||
public FormElement[] Children => children.ToArray();
|
||||
public virtual Template Form => Container.Form;
|
||||
|
||||
public void RunChildren(FormContext context)
|
||||
{
|
||||
foreach (FormElement child in children)
|
||||
{
|
||||
child.Run(context);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,297 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using appsrv.templates.token;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using appsrv.templates.elements;
|
||||
using appsrv.streams;
|
||||
using System.Runtime.InteropServices;
|
||||
namespace appsrv.templates
|
||||
{
|
||||
|
||||
/**
|
||||
*
|
||||
* Expression Syntax
|
||||
*
|
||||
* expr := ( <constant> | ( <objpath> | <path> ) [ "(" <arguments> ")" ] )
|
||||
* objpath := <keyword> [ "." <objpath> ]
|
||||
* arguments := <expr> [ "," <arguments> ]
|
||||
*
|
||||
*
|
||||
**/
|
||||
|
||||
|
||||
public static class Scanner
|
||||
{
|
||||
private static char[] letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray();
|
||||
private static char[] digits = "0123456789".ToCharArray();
|
||||
|
||||
private static char[] keywordChars = letters.Concat(digits).Concat(new char[] { '_' }).ToArray();
|
||||
private static char[] pathChars = keywordChars.Concat(new char[] { '.','/' }).ToArray();
|
||||
private static char[] numberChars = digits.Concat(new char[] { '.' }).ToArray();
|
||||
|
||||
private static char[] operatorChars = new char[] { '+', '-', '*', '/' };
|
||||
|
||||
|
||||
private static char[] ScanValidCharacters(CharStream charStream,char[] validCharacters)
|
||||
{
|
||||
charStream.Mark();
|
||||
while (!charStream.EndOfBuffer && validCharacters.Contains(charStream.Peek())){
|
||||
charStream.Read();
|
||||
}
|
||||
return charStream.GetMarkedIntervall();
|
||||
}
|
||||
|
||||
public static String ScanKeyword(CharStream charStream)
|
||||
{
|
||||
if (!Char.IsLetter(charStream.Peek()))
|
||||
throw new ArgumentException(String.Format("ScanKeyword positioned on character {0}", charStream.Peek()));
|
||||
|
||||
return new string(ScanValidCharacters(charStream,keywordChars));
|
||||
}
|
||||
|
||||
public static String ScanNumber(CharStream charStream)
|
||||
{
|
||||
if (!Char.IsDigit(charStream.Peek()))
|
||||
throw new ArgumentException(String.Format("ScanNumber positioned on character {0}", charStream.Peek()));
|
||||
|
||||
return new string(ScanValidCharacters(charStream, numberChars));
|
||||
}
|
||||
|
||||
public static string ScanString(CharStream charStream)
|
||||
{
|
||||
if (charStream.Peek() != '"')
|
||||
throw new ArgumentException(String.Format("ScanString positioned on {0}", charStream.Peek()));
|
||||
|
||||
charStream.Read();
|
||||
charStream.Mark();
|
||||
|
||||
while (charStream.Peek() != '"')
|
||||
{
|
||||
if (charStream.Read() == '\\')
|
||||
{
|
||||
charStream.Read();
|
||||
}
|
||||
}
|
||||
|
||||
string result = new string(charStream.GetMarkedIntervall());
|
||||
|
||||
charStream.Read();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static PathToken ScanPath(CharStream charStream)
|
||||
{
|
||||
if ((charStream.Peek() != '/')&&(charStream.Peek() != '.'))
|
||||
throw new ArgumentException(String.Format("ScanPath positioned on character {0}", charStream.Peek()));
|
||||
|
||||
if (charStream.Peek() == '.')
|
||||
{
|
||||
charStream.Read();
|
||||
charStream.Read();
|
||||
}
|
||||
return new PathToken(new string(ScanValidCharacters(charStream, pathChars)));
|
||||
}
|
||||
|
||||
public static Token ScanObjectPath(CharStream charStream,ObjectPathToken parent = null){
|
||||
if (!Char.IsLetter(charStream.Peek()))
|
||||
throw new ArgumentOutOfRangeException(String.Format("ScanObjectPath is not positioned on a letter: {0}",charStream.Peek()));
|
||||
|
||||
String component = ScanKeyword(charStream);
|
||||
|
||||
if (charStream.Peek() == '.')
|
||||
{
|
||||
charStream.Read();
|
||||
return ScanObjectPath(charStream, new ObjectPathToken(component, parent));
|
||||
} else if (charStream.Peek() == '(')
|
||||
{
|
||||
Token[] arguments = ScanArguments(charStream);
|
||||
return new CallToken(component, arguments, parent);
|
||||
} else {
|
||||
return new ObjectPathToken(component, parent);
|
||||
}
|
||||
}
|
||||
|
||||
public static char[] SeekProcessingInstruction(CharStream charStream)
|
||||
{
|
||||
charStream.Mark();
|
||||
while (!charStream.EndOfBuffer)
|
||||
{
|
||||
if ((charStream.Remaining > 1) && (charStream.Peek(0) == '<') && (charStream.Peek(1) == '?'))
|
||||
break;
|
||||
charStream.Read();
|
||||
}
|
||||
return charStream.GetMarkedIntervall();
|
||||
}
|
||||
|
||||
public static Token ScanExpression(CharStream charStream)
|
||||
{
|
||||
while (!charStream.EndOfBuffer)
|
||||
{
|
||||
if (charStream.Peek() == '"')
|
||||
{
|
||||
return new ValueToken(ScanString(charStream));
|
||||
}
|
||||
else if (Char.IsLetter(charStream.Peek()))
|
||||
{
|
||||
return ScanObjectPath(charStream);
|
||||
}
|
||||
else if ((charStream.Peek() == '/')||(charStream.Peek() == '.'))
|
||||
{
|
||||
return ScanPath(charStream);
|
||||
}
|
||||
else if (Char.IsDigit(charStream.Peek()))
|
||||
{
|
||||
return new NumberToken(ScanNumber(charStream));
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Unexpected character: {0}", charStream.Read());
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Token[] ScanArguments(CharStream charStream){
|
||||
|
||||
if (charStream.Peek() != '(')
|
||||
throw new ArgumentException(String.Format("ScanArguments not positioned on ("));
|
||||
|
||||
charStream.Read();
|
||||
|
||||
List<Token> arguments = new List<Token>();
|
||||
|
||||
while (!charStream.EndOfBuffer && (charStream.Peek() != ')'))
|
||||
{
|
||||
if (Char.IsWhiteSpace(charStream.Peek()))
|
||||
{
|
||||
charStream.Read();
|
||||
} else {
|
||||
arguments.Add(ScanExpression(charStream));
|
||||
while (Char.IsWhiteSpace(charStream.Peek()))
|
||||
{
|
||||
charStream.Read();
|
||||
}
|
||||
if (charStream.Peek() == ')')
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (charStream.Peek() != ',')
|
||||
throw new ArgumentException(String.Format("expected , or ) after argument"));
|
||||
}
|
||||
}
|
||||
charStream.Read();
|
||||
return arguments.ToArray();
|
||||
}
|
||||
|
||||
public static Token[] ScanParameters(CharStream charStream)
|
||||
{
|
||||
List<Token> tokens = new List<Token>();
|
||||
|
||||
while (!charStream.EndOfBuffer)
|
||||
{
|
||||
if ((charStream.Remaining > 1) && (charStream.Peek() == '?') && (charStream.Peek(1) == '>'))
|
||||
{
|
||||
charStream.Read();
|
||||
charStream.Read();
|
||||
break;
|
||||
}
|
||||
else if (Char.IsWhiteSpace(charStream.Peek()))
|
||||
{
|
||||
charStream.Read();
|
||||
} else {
|
||||
tokens.Add(ScanExpression(charStream));
|
||||
}
|
||||
}
|
||||
return tokens.ToArray();
|
||||
}
|
||||
|
||||
public static void Scan(CharStream sourceStream, FormElement container)
|
||||
{
|
||||
while (!sourceStream.EndOfBuffer)
|
||||
{
|
||||
char[] textPart = SeekProcessingInstruction(sourceStream);
|
||||
if (textPart.Length > 0)
|
||||
{
|
||||
new Text(container, textPart);
|
||||
}
|
||||
if (!sourceStream.EndOfBuffer)
|
||||
{
|
||||
if (!ScanProcessingInstruction(sourceStream, container))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ScanProcessingInstruction(CharStream sourceStream, FormElement container)
|
||||
{
|
||||
if ((sourceStream.Remaining > 1) && (sourceStream.Peek(0) == '<') && (sourceStream.Peek(1) == '?'))
|
||||
{
|
||||
sourceStream.Read();
|
||||
sourceStream.Read();
|
||||
|
||||
if (sourceStream.Peek() == '=')
|
||||
{
|
||||
sourceStream.Read();
|
||||
new Var(container, ScanParameters(sourceStream));
|
||||
}
|
||||
else
|
||||
{
|
||||
String keyword = ScanKeyword(sourceStream);
|
||||
Token[] parameters = ScanParameters(sourceStream);
|
||||
|
||||
switch (keyword)
|
||||
{
|
||||
case "include":
|
||||
new Include(container, parameters);
|
||||
break;
|
||||
case "head":
|
||||
Scan(sourceStream, new Head(container));
|
||||
break;
|
||||
case "set":
|
||||
new Set(container, ((ObjectPathToken)parameters[0]).Component ,parameters[1]);
|
||||
break;
|
||||
case "iterate":
|
||||
Scan(sourceStream, new Iterate(container,((ObjectPathToken)parameters[0]).Component,parameters[1]));
|
||||
break;
|
||||
case "if":
|
||||
Scan(sourceStream, new If(container, parameters[0]));
|
||||
break;
|
||||
case "editor":
|
||||
new Editor(container, parameters);
|
||||
break;
|
||||
case "frame":
|
||||
if (!(parameters[0] is PathToken))
|
||||
throw new ArgumentException("<?frame ...?> needs path as argument.");
|
||||
|
||||
container.Form.Frame = (PathToken)parameters[0];
|
||||
|
||||
break;
|
||||
case "framed":
|
||||
new Framed(container);
|
||||
break;
|
||||
case "else":
|
||||
if (container is ConditionalFormElement)
|
||||
{
|
||||
ConditionalFormElement conditionalFormElement = container as ConditionalFormElement;
|
||||
Scan(sourceStream, conditionalFormElement.AlternativeElement);
|
||||
}
|
||||
break;
|
||||
case "end":
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("ScanProcessingInstruction was not positioned on PI");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,163 @@
|
|||
using System;
|
||||
using System.Xml;
|
||||
using appsrv.resources;
|
||||
using System.IO;
|
||||
using appsrv.server;
|
||||
using System.Collections.Generic;
|
||||
using appsrv.exceptions;
|
||||
using appsrv.http;
|
||||
using System.Text;
|
||||
using appsrv.templates.elements;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using appsrv.templates.token;
|
||||
using appsrv.streams;
|
||||
|
||||
namespace appsrv.templates
|
||||
{
|
||||
public class Template : Resource
|
||||
{
|
||||
String sourceFilename;
|
||||
|
||||
public Resource LookupBase { get; set; }
|
||||
|
||||
public PathToken Frame { get; set; }
|
||||
|
||||
public String Title { get; set; }
|
||||
public SharpFormElement RootElement { get; protected set; }
|
||||
|
||||
public Template(String filename,Resource container)
|
||||
:base(new FileInfo(filename).Name,container)
|
||||
{
|
||||
sourceFilename = filename;
|
||||
|
||||
LookupBase = container.Root;
|
||||
|
||||
LoadSource();
|
||||
}
|
||||
|
||||
protected override void Add(Resource resource)
|
||||
{
|
||||
throw new NotImplementedException("");
|
||||
}
|
||||
|
||||
private void LoadSource()
|
||||
{
|
||||
byte[] loadBuffer;
|
||||
|
||||
RootElement = new SharpFormElement(this);
|
||||
|
||||
using (FileStream fileStream = new FileStream(sourceFilename,FileMode.Open))
|
||||
{
|
||||
loadBuffer = new byte[fileStream.Length];
|
||||
fileStream.Read(loadBuffer, 0, loadBuffer.Length);
|
||||
fileStream.Close();
|
||||
}
|
||||
|
||||
CharStream sourceStream = new CharStream(loadBuffer);
|
||||
Scanner.Scan(sourceStream, RootElement);
|
||||
|
||||
LastModification = File.GetLastWriteTime(sourceFilename);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public bool CheckReloadNeeded()
|
||||
{
|
||||
bool reloadNeeded = (File.GetLastWriteTime(sourceFilename) > LastModification);
|
||||
if (reloadNeeded)
|
||||
{
|
||||
LoadSource();
|
||||
}
|
||||
return reloadNeeded;
|
||||
}
|
||||
|
||||
public FormContext Render(FormContext context){
|
||||
CheckReloadNeeded();
|
||||
|
||||
RootElement.Run(context);
|
||||
|
||||
if (Frame != null)
|
||||
{
|
||||
FormContext frameContext = new FormContext(context, independentContent: true, subFrameContext: context);
|
||||
Template frame = (Template)context.SharpForm.FindByPath(Frame.Path);
|
||||
|
||||
return frame.Render(frameContext);
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
public override void Hit(Stack<string> requestPath, Request request)
|
||||
{
|
||||
object o = null;
|
||||
/* if (requestPath.Count > 0)
|
||||
{
|
||||
Response response = request.SubRequest(requestPath);
|
||||
if (response.HasValue)
|
||||
o = response.Value;
|
||||
else
|
||||
o = response.Content;
|
||||
}*/
|
||||
Render(request, o);
|
||||
}
|
||||
|
||||
public void Render(Request request,object o,IEnumerable<KeyValuePair<string,object>> keyValues = null)
|
||||
{
|
||||
FormContext renderContext = new FormContext(this, request, o);
|
||||
|
||||
if (keyValues != null)
|
||||
{
|
||||
foreach (KeyValuePair<string,object> kvp in keyValues)
|
||||
{
|
||||
renderContext.Set(kvp.Key,kvp.Value);
|
||||
}
|
||||
}
|
||||
|
||||
renderContext.Set("request", request);
|
||||
|
||||
FormContext context = Render(renderContext);
|
||||
Response response = request.Response;
|
||||
|
||||
response.Headers.Add("Content-type", "text/html;charset=utf-8");
|
||||
|
||||
response.Writer.WriteLine("<!DOCTYPE html>");
|
||||
response.Writer.WriteLine("<html><head>");
|
||||
|
||||
response.Writer.Flush();
|
||||
byte[] headBytes = context.HeadBytes;
|
||||
response.Stream.Write(headBytes,0,headBytes.Length);
|
||||
|
||||
response.Writer.WriteLine("</head><body>");
|
||||
response.Writer.Flush();
|
||||
|
||||
byte[] contentBytes = context.ContentBytes;
|
||||
response.Stream.Write(contentBytes, 0, contentBytes.Length);
|
||||
|
||||
response.Writer.WriteLine("</body></html>");
|
||||
response.Writer.Flush();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class SharpFormElement : FormElement
|
||||
{
|
||||
Template sharpForm;
|
||||
public override Template Form => sharpForm;
|
||||
|
||||
public SharpFormElement(Template sharpForm) : base(null)
|
||||
{
|
||||
this.sharpForm = sharpForm;
|
||||
}
|
||||
|
||||
public override void Run(FormContext context)
|
||||
{
|
||||
foreach (FormElement formElement in Children)
|
||||
{
|
||||
formElement.Run(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using System.Security;
|
||||
using appsrv.templates.token;
|
||||
using System.Globalization;
|
||||
|
||||
namespace appsrv.templates.editors
|
||||
{
|
||||
public class DoubleEditor: HTMLEditor
|
||||
{
|
||||
public DoubleEditor(Template template, ObjectPathToken objectPathToken)
|
||||
:base(template,objectPathToken)
|
||||
{
|
||||
}
|
||||
|
||||
public override void RenderHTML(FormContext context)
|
||||
{
|
||||
context.ContentWriter.Write("<input type=\"number\" class=\"editor number\" name=\"{0}\" step=\"any\" value=\"{1}\"/>",
|
||||
ObjectPathToken.ToString().Replace('.', '_'),
|
||||
GetHTMLValue(context)
|
||||
);
|
||||
}
|
||||
|
||||
public override string GetHTMLValue(FormContext context)
|
||||
{
|
||||
double v = (double)GetValue(context);
|
||||
return v.ToString("G",CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
using System;
|
||||
using appsrv.templates.token;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using appsrv.attributes;
|
||||
namespace appsrv.templates.editors
|
||||
{
|
||||
public delegate HTMLEditor HTMLEditorFactory(FormContext context, ObjectPathToken pathToken, Token[] parameters);
|
||||
|
||||
public abstract class HTMLEditor
|
||||
{
|
||||
static List<HTMLEditorFactory> customEditors = new List<HTMLEditorFactory>();
|
||||
|
||||
static Type[] IntTypes = new Type[]{
|
||||
typeof(int),
|
||||
typeof(Int16),
|
||||
typeof(Int64),
|
||||
typeof(uint),
|
||||
typeof(UInt16),
|
||||
typeof(UInt64)
|
||||
};
|
||||
static Type[] FloatTypes = new Type[]{
|
||||
typeof(float),
|
||||
typeof(double)
|
||||
};
|
||||
|
||||
public static void RegisterFactory(HTMLEditorFactory factory)
|
||||
{
|
||||
customEditors.Add(factory);
|
||||
}
|
||||
|
||||
public static HTMLEditor Construct(FormContext context,ObjectPathToken pathToken,Token[] parameters)
|
||||
{
|
||||
HTMLEditorAttribute htmlEditorAttribute = pathToken.GetCustomAttribute<HTMLEditorAttribute>(context);
|
||||
Type valueType = pathToken.GetTargetType(context);
|
||||
|
||||
HTMLEditor editor = null;
|
||||
foreach (HTMLEditorFactory factory in customEditors)
|
||||
{
|
||||
editor = factory(context, pathToken, parameters);
|
||||
if (editor != null)
|
||||
return editor;
|
||||
}
|
||||
|
||||
if (IntTypes.Contains(valueType))
|
||||
return new IntegerEditor(context.SharpForm, pathToken);
|
||||
|
||||
if (FloatTypes.Contains(valueType))
|
||||
return new DoubleEditor(context.SharpForm, pathToken);
|
||||
|
||||
return new StringEditor(context.SharpForm, pathToken);
|
||||
}
|
||||
|
||||
public Template Template { get; }
|
||||
public ObjectPathToken ObjectPathToken { get; }
|
||||
|
||||
public HTMLEditor(Template template,ObjectPathToken objectPathToken)
|
||||
{
|
||||
Template = template;
|
||||
ObjectPathToken = objectPathToken;
|
||||
}
|
||||
|
||||
public abstract void RenderHTML(FormContext context);
|
||||
|
||||
|
||||
public virtual object GetValue(FormContext context){
|
||||
return ObjectPathToken.Evaluate(context);
|
||||
}
|
||||
|
||||
public virtual String GetHTMLValue(FormContext context)
|
||||
{
|
||||
object v = GetValue(context);
|
||||
if (v == null)
|
||||
return "";
|
||||
return v.ToString();
|
||||
}
|
||||
|
||||
public virtual void SetValue(FormContext context,object value){
|
||||
ObjectPathToken.SetValue(context, value);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
using System;
|
||||
using System.Security;
|
||||
using appsrv.templates.token;
|
||||
|
||||
namespace appsrv.templates.editors
|
||||
{
|
||||
public class IntegerEditor: HTMLEditor
|
||||
{
|
||||
public IntegerEditor(Template template, ObjectPathToken objectPathToken)
|
||||
:base(template,objectPathToken)
|
||||
{
|
||||
}
|
||||
|
||||
public override void RenderHTML(FormContext context)
|
||||
{
|
||||
context.ContentWriter.Write("<input type=\"number\" class=\"editor number\" name=\"{0}\" step=\"1\" value=\"{1}\"/>",
|
||||
ObjectPathToken.ToString().Replace('.', '_'),
|
||||
GetHTMLValue(context)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
using System.Security;
|
||||
using appsrv.templates.token;
|
||||
|
||||
namespace appsrv.templates.editors
|
||||
{
|
||||
public class MultiLineEditor: HTMLEditor
|
||||
{
|
||||
public MultiLineEditor(Template template, ObjectPathToken objectPathToken)
|
||||
:base(template,objectPathToken)
|
||||
{
|
||||
}
|
||||
|
||||
public override void RenderHTML(FormContext context)
|
||||
{
|
||||
context.ContentWriter.Write("<textarea class=\"editor string\" name=\"{0}\">{1}</textarea>",
|
||||
ObjectPathToken.ToString().Replace('.', '_'),
|
||||
GetHTMLValue(context)
|
||||
);
|
||||
}
|
||||
|
||||
public override string GetHTMLValue(FormContext context)
|
||||
{
|
||||
return SecurityElement.Escape(base.GetHTMLValue(context));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
using System.Security;
|
||||
using appsrv.templates.token;
|
||||
|
||||
namespace appsrv.templates.editors
|
||||
{
|
||||
public class StringEditor : HTMLEditor
|
||||
{
|
||||
public StringEditor(Template template, ObjectPathToken objectPathToken)
|
||||
:base(template,objectPathToken)
|
||||
{
|
||||
}
|
||||
|
||||
public override void RenderHTML(FormContext context)
|
||||
{
|
||||
context.ContentWriter.Write("<input type=\"text\" class=\"editor string\" name=\"{0}\" value=\"{1}\"/>",
|
||||
ObjectPathToken.ToString().Replace('.', '_'),
|
||||
GetHTMLValue(context)
|
||||
);
|
||||
}
|
||||
|
||||
public override string GetHTMLValue(FormContext context)
|
||||
{
|
||||
return SecurityElement.Escape(base.GetHTMLValue(context));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
using System;
|
||||
using System.Xml;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Linq;
|
||||
using appsrv.templates.token;
|
||||
using appsrv.templates.editors;
|
||||
using appsrv.attributes;
|
||||
|
||||
namespace appsrv.templates.elements
|
||||
{
|
||||
public class Editor : FormElement
|
||||
{
|
||||
public ObjectPathToken PathToken { get; }
|
||||
public Token[] Parameters { get; }
|
||||
|
||||
|
||||
public HTMLEditor HTMLEditor { get; protected set; }
|
||||
|
||||
public Token EditorName { get; protected set; }
|
||||
|
||||
public Editor(FormElement container,Token[] tokens)
|
||||
:base(container)
|
||||
{
|
||||
PathToken = (ObjectPathToken)tokens[0];
|
||||
Parameters = tokens.Skip(1).ToArray();
|
||||
}
|
||||
|
||||
public override void Run(FormContext context)
|
||||
{
|
||||
if (HTMLEditor == null)
|
||||
{
|
||||
HTMLEditor = HTMLEditor.Construct(context, PathToken, Parameters);
|
||||
}
|
||||
HTMLEditor.RenderHTML(context);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
using System;
|
||||
using System.Xml;
|
||||
namespace appsrv.templates.elements
|
||||
{
|
||||
public class Entity : FormElement
|
||||
{
|
||||
public String EntityName { get; }
|
||||
|
||||
public Entity(FormElement container,XmlNode xmlNode)
|
||||
:base(container)
|
||||
{
|
||||
EntityName = xmlNode.Name;
|
||||
}
|
||||
|
||||
public override void Run(FormContext context)
|
||||
{
|
||||
context.ContentWriter.Write("&{0}",EntityName);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
using System;
|
||||
namespace appsrv.templates.elements
|
||||
{
|
||||
public class Framed : FormElement
|
||||
{
|
||||
public Framed(FormElement container)
|
||||
:base(container)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Run(FormContext context)
|
||||
{
|
||||
if (context.SubFrameContext != null){
|
||||
context.Insert(context.SubFrameContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
using System;
|
||||
using System.Xml;
|
||||
|
||||
namespace appsrv.templates.elements
|
||||
{
|
||||
public class Head : FormElement
|
||||
{
|
||||
public Head(FormElement container)
|
||||
:base(container)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Run(FormContext context)
|
||||
{
|
||||
FormContext headContext = new FormContext(context, independentContent: true);
|
||||
|
||||
RunChildren(headContext);
|
||||
|
||||
context.Insert(headContext,true);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
using System;
|
||||
using System.Xml;
|
||||
using appsrv.templates.token;
|
||||
namespace appsrv.templates.elements
|
||||
{
|
||||
public abstract class ConditionalFormElement : FormElement
|
||||
{
|
||||
public FormElement AlternativeElement { get; }
|
||||
|
||||
|
||||
public ConditionalFormElement(FormElement container)
|
||||
:base(container)
|
||||
{
|
||||
AlternativeElement = new AlternativeFormElement(this);
|
||||
}
|
||||
|
||||
|
||||
public class AlternativeFormElement : FormElement
|
||||
{
|
||||
public AlternativeFormElement(ConditionalFormElement container)
|
||||
:base(container.Container)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Run(FormContext context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class If : ConditionalFormElement
|
||||
{
|
||||
public Token Expression { get; }
|
||||
|
||||
public If(FormElement container,Token expression)
|
||||
:base(container)
|
||||
{
|
||||
Expression = expression;
|
||||
}
|
||||
|
||||
public bool EvaluateExpression(FormContext context){
|
||||
object result = Expression.Evaluate(context);
|
||||
if (result == null)
|
||||
return false;
|
||||
|
||||
if ((result is int) && ((int)result == 0))
|
||||
return false;
|
||||
if ((result is float) && ((float)result == 0.0))
|
||||
return false;
|
||||
if ((result is bool) && ((bool)result == false))
|
||||
return false;
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Run(FormContext context)
|
||||
{
|
||||
if (EvaluateExpression(context))
|
||||
RunChildren(context);
|
||||
else
|
||||
AlternativeElement.RunChildren(context);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
using System;
|
||||
using System.Xml;
|
||||
using appsrv.resources;
|
||||
using System.Threading;
|
||||
using appsrv.templates.token;
|
||||
using System.Runtime.Remoting.Contexts;
|
||||
namespace appsrv.templates.elements
|
||||
{
|
||||
public class Include : FormElement
|
||||
{
|
||||
public PathToken Path { get; }
|
||||
public Token OToken { get; }
|
||||
|
||||
public Include(FormElement container,Token[] parameters)
|
||||
:base(container)
|
||||
{
|
||||
Path = (PathToken)parameters[0];
|
||||
if (parameters.Length > 1)
|
||||
{
|
||||
OToken = parameters[1];
|
||||
}
|
||||
}
|
||||
|
||||
public override void Run(FormContext context)
|
||||
{
|
||||
Resource frameResource = (Resource)context.SharpForm.Root.FindByPath(Path.Path);
|
||||
if (frameResource is Template)
|
||||
{
|
||||
Template frame = (Template)frameResource;
|
||||
FormContext frameContext = new FormContext(context);
|
||||
|
||||
frame.Render(frameContext);
|
||||
|
||||
frameContext.HeadWriter.Flush();
|
||||
frameContext.ContentWriter.Flush();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException(String.Format("Path {0} is not a SharpForm", Path.Path));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
using System;
|
||||
using System.Xml;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections;
|
||||
using appsrv.streams;
|
||||
using appsrv.templates.token;
|
||||
namespace appsrv.templates.elements
|
||||
{
|
||||
public class Iterate : FormElement
|
||||
{
|
||||
public String Key { get; }
|
||||
public Token Expression { get; }
|
||||
|
||||
public Iterate(FormElement container,String key,Token expression)
|
||||
:base(container)
|
||||
{
|
||||
Key = key;
|
||||
Expression = expression;
|
||||
}
|
||||
|
||||
public override void Run(FormContext context)
|
||||
{
|
||||
FormContext iterateContext = new FormContext(context);
|
||||
|
||||
object iteration = Expression.Evaluate(context);
|
||||
if (iteration is IEnumerable){
|
||||
IEnumerable enumerable = (IEnumerable)iteration;
|
||||
IEnumerator enumerator = enumerable.GetEnumerator();
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
iterateContext.Set(Key, enumerator.Current);
|
||||
RunChildren(iterateContext);
|
||||
}
|
||||
|
||||
iterateContext.ContentWriter.Flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
using System;
|
||||
using System.Xml;
|
||||
using System.Text;
|
||||
using appsrv.templates.token;
|
||||
namespace appsrv.templates.elements
|
||||
{
|
||||
public class Set : FormElement
|
||||
{
|
||||
public String Target { get; }
|
||||
public Token Expression { get; }
|
||||
|
||||
public Set(FormElement container,String target,Token expression)
|
||||
:base(container)
|
||||
{
|
||||
Target = target;
|
||||
Expression = expression;
|
||||
}
|
||||
|
||||
public override void Run(FormContext context)
|
||||
{
|
||||
context.Set(Target, Expression.Evaluate(context));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
using System;
|
||||
namespace appsrv.templates.elements
|
||||
{
|
||||
public class Text : FormElement
|
||||
{
|
||||
private char[] chars;
|
||||
|
||||
public Text(FormElement container,char[] text)
|
||||
:base(container)
|
||||
{
|
||||
this.chars = text;
|
||||
}
|
||||
|
||||
public override void Run(FormContext context)
|
||||
{
|
||||
context.ContentWriter.Write(this.chars);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
using System;
|
||||
using System.Xml;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Linq;
|
||||
using appsrv.templates.token;
|
||||
namespace appsrv.templates.elements
|
||||
{
|
||||
public class Var : FormElement
|
||||
{
|
||||
Token[] tokens;
|
||||
|
||||
public Var(FormElement container,Token[] tokens)
|
||||
:base(container)
|
||||
{
|
||||
this.tokens = tokens;
|
||||
}
|
||||
|
||||
public override void Run(FormContext context)
|
||||
{
|
||||
foreach (Token token in tokens)
|
||||
{
|
||||
object o = token.Evaluate(context);
|
||||
if (o != null)
|
||||
o = o.ToString();
|
||||
|
||||
context.ContentWriter.Write(
|
||||
System.Net.WebUtility.HtmlEncode(o as string)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
namespace appsrv.templates.token
|
||||
{
|
||||
public class CallToken : Token
|
||||
{
|
||||
public Token Owner { get; }
|
||||
public String Method { get; }
|
||||
public Token[] Arguments { get; }
|
||||
|
||||
public CallToken(String method,Token[] arguments,Token owner)
|
||||
{
|
||||
Owner = owner;
|
||||
Method = method;
|
||||
Arguments = arguments;
|
||||
}
|
||||
|
||||
public override object Evaluate(FormContext context)
|
||||
{
|
||||
object owner;
|
||||
Type ownerType;
|
||||
|
||||
if (Owner != null)
|
||||
{
|
||||
owner = Owner.Evaluate(context);
|
||||
ownerType = owner.GetType();
|
||||
} else {
|
||||
MethodInfo methodInfo = context.Get(Method) as MethodInfo;
|
||||
|
||||
owner = null;
|
||||
ownerType = methodInfo.DeclaringType;
|
||||
}
|
||||
|
||||
Type[] types = new Type[Arguments.Length];
|
||||
object[] arguments = new object[Arguments.Length];
|
||||
|
||||
for (int n = 0; n < arguments.Length;n++)
|
||||
{
|
||||
arguments[n] = Arguments[n].Evaluate(context);
|
||||
}
|
||||
|
||||
return ownerType.InvokeMember(Method, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, owner, arguments);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
using System;
|
||||
namespace appsrv.templates.token
|
||||
{
|
||||
public class KeywordToken : Token
|
||||
{
|
||||
public String Value { get; }
|
||||
|
||||
public KeywordToken(string value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public override object Evaluate(FormContext context)
|
||||
{
|
||||
return context.EvaluateValue(Value);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format("[KeywordToken Value={0}]", Value);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
using System;
|
||||
namespace appsrv.templates.token
|
||||
{
|
||||
public class NumberToken : ValueToken
|
||||
{
|
||||
public NumberToken(String value)
|
||||
{
|
||||
if (value.IndexOf('.') != -1)
|
||||
Value = Double.Parse(value);
|
||||
else
|
||||
Value = int.Parse(value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
namespace appsrv.templates.token
|
||||
{
|
||||
public class ObjectPathToken : Token
|
||||
{
|
||||
public ObjectPathToken ParentToken { get; }
|
||||
public String Component { get; }
|
||||
|
||||
public ObjectPathToken(String component,ObjectPathToken parent)
|
||||
{
|
||||
Component = component;
|
||||
ParentToken = parent;
|
||||
}
|
||||
|
||||
public virtual void SetValue(FormContext context,object value)
|
||||
{
|
||||
if (ParentToken == null)
|
||||
{
|
||||
context.Set(Component, value);
|
||||
} else {
|
||||
object parent = ParentToken.Evaluate(context);
|
||||
if (parent == null)
|
||||
throw new NullReferenceException();
|
||||
|
||||
Type type = parent.GetType();
|
||||
|
||||
FieldInfo fieldInfo = type.GetField(Component, BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
|
||||
if (fieldInfo != null)
|
||||
{
|
||||
if (fieldInfo.FieldType != value.GetType())
|
||||
value = Convert.ChangeType(value, fieldInfo.FieldType);
|
||||
|
||||
fieldInfo.SetValue(parent,value);
|
||||
}
|
||||
|
||||
PropertyInfo propertyInfo = type.GetProperty(Component, BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
|
||||
if (propertyInfo != null)
|
||||
{
|
||||
if (propertyInfo.PropertyType != value.GetType())
|
||||
value = Convert.ChangeType(value, propertyInfo.PropertyType);
|
||||
|
||||
propertyInfo.SetValue(parent,value);
|
||||
}
|
||||
|
||||
throw new MissingMemberException(String.Format("object member not found {0} {1}", Component, parent));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual object GetValue(FormContext context)
|
||||
{
|
||||
object parent = null;
|
||||
|
||||
if (ParentToken == null)
|
||||
{
|
||||
return context.Get(Component);
|
||||
} else {
|
||||
parent = ParentToken.Evaluate(context);
|
||||
}
|
||||
|
||||
|
||||
if (parent == null)
|
||||
throw new NullReferenceException();
|
||||
|
||||
Type type = parent.GetType();
|
||||
|
||||
FieldInfo fieldInfo = type.GetField(Component, BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
|
||||
if (fieldInfo != null)
|
||||
{
|
||||
return fieldInfo.GetValue(parent);
|
||||
}
|
||||
|
||||
PropertyInfo propertyInfo = type.GetProperty(Component, BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
|
||||
if (propertyInfo != null)
|
||||
{
|
||||
return propertyInfo.GetValue(parent);
|
||||
}
|
||||
|
||||
throw new MissingMemberException(String.Format("object member not found {0} {1}",Component,parent));
|
||||
}
|
||||
|
||||
public Type GetTargetType(FormContext context)
|
||||
{
|
||||
if (ParentToken == null)
|
||||
{
|
||||
return context.Get(Component).GetType();
|
||||
}
|
||||
|
||||
Type parentType = ParentToken.GetTargetType(context);
|
||||
|
||||
FieldInfo fieldInfo = parentType.GetField(Component, BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
|
||||
if (fieldInfo != null)
|
||||
{
|
||||
return fieldInfo.FieldType;
|
||||
}
|
||||
|
||||
PropertyInfo propertyInfo = parentType.GetProperty(Component, BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
|
||||
if (propertyInfo != null)
|
||||
{
|
||||
return propertyInfo.PropertyType;
|
||||
}
|
||||
|
||||
throw new MissingMemberException(String.Format("object member not found {0} {1}", Component, parentType));
|
||||
}
|
||||
|
||||
public T GetCustomAttribute<T>(FormContext context) where T: Attribute
|
||||
{
|
||||
Type parentType = (ParentToken == null) ? context.Get(Component).GetType() : ParentToken.GetTargetType(context);
|
||||
|
||||
FieldInfo fieldInfo = parentType.GetField(Component, BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
|
||||
if (fieldInfo != null)
|
||||
{
|
||||
return fieldInfo.GetCustomAttribute<T>();
|
||||
}
|
||||
|
||||
PropertyInfo propertyInfo = parentType.GetProperty(Component, BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
|
||||
if (propertyInfo != null)
|
||||
{
|
||||
return propertyInfo.GetCustomAttribute<T>();
|
||||
}
|
||||
|
||||
throw new MissingMemberException(String.Format("object member not found {0} {1}", Component, parentType));
|
||||
}
|
||||
|
||||
public override object Evaluate(FormContext context) => GetValue(context);
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (ParentToken == null)
|
||||
return Component;
|
||||
|
||||
return String.Format("{0}.{1}",ParentToken.ToString(),Component);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
using System;
|
||||
namespace appsrv.templates.token
|
||||
{
|
||||
public class OperatorToken : Token
|
||||
{
|
||||
public char Operator { get; }
|
||||
|
||||
public OperatorToken(char op)
|
||||
{
|
||||
Operator = op;
|
||||
}
|
||||
|
||||
public override object Evaluate(FormContext context)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format("[OperatorToken Operator={0}]", Operator);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
using System;
|
||||
using appsrv.resources;
|
||||
using System.Text;
|
||||
using appsrv.server;
|
||||
namespace appsrv.templates.token
|
||||
{
|
||||
public class PathToken : Token
|
||||
{
|
||||
public string Path { get; set; }
|
||||
|
||||
public PathToken(String path)
|
||||
{
|
||||
Path = path;
|
||||
}
|
||||
|
||||
public override object Evaluate(FormContext context)
|
||||
{
|
||||
Response response = context.Request.SubRequest(Path);
|
||||
response.Flush();
|
||||
|
||||
byte[] content = response.ContentBytes;
|
||||
return Encoding.UTF8.GetString(content);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
using System;
|
||||
namespace appsrv.templates.token
|
||||
{
|
||||
public abstract class Token
|
||||
{
|
||||
public Token()
|
||||
{
|
||||
}
|
||||
|
||||
public abstract object Evaluate(FormContext context);
|
||||
}
|
||||
|
||||
public class ValueToken : Token
|
||||
{
|
||||
public object Value { get; protected set; }
|
||||
|
||||
protected ValueToken()
|
||||
{
|
||||
}
|
||||
|
||||
public ValueToken(object value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public override object Evaluate(FormContext context)
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format("[ValueToken Value={0}]", Value);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
using System;
|
||||
using appsrv.attributes;
|
||||
namespace appsrv.test
|
||||
{
|
||||
public class RTest
|
||||
{
|
||||
|
||||
[PublishedMember]
|
||||
public static AClass CreateAClass(){
|
||||
return new AClass();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class AClass {
|
||||
BClass bClass;
|
||||
|
||||
[PublishedMember]
|
||||
public BClass PropB => bClass;
|
||||
[PublishedMember]
|
||||
public BClass GetB() { return bClass; }
|
||||
|
||||
[PublishedMember]
|
||||
public BClass CreateB(String v){
|
||||
return new BClass(this, v);
|
||||
}
|
||||
|
||||
public AClass()
|
||||
{
|
||||
bClass = new BClass(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class BClass {
|
||||
[PublishedMember]
|
||||
public AClass A;
|
||||
|
||||
[PublishedMember]
|
||||
public string Version = "1.0.1234";
|
||||
|
||||
public BClass(AClass a)
|
||||
{
|
||||
A = a;
|
||||
}
|
||||
|
||||
public BClass(AClass a,String v)
|
||||
{
|
||||
A = a;
|
||||
Version = v;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,21 +1,89 @@
|
|||
using System;
|
||||
using appsrv.attributes;
|
||||
using System.Collections.Generic;
|
||||
namespace appsrv.test
|
||||
{
|
||||
|
||||
public class StaticTest
|
||||
{
|
||||
|
||||
[WebCallable]
|
||||
[WebCallableAttribute]
|
||||
public static int Add(int a,int b){
|
||||
Console.WriteLine("StaticTest.Add({0},{1})", a, b);
|
||||
return a + b;
|
||||
}
|
||||
|
||||
[WebCallable]
|
||||
public static void Debug()
|
||||
[WebCallableAttribute]
|
||||
public static String Debug(String debugMessage = null)
|
||||
{
|
||||
Console.WriteLine("StaticTest.Debug() has been called");
|
||||
String debugLine = String.Format("StaticTest.Debug({0}) has been called", debugMessage);
|
||||
Console.WriteLine(debugLine);
|
||||
return debugLine;
|
||||
}
|
||||
|
||||
static string[] lookupItems = new string[]{
|
||||
"Ich bin ein Mondstein",
|
||||
"Du bist ein Saurier",
|
||||
"Wir sind hier",
|
||||
"Ihr seid irgendwo",
|
||||
"Du warst gestern nicht hier",
|
||||
"Ihr seid es heute auch nicht mehr",
|
||||
"Warum sind wir nicht hier?"
|
||||
};
|
||||
|
||||
[WebCallableAttribute(Serialization = SERIALIZATION.JSON)]
|
||||
public static String[] LookupList(String pattern){
|
||||
List<String> result = new List<string>();
|
||||
foreach (String item in lookupItems){
|
||||
if (item.StartsWith(pattern))
|
||||
result.Add(item);
|
||||
}
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
[WebCallableAttribute]
|
||||
public static Test TestInstance(){
|
||||
return new Test();
|
||||
}
|
||||
|
||||
static Random random = new Random();
|
||||
|
||||
[WebCallableAttribute]
|
||||
public static int Random()
|
||||
{
|
||||
return random.Next();
|
||||
}
|
||||
|
||||
[WebCallableAttribute]
|
||||
public static int[] Loop(int loopLength = 1000)
|
||||
{
|
||||
int[] ia = new int[loopLength];
|
||||
for (int n = 0; n < loopLength;n++)
|
||||
{
|
||||
ia[n] = n;
|
||||
}
|
||||
return ia;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class Test
|
||||
{
|
||||
public static int[] Loops => Loop(1000);
|
||||
|
||||
public String Name { get; set; } = "Mein Name";
|
||||
public int age = 18;
|
||||
public String NickName { get; set; } = "Der Spitze";
|
||||
|
||||
string[] texts = lookupItems;
|
||||
|
||||
bool True = true;
|
||||
bool False = false;
|
||||
|
||||
public Test()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<SharpWebApplication>
|
||||
<ApplicationName>localhost</ApplicationName>
|
||||
<Resource Default="/index.html">
|
||||
<Provider Name="StaticDirectory">www</Provider>
|
||||
<Resource>
|
||||
<Provider Name="StaticClass">appsrv.test.StaticTest</Provider>
|
||||
</Resource>
|
||||
<Resource>
|
||||
<Provider Name="StaticDirectory">www/static</Provider>
|
||||
</Resource>
|
||||
<Resource Default="index.hfrm">
|
||||
<Provider Name="StaticDirectory">form</Provider>
|
||||
</Resource>
|
||||
<Resource>
|
||||
<Provider Name="ReflectiveResource">appsrv.test.RTest</Provider>
|
||||
<Parameter Name="templates">rtest.templates</Parameter>
|
||||
</Resource>
|
||||
</Resource>
|
||||
</SharpWebApplication>
|
|
@ -0,0 +1,18 @@
|
|||
<?head?>
|
||||
<title>sharp-application-server Version [<?="0.0.0" ?>] / <?=pagetitle?></title>
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<?end?>
|
||||
|
||||
<section id="headbar">
|
||||
<div class="title"><a id="wawi" href="/">WaWi</a></div>
|
||||
<div class="title center" id="pagetitle"><?=pagetitle ?></div>
|
||||
</section>
|
||||
|
||||
<section id="pagearea">
|
||||
<?framed?>
|
||||
</section>
|
||||
|
||||
<section id="footbar">
|
||||
<div></div>
|
||||
<div id="pagetime"><?="----"?></div>
|
||||
</section>
|
|
@ -0,0 +1,45 @@
|
|||
<?xml version="1.0"?>
|
||||
<SharpForm Title="Index Form" Frame="/form/frame.hfrm">
|
||||
<Set Name="title">Index Page</Set>
|
||||
|
||||
<div><Var>title</Var></div>
|
||||
|
||||
<br/>
|
||||
<div>Object: <Var>o</Var></div>
|
||||
<div>Name: <Var>o.Name</Var></div>
|
||||
<div>Age: <Var>o.age</Var></div>
|
||||
|
||||
<Iterate Key="text" Expression="o.texts">
|
||||
<div>Ein Text: <Var>text</Var></div>
|
||||
</Iterate>
|
||||
|
||||
<h1>Boolean Conditionals Test</h1>
|
||||
|
||||
<If Expression="o.True"><h1>YES</h1></If>
|
||||
<If Expression="!o.True"><h1>!YES</h1></If>
|
||||
<If Expression="o.False"><h1>NO</h1></If>
|
||||
<If Expression="!o.False"><h1>!NO</h1></If>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>#</td>
|
||||
<td>Spalte 1</td>
|
||||
<td>Spalte 2</td>
|
||||
<td>Spalte 3</td>
|
||||
<td>Spalte 4</td>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<Iterate Key="n" Expression="/StaticTest/Loop">
|
||||
<tr>
|
||||
<td>#<Var Expression="n"/></td>
|
||||
<td><Var>/StaticTest/Random</Var></td>
|
||||
<td><Var>/StaticTest/Random</Var></td>
|
||||
<td><Var>/StaticTest/Random</Var></td>
|
||||
<td><Var>/StaticTest/Random</Var></td>
|
||||
</tr>
|
||||
</Iterate>
|
||||
</table>
|
||||
|
||||
</SharpForm>
|
|
@ -0,0 +1,44 @@
|
|||
<?frame /form/frame.hfrm?>
|
||||
<?set pagetitle "AClass Form"?>
|
||||
|
||||
<div><?=pagetitle?></div>
|
||||
|
||||
<br/>
|
||||
<div>Object: <?=o?></div>
|
||||
<div>Name: <?=o?></div>
|
||||
<div>Age: <?=o.PropB?></div>
|
||||
|
||||
<Iterate Key="text" Expression="o.texts">
|
||||
<div>Ein Text: <?="text"?></div>
|
||||
</Iterate>
|
||||
|
||||
<h1>Boolean Conditionals Test</h1>
|
||||
|
||||
<If Expression="o.True"><h1>YES</h1></If>
|
||||
<If Expression="!o.True"><h1>!YES</h1></If>
|
||||
<If Expression="o.False"><h1>NO</h1></If>
|
||||
<If Expression="!o.False"><h1>!NO</h1></If>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>#</td>
|
||||
<td>Spalte 1</td>
|
||||
<td>Spalte 2</td>
|
||||
<td>Spalte 3</td>
|
||||
<td>Spalte 4</td>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<Iterate Key="n" Expression="/StaticTest/Loop">
|
||||
<tr>
|
||||
<td>#<Var Expression="n"/></td>
|
||||
<td><Var>/StaticTest/Random</Var></td>
|
||||
<td><Var>/StaticTest/Random</Var></td>
|
||||
<td><Var>/StaticTest/Random</Var></td>
|
||||
<td><Var>/StaticTest/Random</Var></td>
|
||||
</tr>
|
||||
</Iterate>
|
||||
</table>
|
||||
|
||||
</SharpForm>
|
|
@ -0,0 +1,54 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>appsrv test page</title>
|
||||
|
||||
<script type="text/javascript" src="scripts.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
function lookup(o) {
|
||||
var pattern = o.value;
|
||||
if (o.lastpattern != pattern)
|
||||
{
|
||||
o.lastpattern = pattern;
|
||||
var url = o.getAttribute("lookup") + "?pattern=" + pattern;
|
||||
|
||||
callDATALIST(url, o);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.popup {
|
||||
display: block;
|
||||
position: relative;
|
||||
min-width: 240px;
|
||||
max-width: 100%;
|
||||
min-height: 80px;
|
||||
max-height: 240px;
|
||||
background-color: red;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>appsrv test page</h1>
|
||||
<p>A paragraph to see the file is displayed.</p>
|
||||
<form action="/StaticTest/Debug">
|
||||
Debug Message:
|
||||
<input type="text" name="debugMessage" value="" />
|
||||
<input type="submit" value="Senden" />
|
||||
</form>
|
||||
|
||||
<form action="/StaticTest/Add">
|
||||
Add two Numbers:
|
||||
<input type="text" name="a" value="1" />
|
||||
<input type="text" name="b" value="1" />
|
||||
<input type="submit" value="Calculate" />
|
||||
</form>
|
||||
|
||||
<form action="">
|
||||
JSON Lookup Test:
|
||||
<input type="text" name="pattern" list="pattern-ac-list" onkeyup="lookup(this);" lookup="/StaticTest/LookupList" autocomplete="off"/>
|
||||
<datalist id="pattern-ac-list"></datalist>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,62 @@
|
|||
function $(id){
|
||||
return document.getElementById(id);
|
||||
}
|
||||
|
||||
function pathSplit(path){
|
||||
var s = path.split("/");
|
||||
if (s[0] == ""){
|
||||
s.splice(0,1);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function pathJoin(path){
|
||||
return path.join("/");
|
||||
}
|
||||
|
||||
|
||||
function callURL(url, content, asyncReceiver = null, method = "GET"){
|
||||
var r = new XMLHttpRequest();
|
||||
var async = asyncReceiver != null;
|
||||
|
||||
r.open(method,url,async);
|
||||
if (async) {
|
||||
r.onload = asyncReceiver;
|
||||
}
|
||||
r.send(content);
|
||||
if (!async){
|
||||
return r.response;
|
||||
}
|
||||
}
|
||||
|
||||
function callJSON(url, parameters){
|
||||
var reply = callURL(url, JSON.stringify( parameters ), null, "POST" );
|
||||
return JSON.parse( reply );
|
||||
}
|
||||
|
||||
function loadHTML(container,source){
|
||||
container.innerHTML = source;
|
||||
var scripts = container.getElementsByTagName("script");
|
||||
for (var n=0;n<scripts.length;n++){
|
||||
eval.call(window,scripts[n].innerText);
|
||||
}
|
||||
}
|
||||
|
||||
function callDATALIST(url,o,parameters)
|
||||
{
|
||||
var reply = callJSON(url, parameters);
|
||||
|
||||
var datalist = $(o.name + "-ac-list");
|
||||
|
||||
datalist.innerHTML = "";
|
||||
|
||||
for (var n=0;n<reply.length;n++)
|
||||
{
|
||||
var opt = document.createElement("OPTION");
|
||||
opt.value = reply[n];
|
||||
datalist.appendChild(opt);
|
||||
}
|
||||
|
||||
return datalist;
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
function $(id){
|
||||
return document.getElementById(id);
|
||||
}
|
||||
|
||||
function pathSplit(path){
|
||||
var s = path.split("/");
|
||||
if (s[0] == ""){
|
||||
s.splice(0,1);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function pathJoin(path){
|
||||
return path.join("/");
|
||||
}
|
||||
|
||||
|
||||
function callURL(url, content, asyncReceiver = null, method = "GET"){
|
||||
var r = new XMLHttpRequest();
|
||||
var async = asyncReceiver != null;
|
||||
|
||||
r.open(method,url,async);
|
||||
if (async) {
|
||||
r.onload = asyncReceiver;
|
||||
}
|
||||
r.send(content);
|
||||
if (!async){
|
||||
return r.response;
|
||||
}
|
||||
}
|
||||
|
||||
function callJSON(url, parameters){
|
||||
var reply = callURL(url, JSON.stringify( parameters ), null, "POST" );
|
||||
return JSON.parse( reply );
|
||||
}
|
||||
|
||||
function loadHTML(container,source){
|
||||
container.innerHTML = source;
|
||||
var scripts = container.getElementsByTagName("script");
|
||||
for (var n=0;n<scripts.length;n++){
|
||||
eval.call(window,scripts[n].innerText);
|
||||
}
|
||||
}
|
||||
|
||||
function callDATALIST(url,o,parameters)
|
||||
{
|
||||
var reply = callJSON(url, parameters);
|
||||
|
||||
var datalist = $(o.name + "-ac-list");
|
||||
|
||||
datalist.innerHTML = "";
|
||||
|
||||
for (var n=0;n<reply.length;n++)
|
||||
{
|
||||
var opt = document.createElement("OPTION");
|
||||
opt.value = reply[n];
|
||||
datalist.appendChild(opt);
|
||||
}
|
||||
|
||||
return datalist;
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>appsrv test page</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>appsrv test page</h1>
|
||||
<p>A paragraph to see the file is displayed.</p>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue