195 lines
7.5 KiB
C#
195 lines
7.5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Concurrent;
|
|
using System.IO;
|
|
using System.Net;
|
|
using System.Net.Security;
|
|
using System.Net.Sockets;
|
|
using System.Security.Authentication;
|
|
using System.Security.Cryptography.X509Certificates;
|
|
|
|
namespace OSHttpServer
|
|
{
|
|
/// <summary>
|
|
/// Used to create and reuse contexts.
|
|
/// </summary>
|
|
public class HttpContextFactory : IHttpContextFactory
|
|
{
|
|
private readonly ConcurrentDictionary<int, HttpClientContext> m_activeContexts = new ConcurrentDictionary<int, HttpClientContext>();
|
|
private readonly IRequestParserFactory m_parserfactory;
|
|
private readonly ILogWriter m_logWriter;
|
|
|
|
/// <summary>
|
|
/// A request have been received from one of the contexts.
|
|
/// </summary>
|
|
public event EventHandler<RequestEventArgs> RequestReceived;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="HttpContextFactory"/> class.
|
|
/// </summary>
|
|
/// <param name="writer">The writer.</param>
|
|
/// <param name="bufferSize">Amount of bytes to read from the incoming socket stream.</param>
|
|
/// <param name="factory">Used to create a request parser.</param>
|
|
public HttpContextFactory(ILogWriter writer, IRequestParserFactory factory)
|
|
{
|
|
m_logWriter = writer;
|
|
m_parserfactory = factory;
|
|
ContextTimeoutManager.Start();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a new context.
|
|
/// </summary>
|
|
/// <param name="isSecured">true if socket is running HTTPS.</param>
|
|
/// <param name="endPoint">Client that connected</param>
|
|
/// <param name="stream">Network/SSL stream.</param>
|
|
/// <returns>A context.</returns>
|
|
protected HttpClientContext CreateContext(bool isSecured, IPEndPoint endPoint, Stream stream, Socket sock)
|
|
{
|
|
HttpClientContext context;
|
|
|
|
context = CreateNewContext(isSecured, endPoint, stream, sock);
|
|
context.Disconnected += OnFreeContext;
|
|
context.RequestReceived += OnRequestReceived;
|
|
|
|
context.Stream = stream;
|
|
context.IsSecured = isSecured;
|
|
context.RemotePort = endPoint.Port.ToString();
|
|
context.RemoteAddress = endPoint.Address.ToString();
|
|
ContextTimeoutManager.StartMonitoringContext(context);
|
|
m_activeContexts[context.contextID] = context;
|
|
context.Start();
|
|
return context;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a new context.
|
|
/// </summary>
|
|
/// <param name="isSecured">true if HTTPS is used.</param>
|
|
/// <param name="endPoint">Remote client</param>
|
|
/// <param name="stream">Network stream, <see cref="HttpClientContext"/></param>
|
|
/// <returns>A new context (always).</returns>
|
|
protected virtual HttpClientContext CreateNewContext(bool isSecured, IPEndPoint endPoint, Stream stream, Socket sock)
|
|
{
|
|
return new HttpClientContext(isSecured, endPoint, stream, m_parserfactory, sock);
|
|
}
|
|
|
|
private void OnRequestReceived(object sender, RequestEventArgs e)
|
|
{
|
|
RequestReceived?.Invoke(sender, e);
|
|
}
|
|
|
|
private void OnFreeContext(object sender, DisconnectedEventArgs e)
|
|
{
|
|
var imp = sender as HttpClientContext;
|
|
if (imp == null || imp.contextID < 0)
|
|
return;
|
|
|
|
m_activeContexts.TryRemove(imp.contextID, out HttpClientContext dummy);
|
|
|
|
imp.Close();
|
|
}
|
|
|
|
|
|
#region IHttpContextFactory Members
|
|
|
|
/// <summary>
|
|
/// Create a secure <see cref="IHttpClientContext"/>.
|
|
/// </summary>
|
|
/// <param name="socket">Client socket (accepted by the <see cref="OSHttpListener"/>).</param>
|
|
/// <param name="certificate">HTTPS certificate to use.</param>
|
|
/// <param name="protocol">Kind of HTTPS protocol. Usually TLS or SSL.</param>
|
|
/// <returns>
|
|
/// A created <see cref="IHttpClientContext"/>.
|
|
/// </returns>
|
|
public IHttpClientContext CreateSecureContext(Socket socket, X509Certificate certificate,
|
|
SslProtocols protocol, RemoteCertificateValidationCallback _clientCallback = null)
|
|
{
|
|
socket.NoDelay = true;
|
|
var networkStream = new NetworkStream(socket, true);
|
|
var remoteEndPoint = (IPEndPoint)socket.RemoteEndPoint;
|
|
|
|
SslStream sslStream = null;
|
|
try
|
|
{
|
|
if (_clientCallback == null)
|
|
{
|
|
sslStream = new SslStream(networkStream, false);
|
|
sslStream.AuthenticateAsServer(certificate, false, protocol, false);
|
|
}
|
|
else
|
|
{
|
|
sslStream = new SslStream(networkStream, false,
|
|
new RemoteCertificateValidationCallback(_clientCallback));
|
|
sslStream.AuthenticateAsServer(certificate, true, protocol, false);
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
m_logWriter.Write(this, LogPrio.Error, e.Message);
|
|
sslStream.Close();
|
|
return null;
|
|
}
|
|
|
|
return CreateContext(true, remoteEndPoint, sslStream, socket);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a <see cref="IHttpClientContext"/> that handles a connected client.
|
|
/// </summary>
|
|
/// <param name="socket">Client socket (accepted by the <see cref="OSHttpListener"/>).</param>
|
|
/// <returns>
|
|
/// A creates <see cref="IHttpClientContext"/>.
|
|
/// </returns>
|
|
public IHttpClientContext CreateContext(Socket socket)
|
|
{
|
|
socket.NoDelay = true;
|
|
var networkStream = new NetworkStream(socket, true);
|
|
var remoteEndPoint = (IPEndPoint)socket.RemoteEndPoint;
|
|
return CreateContext(false, remoteEndPoint, networkStream, socket);
|
|
}
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// Server is shutting down so shut down the factory
|
|
/// </summary>
|
|
public void Shutdown()
|
|
{
|
|
ContextTimeoutManager.Stop();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Used to create <see cref="IHttpClientContext"/>es.
|
|
/// </summary>
|
|
public interface IHttpContextFactory
|
|
{
|
|
/// <summary>
|
|
/// Creates a <see cref="IHttpClientContext"/> that handles a connected client.
|
|
/// </summary>
|
|
/// <param name="socket">Client socket (accepted by the <see cref="OSHttpListener"/>).</param>
|
|
/// <returns>A creates <see cref="IHttpClientContext"/>.</returns>
|
|
IHttpClientContext CreateContext(Socket socket);
|
|
|
|
/// <summary>
|
|
/// Create a secure <see cref="IHttpClientContext"/>.
|
|
/// </summary>
|
|
/// <param name="socket">Client socket (accepted by the <see cref="OSHttpListener"/>).</param>
|
|
/// <param name="certificate">HTTPS certificate to use.</param>
|
|
/// <param name="protocol">Kind of HTTPS protocol. Usually TLS or SSL.</param>
|
|
/// <returns>A created <see cref="IHttpClientContext"/>.</returns>
|
|
IHttpClientContext CreateSecureContext(Socket socket, X509Certificate certificate,
|
|
SslProtocols protocol, RemoteCertificateValidationCallback _clientCallback = null);
|
|
|
|
/// <summary>
|
|
/// A request have been received from one of the contexts.
|
|
/// </summary>
|
|
event EventHandler<RequestEventArgs> RequestReceived;
|
|
|
|
/// <summary>
|
|
/// Server is shutting down so shut down the factory
|
|
/// </summary>
|
|
void Shutdown();
|
|
}
|
|
} |