diff --git a/src/GlobalSuppressions.cs b/src/GlobalSuppressions.cs index 2a338a8..927773a 100644 Binary files a/src/GlobalSuppressions.cs and b/src/GlobalSuppressions.cs differ diff --git a/src/Infrastructure/IOperatingSystem.cs b/src/Infrastructure/IOperatingSystem.cs index 8e14318..f9f87b5 100644 --- a/src/Infrastructure/IOperatingSystem.cs +++ b/src/Infrastructure/IOperatingSystem.cs @@ -8,6 +8,7 @@ public interface IOperatingSystem IEnvironment Environment { get; } IFileFacade File { get; } IMemoryMappedFileFactory MemoryMappedFiles { get; } + INetFactory Net { get; } IProcessLocator ProcessLocator { get; } IProcessStarter ProcessStarter { get; } IRegistry Registry { get; } diff --git a/src/Net/HttpListenerContextWrapper.cs b/src/Net/HttpListenerContextWrapper.cs new file mode 100644 index 0000000..7325ee8 --- /dev/null +++ b/src/Net/HttpListenerContextWrapper.cs @@ -0,0 +1,47 @@ +using System; +using System.Net; +using System.Net.WebSockets; +using System.Security.Principal; +using System.Threading.Tasks; + +namespace Rothko +{ + public class HttpListenerContextWrapper : IHttpListenerContext + { + readonly HttpListenerContext inner; + + public HttpListenerContextWrapper(HttpListenerContext inner) + { + this.inner = inner; + Request = new HttpListenerRequestWrapper(inner.Request); + Response = new HttpListenerResponseWrapper(inner.Response); + } + + public IHttpListenerRequest Request { get; } + + public IHttpListenerResponse Response { get; } + + public IPrincipal User => inner.User; + + public Task AcceptWebSocketAsync(string subProtocol) + { + return inner.AcceptWebSocketAsync(subProtocol); + } + + public Task AcceptWebSocketAsync(string subProtocol, TimeSpan keepAliveInterval) + { + return inner.AcceptWebSocketAsync(subProtocol, keepAliveInterval); + } + + public Task AcceptWebSocketAsync(string subProtocol, int receiveBufferSize, TimeSpan keepAliveInterval) + { + return inner.AcceptWebSocketAsync(subProtocol, receiveBufferSize, keepAliveInterval); + } + + public Task AcceptWebSocketAsync(string subProtocol, int receiveBufferSize, TimeSpan keepAliveInterval, + ArraySegment internalBuffer) + { + return inner.AcceptWebSocketAsync(subProtocol, receiveBufferSize, keepAliveInterval, internalBuffer); + } + } +} diff --git a/src/Net/HttpListenerRequestWrapper.cs b/src/Net/HttpListenerRequestWrapper.cs new file mode 100644 index 0000000..7fdb9a0 --- /dev/null +++ b/src/Net/HttpListenerRequestWrapper.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Specialized; +using System.IO; +using System.Net; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading.Tasks; + +namespace Rothko +{ + public class HttpListenerRequestWrapper : IHttpListenerRequest + { + readonly HttpListenerRequest inner; + + public HttpListenerRequestWrapper(HttpListenerRequest inner) + { + this.inner = inner; + } + + public string[] AcceptTypes => inner.AcceptTypes; + + public int ClientCertificateError => inner.ClientCertificateError; + + public Encoding ContentEncoding => inner.ContentEncoding; + + public long ContentLength64 => inner.ContentLength64; + + public string ContentType => inner.ContentType; + + public CookieCollection Cookies => inner.Cookies; + + public bool HasEntityBody => inner.HasEntityBody; + + public NameValueCollection Headers => inner.Headers; + + public string HttpMethod => inner.HttpMethod; + + public Stream InputStream => inner.InputStream; + + public bool IsAuthenticated => inner.IsAuthenticated; + + public bool IsLocal => inner.IsLocal; + + public bool IsSecureConnection => inner.IsSecureConnection; + + public bool IsWebSocketRequest => inner.IsWebSocketRequest; + + public bool KeepAlive => inner.KeepAlive; + + public IPEndPoint LocalEndPoint => inner.LocalEndPoint; + + public Version ProtocolVersion => inner.ProtocolVersion; + + public NameValueCollection QueryString => inner.QueryString; + + public string RawUrl => inner.RawUrl; + + public IPEndPoint RemoteEndPoint => inner.RemoteEndPoint; + + public Guid RequestTraceIdentifier => inner.RequestTraceIdentifier; + + public string ServiceName => inner.ServiceName; + + public TransportContext TransportContext => inner.TransportContext; + + public Uri Url => inner.Url; + + public Uri UrlReferrer => inner.UrlReferrer; + + public string UserAgent => inner.UserAgent; + + public string UserHostAddress => inner.UserHostAddress; + + public string UserHostName => inner.UserHostName; + + public string[] UserLanguages => inner.UserLanguages; + + public IAsyncResult BeginGetClientCertificate(AsyncCallback requestCallback, object state) + { + return inner.BeginGetClientCertificate(requestCallback, state); + } + + public X509Certificate2 EndGetClientCertificate(IAsyncResult asyncResult) + { + return inner.EndGetClientCertificate(asyncResult); + } + + public X509Certificate2 GetClientCertificate() + { + return inner.GetClientCertificate(); + } + + public Task GetClientCertificateAsync() + { + return inner.GetClientCertificateAsync(); + } + } +} diff --git a/src/Net/HttpListenerResponseWrapper.cs b/src/Net/HttpListenerResponseWrapper.cs new file mode 100644 index 0000000..51bae95 --- /dev/null +++ b/src/Net/HttpListenerResponseWrapper.cs @@ -0,0 +1,122 @@ +using System; +using System.IO; +using System.Net; +using System.Text; + +namespace Rothko +{ + public class HttpListenerResponseWrapper : IHttpListenerResponse + { + private HttpListenerResponse inner; + + public HttpListenerResponseWrapper(HttpListenerResponse inner) + { + this.inner = inner; + } + + ~HttpListenerResponseWrapper() + { + Dispose(false); + } + + void IDisposable.Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + public Encoding ContentEncoding + { + get { return inner.ContentEncoding; } + set { inner.ContentEncoding = value; } + } + + public long ContentLength64 + { + get { return inner.ContentLength64; } + set { inner.ContentLength64 = value; } + } + + public string ContentType + { + get { return inner.ContentType; } + set { inner.ContentType = value; } + } + + public CookieCollection Cookies + { + get { return inner.Cookies; } + set { inner.Cookies = value; } + } + + public WebHeaderCollection Headers + { + get { return inner.Headers; } + set { inner.Headers = value; } + } + + public bool KeepAlive + { + get { return inner.KeepAlive; } + set { inner.KeepAlive = value; } + } + + public Stream OutputStream => inner.OutputStream; + + public Version ProtocolVersion + { + get { return inner.ProtocolVersion; } + set { inner.ProtocolVersion = value; } + } + + public string RedirectLocation + { + get { return inner.RedirectLocation; } + set { inner.RedirectLocation = value; } + } + + public bool SendChunked + { + get { return inner.SendChunked; } + set { inner.SendChunked = value; } + } + + public int StatusCode + { + get { return inner.StatusCode; } + set { inner.StatusCode = value; } + } + + public string StatusDescription + { + get { return inner.StatusDescription; } + set { inner.StatusDescription = value; } + } + + public void Abort() => inner.Abort(); + + public void AddHeader(string name, string value) => inner.AddHeader(name, value); + + public void AppendCookie(Cookie cookie) => inner.AppendCookie(cookie); + + public void AppendHeader(string name, string value) => inner.AppendHeader(name, value); + + public void Close() => inner.Close(); + + public void Close(byte[] responseEntity, bool willBlock) => inner.Close(responseEntity, willBlock); + + public void CopyFrom(HttpListenerResponse templateResponse) => inner.CopyFrom(templateResponse); + + public void Redirect(string url) => inner.Redirect(url); + + public void SetCookie(Cookie cookie) => inner.SetCookie(cookie); + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + ((IDisposable)inner).Dispose(); + } + } + } +} diff --git a/src/Net/HttpListenerWrapper.cs b/src/Net/HttpListenerWrapper.cs new file mode 100644 index 0000000..7920290 --- /dev/null +++ b/src/Net/HttpListenerWrapper.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Security.Authentication.ExtendedProtection; +using System.Threading.Tasks; + +namespace Rothko +{ + public sealed class HttpListenerWrapper : IHttpListener + { + readonly HttpListener inner; + + public HttpListenerWrapper(HttpListener inner) + { + this.inner = inner; + } + + public AuthenticationSchemes AuthenticationSchemes + { + get { return inner.AuthenticationSchemes; } + set { inner.AuthenticationSchemes = value; } + } + + public AuthenticationSchemeSelector AuthenticationSchemeSelectorDelegate + { + get { return inner.AuthenticationSchemeSelectorDelegate; } + set { inner.AuthenticationSchemeSelectorDelegate = value; } + } + + public ServiceNameCollection DefaultServiceNames => inner.DefaultServiceNames; + + public ExtendedProtectionPolicy ExtendedProtectionPolicy + { + get { return inner.ExtendedProtectionPolicy; } + set { inner.ExtendedProtectionPolicy = value; } + } + + public HttpListener.ExtendedProtectionSelector ExtendedProtectionSelectorDelegate + { + get { return inner.ExtendedProtectionSelectorDelegate; } + set { inner.ExtendedProtectionSelectorDelegate = value; } + } + + public bool IgnoreWriteExceptions + { + get { return inner.IgnoreWriteExceptions; } + set { inner.IgnoreWriteExceptions = value; } + } + + public bool IsListening => inner.IsListening; + + public ICollection Prefixes => inner.Prefixes; + + public string Realm + { + get { return inner.Realm; } + set { inner.Realm = value; } + } + + public HttpListenerTimeoutManager TimeoutManager => inner.TimeoutManager; + + public bool UnsafeConnectionNtlmAuthentication + { + get { return inner.UnsafeConnectionNtlmAuthentication; } + set { inner.UnsafeConnectionNtlmAuthentication = value; } + } + + public void Abort() => inner.Abort(); + public IAsyncResult BeginGetContext(AsyncCallback callback, object state) => inner.BeginGetContext(callback, state); + public void Close() => inner.Close(); + + public IHttpListenerContext EndGetContext(IAsyncResult asyncResult) + { + return new HttpListenerContextWrapper(inner.EndGetContext(asyncResult)); + } + + public IHttpListenerContext GetContext() + { + return new HttpListenerContextWrapper(inner.GetContext()); + } + + public async Task GetContextAsync() + { + return new HttpListenerContextWrapper(await inner.GetContextAsync()); + } + + public void Start() => inner.Start(); + public void Stop() => inner.Stop(); + } +} diff --git a/src/Net/IHttpListener.cs b/src/Net/IHttpListener.cs new file mode 100644 index 0000000..057542a --- /dev/null +++ b/src/Net/IHttpListener.cs @@ -0,0 +1,297 @@ +using System; +using System.Threading.Tasks; +using System.Net; +using System.Security.Authentication.ExtendedProtection; +using System.Collections.Generic; + +namespace Rothko +{ + /// + /// Provides a simple, programmatically controlled HTTP protocol listener. + /// + public interface IHttpListener + { + /// + /// Gets or sets the scheme used to authenticate clients. + /// + /// + /// A bitwise combination of System.Net.AuthenticationSchemes enumeration values + /// that indicates how clients are to be authenticated. The default value is + /// . + /// + /// + /// This object has been closed. + /// + AuthenticationSchemes AuthenticationSchemes { get; set; } + + /// + /// Gets or sets the delegate called to determine the protocol used to authenticate + /// clients. + /// + /// + /// An System.Net.AuthenticationSchemeSelector delegate that invokes the method used + /// to select an authentication protocol. The default value is null. + /// + /// + /// This object has been closed. + /// + AuthenticationSchemeSelector AuthenticationSchemeSelectorDelegate { get; set; } + + /// + /// Gets a default list of Service Provider Names (SPNs) as determined by registered + /// prefixes. + /// + /// + /// A System.Security.Authentication.ExtendedProtection.ServiceNameCollection that + /// contains a list of SPNs. + /// + ServiceNameCollection DefaultServiceNames { get; } + + /// + /// Get or set the System.Security.Authentication.ExtendedProtection.ExtendedProtectionPolicy + /// to use for extended protection for a session. + /// + /// + /// A System.Security.Authentication.ExtendedProtection.ExtendedProtectionPolicy + /// that specifies the policy to use for extended protection. + /// + /// + /// An attempt was made to set the System.Net.HttpListener.ExtendedProtectionPolicy + /// property, but the System.Security.Authentication.ExtendedProtection.ExtendedProtectionPolicy.CustomChannelBinding + /// property was not null. + /// + /// + /// An attempt was made to set the System.Net.HttpListener.ExtendedProtectionPolicy + /// property to null. + /// + /// + /// An attempt was made to set the System.Net.HttpListener.ExtendedProtectionPolicy + /// property after the System.Net.HttpListener.Start method was already called. + /// + /// + /// This object has been closed. + /// + /// + /// The System.Security.Authentication.ExtendedProtection.ExtendedProtectionPolicy.PolicyEnforcement + /// property was set to System.Security.Authentication.ExtendedProtection.PolicyEnforcement.Always + /// on a platform that does not support extended protection. + /// + ExtendedProtectionPolicy ExtendedProtectionPolicy { get; set; } + + /// + /// Get or set the delegate called to determine the System.Security.Authentication.ExtendedProtection.ExtendedProtectionPolicy + /// to use for each request. + /// + /// + /// A System.Security.Authentication.ExtendedProtection.ExtendedProtectionPolicy + /// that specifies the policy to use for extended protection. + /// + /// + /// An attempt was made to set the System.Net.HttpListener.ExtendedProtectionSelectorDelegate + /// property, but the System.Security.Authentication.ExtendedProtection.ExtendedProtectionPolicy.CustomChannelBinding + /// property must be null. + /// + /// + /// An attempt was made to set the System.Net.HttpListener.ExtendedProtectionSelectorDelegate + /// property to null. + /// + /// + /// An attempt was made to set the System.Net.HttpListener.ExtendedProtectionSelectorDelegate + /// property after the System.Net.HttpListener.Start method was already called. + /// + /// + /// This object has been closed. + /// + /// + /// An attempt was made to set the System.Net.HttpListener.ExtendedProtectionSelectorDelegate + /// property on a platform that does not support extended protection. + /// + HttpListener.ExtendedProtectionSelector ExtendedProtectionSelectorDelegate { get; set; } + + /// + /// Gets or sets a System.Boolean value that specifies whether your application receives + /// exceptions that occur when an System.Net.HttpListener sends the response to the + /// client. + /// + /// + /// true if this System.Net.HttpListener should not return exceptions that occur + /// when sending the response to the client; otherwise false. The default value is + /// false. + /// + /// + /// This object has been closed. + /// + bool IgnoreWriteExceptions { get; set; } + + /// + /// Gets a value that indicates whether System.Net.HttpListener has been started. + /// + /// + /// true if the System.Net.HttpListener was started; otherwise, false. + /// + bool IsListening { get; } + + /// + /// Gets the Uniform Resource Identifier (URI) prefixes handled by this System.Net.HttpListener + /// object. + /// + /// + /// An System.Net.HttpListenerPrefixCollection that contains the URI prefixes that + /// this System.Net.HttpListener object is configured to handle. + /// + /// + /// This object has been closed. + /// + ICollection Prefixes { get; } + + /// + /// Gets or sets the realm, or resource partition, associated with this System.Net.HttpListener + /// object. + /// + /// + /// A System.String value that contains the name of the realm associated with the + /// System.Net.HttpListener object. + /// + /// + /// This object has been closed. + /// + string Realm { get; set; } + + /// + /// The timeout manager for this System.Net.HttpListener instance. + /// + /// + /// Returns System.Net.HttpListenerTimeoutManager.The timeout manager for this System.Net.HttpListener + /// instance. + /// + HttpListenerTimeoutManager TimeoutManager { get; } + + /// + /// Gets or sets a System.Boolean value that controls whether, when NTLM is used, + /// additional requests using the same Transmission Control Protocol (TCP) connection + /// are required to authenticate. + /// + /// + /// true if the System.Security.Principal.IIdentity of the first request will be + /// used for subsequent requests on the same connection; otherwise, false. The default + /// value is false. + /// + /// + /// This object has been closed. + /// + bool UnsafeConnectionNtlmAuthentication { get; set; } + + /// + /// Shuts down the System.Net.HttpListener object immediately, discarding all currently + /// queued requests. + /// + void Abort(); + + /// + /// Begins asynchronously retrieving an incoming request. + /// + /// + /// An System.AsyncCallback delegate that references the method to invoke when a + /// client request is available. + /// + /// + /// A user-defined object that contains information about the operation. This object + /// is passed to the callback delegate when the operation completes. + /// + /// + /// An System.IAsyncResult object that indicates the status of the asynchronous operation. + /// + /// + /// A Win32 function call failed. Check the exception's + /// property to determine the cause of the exception. + /// + /// + /// This object has not been started or is currently stopped. + /// + /// + /// This object has been closed. + /// + IAsyncResult BeginGetContext(AsyncCallback callback, object state); + + /// + /// Shuts down the System.Net.HttpListener. + /// + void Close(); + + /// + /// Completes an asynchronous operation to retrieve an incoming client request. + /// + /// + /// An System.IAsyncResult object that was obtained when the asynchronous operation + /// was started. + /// + /// + /// An System.Net.HttpListenerContext object that represents the client request. + /// + /// + /// asyncResult was not obtained by calling the + /// + /// method. + /// + /// + /// asyncResult is null. + /// + /// + /// The System.Net.HttpListener.EndGetContext(System.IAsyncResult) method was already + /// called for the specified asyncResult object. + /// + /// + /// This object has been closed. + /// + IHttpListenerContext EndGetContext(IAsyncResult asyncResult); + + /// + /// Waits for an incoming request and returns when one is received. + /// + /// + /// An System.Net.HttpListenerContext object that represents a client request. + /// + /// + /// A Win32 function call failed. Check the exception's + /// property to determine the cause of the exception. + /// + /// + /// This object has not been started or is currently stopped.-or-The System.Net.HttpListener + /// does not have any Uniform Resource Identifier (URI) prefixes to respond to. See + /// + /// + /// This object has been closed. + /// + IHttpListenerContext GetContext(); + + /// + /// Waits for an incoming request as an asynchronous operation. + /// + /// + /// Returns System.Threading.Tasks.Task`1.The task object representing the asynchronous + /// operation. The System.Threading.Tasks.Task`1.Result property on the task object + /// returns an System.Net.HttpListenerContext object that represents a client request. + /// + Task GetContextAsync(); + + /// + /// Allows this instance to receive incoming requests. + /// + /// + /// A Win32 function call failed. Check the exception's + /// property to determine the cause of the exception. + /// + /// + /// This object has been closed. + /// + void Start(); + + /// + /// Causes this instance to stop receiving incoming requests. + /// + /// + /// This object has been closed. + /// + void Stop(); + } +} diff --git a/src/Net/IHttpListenerContext.cs b/src/Net/IHttpListenerContext.cs new file mode 100644 index 0000000..824eb4c --- /dev/null +++ b/src/Net/IHttpListenerContext.cs @@ -0,0 +1,158 @@ +using System; +using System.ComponentModel; +using System.Net.WebSockets; +using System.Security.Principal; +using System.Threading.Tasks; + +namespace Rothko +{ + /// + /// Provides access to the request and response objects used by the interface. + /// + public interface IHttpListenerContext + { + /// + /// Gets the that represents a client's request for + /// a resource. + /// + /// + /// An object that represents the client request. + /// + IHttpListenerRequest Request { get; } + + /// + /// Gets the object that will be sent to the client + /// in response to the client's request. + /// + /// + /// An object used to send a response back to the client. + /// + IHttpListenerResponse Response { get; } + + /// + /// Gets an object used to obtain identity, authentication information, and security + /// roles for the client whose request is represented by this System.Net.HttpListenerContext + /// object. + /// + /// + /// An object that describes the client, or null if the + /// that supplied this + /// does not require authentication. + /// + IPrincipal User { get; } + + /// + /// Accept a WebSocket connection as an asynchronous operation. + /// + /// + /// The supported WebSocket sub-protocol. + /// + /// + /// Returns System.Threading.Tasks.Task`1.The task object representing the asynchronous + /// operation. The System.Threading.Tasks.Task`1.Result property on the task object + /// returns an System.Net.WebSockets.HttpListenerWebSocketContext object. + /// + /// + /// is an empty string-or- + /// contains illegal characters. + /// + /// + /// An error occurred when sending the response to complete the WebSocket handshake. + /// + Task AcceptWebSocketAsync(string subProtocol); + + /// + /// Accept a WebSocket connection specifying the supported WebSocket sub-protocol + /// and WebSocket keep-alive interval as an asynchronous operation. + /// + /// + /// The supported WebSocket sub-protocol. + /// + /// + /// The WebSocket protocol keep-alive interval in milliseconds. + /// + /// + /// Returns System.Threading.Tasks.Task`1.The task object representing the asynchronous + /// operation. The System.Threading.Tasks.Task`1.Result property on the task object + /// returns an System.Net.WebSockets.HttpListenerWebSocketContext object. + /// + /// + /// is an empty string-or- + /// contains illegal characters. + /// + /// + /// is too small. + /// + /// + /// An error occurred when sending the response to complete the WebSocket handshake. + /// + Task AcceptWebSocketAsync(string subProtocol, TimeSpan keepAliveInterval); + + /// + /// Accept a WebSocket connection specifying the supported WebSocket sub-protocol, + /// receive buffer size, and WebSocket keep-alive interval as an asynchronous operation. + /// + /// + /// The supported WebSocket sub-protocol. + /// + /// + /// The receive buffer size in bytes. + /// + /// + /// The WebSocket protocol keep-alive interval in milliseconds. + /// + /// + /// Returns System.Threading.Tasks.Task`1.The task object representing the asynchronous + /// operation. The System.Threading.Tasks.Task`1.Result property on the task object + /// returns an System.Net.WebSockets.HttpListenerWebSocketContext object. + /// + /// + /// is an empty string-or- + /// contains illegal characters. + /// + /// + /// is too small.-or- receiveBufferSize is less than 16 bytes-or- + /// is greater than 64K bytes. + /// + /// + /// An error occurred when sending the response to complete the WebSocket handshake. + /// + Task AcceptWebSocketAsync(string subProtocol, int receiveBufferSize, TimeSpan keepAliveInterval); + + /// + /// Accept a WebSocket connection specifying the supported WebSocket sub-protocol, + /// receive buffer size, WebSocket keep-alive interval, and the internal buffer as + /// an asynchronous operation. + /// + /// + /// The supported WebSocket sub-protocol. + /// + /// + /// The receive buffer size in bytes. + /// + /// + /// The WebSocket protocol keep-alive interval in milliseconds. + /// + /// + /// An internal buffer to use for this operation. + /// + /// + /// Returns System.Threading.Tasks.Task`1.The task object representing the asynchronous + /// operation. The System.Threading.Tasks.Task`1.Result property on the task object + /// returns an System.Net.WebSockets.HttpListenerWebSocketContext object. + /// + /// + /// is an empty string-or- + /// contains illegal characters. + /// + /// + /// is too small.-or- receiveBufferSize is less than 16 bytes-or- + /// is greater than 64K bytes. + /// + /// + /// An error occurred when sending the response to complete the WebSocket handshake. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + Task AcceptWebSocketAsync(string subProtocol, int receiveBufferSize, TimeSpan keepAliveInterval, ArraySegment internalBuffer); + } +} diff --git a/src/Net/IHttpListenerRequest.cs b/src/Net/IHttpListenerRequest.cs new file mode 100644 index 0000000..6150508 --- /dev/null +++ b/src/Net/IHttpListenerRequest.cs @@ -0,0 +1,333 @@ +using System; +using System.Collections.Specialized; +using System.IO; +using System.Net; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading.Tasks; + +namespace Rothko +{ + /// + /// Describes an incoming HTTP request to an System.Net.HttpListener object. + /// + public interface IHttpListenerRequest + { + /// + /// Gets the MIME types accepted by the client. + /// + /// + /// A array that contains the type names specified in the request's + /// Accept header or null if the client request did not include an Accept header. + /// + string[] AcceptTypes { get; } + + /// + /// Gets an error code that identifies a problem with the + /// provided by the client. + /// + /// + /// An System.Int32 value that contains a Windows error code. + /// + /// + /// The client certificate has not been initialized yet by a call to the + /// or methods -or - The operation + /// is still in progress. + /// + int ClientCertificateError { get; } + + /// + /// Gets the content encoding that can be used with data sent with the request + /// + /// + /// An object suitable for use with the data in the + /// property. + /// + Encoding ContentEncoding { get; } + + /// + /// Gets the length of the body data included in the request. + /// + /// + /// The value from the request's Content-Length header. This value is -1 if the content + /// length is not known. + /// + long ContentLength64 { get; } + + /// + /// Gets the MIME type of the body data included in the request. + /// + /// + /// A that contains the text of the request's Content-Type header. + /// + string ContentType { get; } + + /// + /// Gets the cookies sent with the request. + /// + /// + /// A that contains cookies that accompany the request. + /// This property returns an empty collection if the request does not contain cookies. + /// + CookieCollection Cookies { get; } + + /// + /// Gets a value that indicates whether the request has associated + /// body data. + /// + /// + /// true if the request has associated body data; otherwise, false. + /// + bool HasEntityBody { get; } + + /// + /// Gets the collection of header name/value pairs sent in the request. + /// + /// + /// A that contains the HTTP headers included in the + /// request. + /// + NameValueCollection Headers { get; } + + /// + /// Gets the HTTP method specified by the client. + /// + /// + /// A that contains the method used in the request. + /// + string HttpMethod { get; } + + /// + /// Gets a stream that contains the body data sent by the client. + /// + /// + /// A readable object that contains the bytes sent by the client + /// in the body of the request. This property returns if no + /// data is sent with the request. + /// + Stream InputStream { get; } + + /// + /// Gets a value that indicates whether the client sending this request + /// is authenticated. + /// + /// + /// true if the client was authenticated; otherwise, false. + /// + bool IsAuthenticated { get; } + + /// + /// Gets a value that indicates whether the request is sent from the + /// local computer. + /// + /// + /// true if the request originated on the same computer as the + /// object that provided the request; otherwise, false. + /// + bool IsLocal { get; } + + /// + /// Gets a value that indicates whether the TCP connection used to + /// send the request is using the Secure Sockets Layer (SSL) protocol. + /// + /// + /// true if the TCP connection is using SSL; otherwise, false. + /// + bool IsSecureConnection { get; } + + /// + /// Gets a value that indicates whether the TCP connection was a WebSocket + /// request. + /// + /// + /// Returns true if the TCP connection is a WebSocket request; otherwise, false. + /// + bool IsWebSocketRequest { get; } + + /// + /// Gets a value that indicates whether the client requests a persistent + /// connection. + /// + /// + /// true if the connection should be kept open; otherwise, false. + /// + bool KeepAlive { get; } + + /// + /// Get the server IP address and port number to which the request is directed. + /// + /// + /// An that represents the IP address that the request is sent + /// to. + /// + IPEndPoint LocalEndPoint { get; } + + /// + /// Gets the HTTP version used by the requesting client. + /// + /// + /// A that identifies the client's version of HTTP. + /// + Version ProtocolVersion { get; } + + /// + /// Gets the query string included in the request. + /// + /// + /// A object that contains the query data included in + /// the request . + /// + NameValueCollection QueryString { get; } + + /// + /// Gets the URL information (without the host and port) requested by the client. + /// + /// + /// A that contains the raw URL for this request. + /// + string RawUrl { get; } + + /// + /// Gets the client IP address and port number from which the request originated. + /// + /// + /// An that represents the IP address and port number from + /// which the request originated. + /// + IPEndPoint RemoteEndPoint { get; } + + /// + /// Gets the request identifier of the incoming HTTP request. + /// + /// + /// A object that contains the identifier of the HTTP request. + /// + Guid RequestTraceIdentifier { get; } + + /// + /// Gets the Service Provider Name (SPN) that the client sent on the request. + /// + /// + /// A that contains the SPN the client sent on the request. + /// + string ServiceName { get; } + + /// + /// Gets the for the client request. + /// + /// + /// A object for the client request. + /// + TransportContext TransportContext { get; } + + /// + /// Gets the object requested by the client. + /// + /// + /// A object that identifies the resource requested by the client. + /// + Uri Url { get; } + + /// + /// Gets the Uniform Resource Identifier (URI) of the resource that referred the + /// client to the server. + /// + /// + /// A object that contains the text of the request's + /// header, or null if the header was not included in the request. + /// + Uri UrlReferrer { get; } + + /// + /// Gets the user agent presented by the client. + /// + /// + /// A object that contains the text of the request's User-Agent header. + /// + string UserAgent { get; } + + /// + /// Gets the server IP address and port number to which the request is directed. + /// + /// + /// A that contains the host address information. + /// + string UserHostAddress { get; } + + /// + /// Gets the DNS name and, if provided, the port number specified by the client. + /// + /// + /// A value that contains the text of the request's Host header. + /// + string UserHostName { get; } + + /// + /// Gets the natural languages that are preferred for the response. + /// + /// + /// A array that contains the languages specified in the request's + /// header or null if the client request + /// did not include an header. + /// + string[] UserLanguages { get; } + + /// + /// Begins an asynchronous request for the client's X.509 v.3 certificate. + /// + /// + /// An delegate that references the method to invoke when the + /// operation is complete. + /// + /// + /// A user-defined object that contains information about the operation. This object + /// is passed to the callback delegate when the operation completes. + /// + /// + /// An that indicates the status of the operation. + /// + IAsyncResult BeginGetClientCertificate(AsyncCallback requestCallback, object state); + + /// + /// Ends an asynchronous request for the client's X.509 v.3 certificate. + /// + /// + /// The pending request for the certificate. + /// + /// + /// The System.IAsyncResult object that is returned when the operation started. + /// + /// + /// asyncResult is null. + /// + /// + /// asyncResult was not obtained by calling . + /// + /// + /// This method was already called for the operation identified by asyncResult. + /// + X509Certificate2 EndGetClientCertificate(IAsyncResult asyncResult); + + /// + /// Retrieves the client's X.509 v.3 certificate. + /// + /// + /// A object that contains the client's X.509 v.3 certificate. + /// + /// + /// A call to this method to retrieve the client's X.509 v.3 certificate is in progress + /// and therefore another call to this method cannot be made. + /// + X509Certificate2 GetClientCertificate(); + + /// + /// Retrieves the client's X.509 v.3 certificate as an asynchronous operation. + /// + /// + /// The task object representing the asynchronous operation. The property + /// on the task object returns a object that containsthe client's X.509 v.3 + /// certificate. + /// + Task GetClientCertificateAsync(); + } +} diff --git a/src/Net/IHttpListenerResponse.cs b/src/Net/IHttpListenerResponse.cs new file mode 100644 index 0000000..409eedd --- /dev/null +++ b/src/Net/IHttpListenerResponse.cs @@ -0,0 +1,292 @@ +using System; +using System.IO; +using System.Net; +using System.Text; + +namespace Rothko +{ + /// + /// Represents a response to a request being handled by an . + /// + public interface IHttpListenerResponse : IDisposable + { + /// + /// Gets or sets the for this response's . + /// + /// + /// An object suitable for use with the data in the + /// property, or null if no encoding is specified. + /// + Encoding ContentEncoding { get; set; } + + /// + /// Gets or sets the number of bytes in the body data included in the response. + /// + /// + /// The value of the response's Content-Length header. + /// + /// + /// The value specified for a set operation is less than zero. + /// + /// + /// The response is already being sent. + /// + /// + /// This object is closed. + /// + long ContentLength64 { get; set; } + + /// + /// Gets or sets the MIME type of the content returned. + /// + /// + /// A instance that contains the text of the response's Content-Type + /// header. + /// + /// + /// The value specified for a set operation is null. + /// + /// + /// The value specified for a set operation is an empty string (""). + /// + /// + /// This object is closed. + /// + string ContentType { get; set; } + + /// + /// Gets or sets the collection of cookies returned with the response. + /// + /// + /// A that contains cookies to accompany the response. + /// The collection is empty if no cookies have been added to the response. + /// + CookieCollection Cookies { get; set; } + + /// + /// Gets or sets the collection of header name/value pairs returned by the server. + /// + /// + /// A instance that contains all the explicitly set + /// HTTP headers to be included in the response. + /// + /// + /// The instance specified for a set operation is + /// not valid for a response. + /// + WebHeaderCollection Headers { get; set; } + + /// + /// Gets or sets a value indicating whether the server requests a persistent connection. + /// + /// + /// true if the server requests a persistent connection; otherwise, false. The default + /// is true. + /// + /// + /// This object is closed. + /// + bool KeepAlive { get; set; } + + /// + /// Gets a System.IO.Stream object to which a response can be written. + /// + /// + /// A System.IO.Stream object to which a response can be written. + /// + /// + /// This object is closed. + /// + Stream OutputStream { get; } + + /// + /// Gets or sets the HTTP version used for the response. + /// + /// + /// A object indicating the version of HTTP used when responding to + /// the client. Note that this property is now obsolete. + /// + /// + /// The value specified for a set operation is null. + /// + /// + /// The value specified for a set operation does not have its + /// property set to 1 or does not have its property set to either + /// 0 or 1. + /// + /// + /// This object is closed. + /// + Version ProtocolVersion { get; set; } + + /// + /// Gets or sets the value of the HTTP Location header in this response. + /// + /// + /// A that contains the absolute URL to be sent to the client in the + /// Location header. + /// + /// + /// The value specified for a set operation is an empty string (""). + /// + /// + /// This object is closed. + /// + string RedirectLocation { get; set; } + + /// + /// Gets or sets whether the response uses chunked transfer encoding. + /// + /// + /// true if the response is set to use chunked transfer encoding; otherwise, false. + /// The default is false. + /// + bool SendChunked { get; set; } + + /// + /// Gets or sets the HTTP status code to be returned to the client. + /// + /// + /// An value that specifies the HTTP status code for the requested resource. + /// The default is , indicating that the server successfully + /// processed the client's request and included the requested resource in the response + /// body. + /// + /// + /// This object is closed. + /// + /// + /// The value specified for a set operation is not valid. Valid values are between + /// 100 and 999 inclusive. + /// + int StatusCode { get; set; } + + /// + /// Gets or sets a text description of the HTTP status code returned to the client. + /// + /// + /// The text description of the HTTP status code returned to the client. The default + /// is the RFC 2616 description for the System.Net.HttpListenerResponse.StatusCode + /// property value, or an empty string ("") if an RFC 2616 description does not exist. + /// + /// Exceptions: + /// + /// The value specified for a set operation is null. + /// + /// + /// The value specified for a set operation contains non-printable characters. + /// + string StatusDescription { get; set; } + + /// + /// Closes the connection to the client without sending a response. + /// + void Abort(); + + /// + /// Adds the specified header and value to the HTTP headers for this response. + /// + /// + /// The name of the HTTP header to set. + /// + /// + /// The value for the name header. + /// + /// + /// name is null or an empty string (""). + /// + /// + /// You are not allowed to specify a value for the specified header.-or-name or value + /// contains invalid characters. + /// + /// + /// The length of value is greater than 65,535 characters. + /// + void AddHeader(string name, string value); + + /// + /// Adds the specified System.Net.Cookie to the collection of cookies for this response. + /// + /// + /// The System.Net.Cookie to add to the collection to be sent with this response + /// + /// + /// cookie is null. + /// + void AppendCookie(Cookie cookie); + + /// + /// Appends a value to the specified HTTP header to be sent with this response. + /// + /// + /// The name of the HTTP header to append value to. + /// + /// + /// The value to append to the name header. + /// + /// + /// name is null or an empty string ("").-or-You are not allowed to specify a value + /// for the specified header.-or-name or value contains invalid characters. + /// + /// + /// The length of value is greater than 65,535 characters. + /// + void AppendHeader(string name, string value); + + /// + /// Sends the response to the client and releases the resources held by this System.Net.HttpListenerResponse + /// instance. + /// + void Close(); + + /// + /// Returns the specified byte array to the client and releases the resources held + /// by this System.Net.HttpListenerResponse instance. + /// + /// + /// A array that contains the response to send to the client. + /// + /// + /// true to block execution while flushing the stream to the client; otherwise, false. + /// + /// + /// responseEntity is null. + /// + /// + /// This object is closed. + /// + void Close(byte[] responseEntity, bool willBlock); + + /// + /// Copies properties from the specified System.Net.HttpListenerResponse to this + /// response. + /// + /// + /// The System.Net.HttpListenerResponse instance to copy. + /// + void CopyFrom(HttpListenerResponse templateResponse); + + /// + /// Configures the response to redirect the client to the specified URL. + /// + /// + /// The URL that the client should use to locate the requested resource. + /// + void Redirect(string url); + + /// + /// Adds or updates a System.Net.Cookie in the collection of cookies sent with this + /// response. + /// + /// + /// A for this response. + /// + /// + /// cookie is null. + /// + /// + /// The cookie already exists in the collection and could not be replaced. + /// + void SetCookie(Cookie cookie); + } +} diff --git a/src/Net/INetFactory.cs b/src/Net/INetFactory.cs new file mode 100644 index 0000000..7a31a02 --- /dev/null +++ b/src/Net/INetFactory.cs @@ -0,0 +1,9 @@ +using System; + +namespace Rothko +{ + public interface INetFactory + { + IHttpListener CreateHttpListener(); + } +} diff --git a/src/Net/NetFactory.cs b/src/Net/NetFactory.cs new file mode 100644 index 0000000..af3bf89 --- /dev/null +++ b/src/Net/NetFactory.cs @@ -0,0 +1,12 @@ +using System.Net; + +namespace Rothko +{ + public class NetFactory : INetFactory + { + public IHttpListener CreateHttpListener() + { + return new HttpListenerWrapper(new HttpListener()); + } + } +} diff --git a/src/OperatingSystemFacade.cs b/src/OperatingSystemFacade.cs index ecbeac4..3225526 100644 --- a/src/OperatingSystemFacade.cs +++ b/src/OperatingSystemFacade.cs @@ -15,6 +15,7 @@ public OperatingSystemFacade() Environment = new Environment(); File = new FileFacade(); MemoryMappedFiles = new MemoryMappedFileFactory(); + Net = new NetFactory(); ProcessLocator = new ProcessLocator(); ProcessStarter = new ProcessStarter(); Registry = new Registry(); @@ -27,6 +28,7 @@ public OperatingSystemFacade() public IEnvironment Environment { get; private set; } public IFileFacade File { get; private set; } public IMemoryMappedFileFactory MemoryMappedFiles { get; private set; } + public INetFactory Net { get; private set; } public IProcessLocator ProcessLocator { get; private set; } public IProcessStarter ProcessStarter { get; private set; } public IRegistry Registry { get; private set; } diff --git a/src/Rothko.csproj b/src/Rothko.csproj index 7d16f96..6cb585e 100644 --- a/src/Rothko.csproj +++ b/src/Rothko.csproj @@ -106,6 +106,16 @@ + + + + + + + + + +