123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447 |
- #region License
- /*
- * HttpListenerContext.cs
- *
- * This code is derived from HttpListenerContext.cs (System.Net) of Mono
- * (http://www.mono-project.com).
- *
- * The MIT License
- *
- * Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
- * Copyright (c) 2012-2022 sta.blockhead
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
- #endregion
- #region Authors
- /*
- * Authors:
- * - Gonzalo Paniagua Javier <gonzalo@novell.com>
- */
- #endregion
- using System;
- using System.Security.Principal;
- using System.Text;
- using WebSocketSharp.Net.WebSockets;
- namespace WebSocketSharp.Net
- {
- /// <summary>
- /// Provides the access to the HTTP request and response objects used by
- /// the <see cref="HttpListener"/> class.
- /// </summary>
- /// <remarks>
- /// This class cannot be inherited.
- /// </remarks>
- public sealed class HttpListenerContext
- {
- #region Private Fields
- private HttpConnection _connection;
- private string _errorMessage;
- private int _errorStatusCode;
- private HttpListener _listener;
- private HttpListenerRequest _request;
- private HttpListenerResponse _response;
- private IPrincipal _user;
- private HttpListenerWebSocketContext _websocketContext;
- #endregion
- #region Internal Constructors
- internal HttpListenerContext (HttpConnection connection)
- {
- _connection = connection;
- _errorStatusCode = 400;
- _request = new HttpListenerRequest (this);
- _response = new HttpListenerResponse (this);
- }
- #endregion
- #region Internal Properties
- internal HttpConnection Connection {
- get {
- return _connection;
- }
- }
- internal string ErrorMessage {
- get {
- return _errorMessage;
- }
- set {
- _errorMessage = value;
- }
- }
- internal int ErrorStatusCode {
- get {
- return _errorStatusCode;
- }
- set {
- _errorStatusCode = value;
- }
- }
- internal bool HasErrorMessage {
- get {
- return _errorMessage != null;
- }
- }
- internal HttpListener Listener {
- get {
- return _listener;
- }
- set {
- _listener = value;
- }
- }
- #endregion
- #region Public Properties
- /// <summary>
- /// Gets the HTTP request object that represents a client request.
- /// </summary>
- /// <value>
- /// A <see cref="HttpListenerRequest"/> that represents the client request.
- /// </value>
- public HttpListenerRequest Request {
- get {
- return _request;
- }
- }
- /// <summary>
- /// Gets the HTTP response object used to send a response to the client.
- /// </summary>
- /// <value>
- /// A <see cref="HttpListenerResponse"/> that represents a response to
- /// the client request.
- /// </value>
- public HttpListenerResponse Response {
- get {
- return _response;
- }
- }
- /// <summary>
- /// Gets the client information.
- /// </summary>
- /// <value>
- /// <para>
- /// A <see cref="IPrincipal"/> instance that represents identity,
- /// authentication, and security roles for the client.
- /// </para>
- /// <para>
- /// <see langword="null"/> if the client is not authenticated.
- /// </para>
- /// </value>
- public IPrincipal User {
- get {
- return _user;
- }
- }
- #endregion
- #region Private Methods
- private static string createErrorContent (
- int statusCode, string statusDescription, string message
- )
- {
- return message != null && message.Length > 0
- ? String.Format (
- "<html><body><h1>{0} {1} ({2})</h1></body></html>",
- statusCode,
- statusDescription,
- message
- )
- : String.Format (
- "<html><body><h1>{0} {1}</h1></body></html>",
- statusCode,
- statusDescription
- );
- }
- #endregion
- #region Internal Methods
- internal HttpListenerWebSocketContext GetWebSocketContext (string protocol)
- {
- _websocketContext = new HttpListenerWebSocketContext (this, protocol);
- return _websocketContext;
- }
- internal void SendAuthenticationChallenge (
- AuthenticationSchemes scheme, string realm
- )
- {
- _response.StatusCode = 401;
- var chal = new AuthenticationChallenge (scheme, realm).ToString ();
- _response.Headers.InternalSet ("WWW-Authenticate", chal, true);
- _response.Close ();
- }
- internal void SendError ()
- {
- try {
- _response.StatusCode = _errorStatusCode;
- _response.ContentType = "text/html";
- var content = createErrorContent (
- _errorStatusCode,
- _response.StatusDescription,
- _errorMessage
- );
- var enc = Encoding.UTF8;
- var entity = enc.GetBytes (content);
- _response.ContentEncoding = enc;
- _response.ContentLength64 = entity.LongLength;
- _response.Close (entity, true);
- }
- catch {
- _connection.Close (true);
- }
- }
- internal void SendError (int statusCode)
- {
- _errorStatusCode = statusCode;
- SendError ();
- }
- internal void SendError (int statusCode, string message)
- {
- _errorStatusCode = statusCode;
- _errorMessage = message;
- SendError ();
- }
- internal bool SetUser (
- AuthenticationSchemes scheme,
- string realm,
- Func<IIdentity, NetworkCredential> credentialsFinder
- )
- {
- var user = HttpUtility.CreateUser (
- _request.Headers["Authorization"],
- scheme,
- realm,
- _request.HttpMethod,
- credentialsFinder
- );
- if (user == null)
- return false;
- if (!user.Identity.IsAuthenticated)
- return false;
- _user = user;
- return true;
- }
- internal void Unregister ()
- {
- if (_listener == null)
- return;
- _listener.UnregisterContext (this);
- }
- #endregion
- #region Public Methods
- /// <summary>
- /// Accepts a WebSocket connection.
- /// </summary>
- /// <returns>
- /// A <see cref="HttpListenerWebSocketContext"/> that represents
- /// the WebSocket handshake request.
- /// </returns>
- /// <param name="protocol">
- /// <para>
- /// A <see cref="string"/> that specifies the name of the subprotocol
- /// supported on the WebSocket connection.
- /// </para>
- /// <para>
- /// <see langword="null"/> if not necessary.
- /// </para>
- /// </param>
- /// <exception cref="InvalidOperationException">
- /// <para>
- /// This method has already been done.
- /// </para>
- /// <para>
- /// -or-
- /// </para>
- /// <para>
- /// The client request is not a WebSocket handshake request.
- /// </para>
- /// </exception>
- /// <exception cref="ArgumentException">
- /// <para>
- /// <paramref name="protocol"/> is empty.
- /// </para>
- /// <para>
- /// -or-
- /// </para>
- /// <para>
- /// <paramref name="protocol"/> contains an invalid character.
- /// </para>
- /// </exception>
- public HttpListenerWebSocketContext AcceptWebSocket (string protocol)
- {
- return AcceptWebSocket (protocol, null);
- }
- /// <summary>
- /// Accepts a WebSocket connection with initializing the WebSocket
- /// interface.
- /// </summary>
- /// <returns>
- /// A <see cref="HttpListenerWebSocketContext"/> that represents
- /// the WebSocket handshake request.
- /// </returns>
- /// <param name="protocol">
- /// <para>
- /// A <see cref="string"/> that specifies the name of the subprotocol
- /// supported on the WebSocket connection.
- /// </para>
- /// <para>
- /// <see langword="null"/> if not necessary.
- /// </para>
- /// </param>
- /// <param name="initializer">
- /// <para>
- /// An <see cref="T:System.Action{WebSocket}"/> delegate.
- /// </para>
- /// <para>
- /// It specifies the delegate that invokes the method called when
- /// initializing a new WebSocket instance.
- /// </para>
- /// </param>
- /// <exception cref="InvalidOperationException">
- /// <para>
- /// This method has already been done.
- /// </para>
- /// <para>
- /// -or-
- /// </para>
- /// <para>
- /// The client request is not a WebSocket handshake request.
- /// </para>
- /// </exception>
- /// <exception cref="ArgumentException">
- /// <para>
- /// <paramref name="protocol"/> is empty.
- /// </para>
- /// <para>
- /// -or-
- /// </para>
- /// <para>
- /// <paramref name="protocol"/> contains an invalid character.
- /// </para>
- /// <para>
- /// -or-
- /// </para>
- /// <para>
- /// <paramref name="initializer"/> caused an exception.
- /// </para>
- /// </exception>
- public HttpListenerWebSocketContext AcceptWebSocket (
- string protocol, Action<WebSocket> initializer
- )
- {
- if (_websocketContext != null) {
- var msg = "The method has already been done.";
- throw new InvalidOperationException (msg);
- }
- if (!_request.IsWebSocketRequest) {
- var msg = "The request is not a WebSocket handshake request.";
- throw new InvalidOperationException (msg);
- }
- if (protocol != null) {
- if (protocol.Length == 0) {
- var msg = "An empty string.";
- throw new ArgumentException (msg, "protocol");
- }
- if (!protocol.IsToken ()) {
- var msg = "It contains an invalid character.";
- throw new ArgumentException (msg, "protocol");
- }
- }
- var ret = GetWebSocketContext (protocol);
- var ws = ret.WebSocket;
- if (initializer != null) {
- try {
- initializer (ws);
- }
- catch (Exception ex) {
- if (ws.ReadyState == WebSocketState.New)
- _websocketContext = null;
- var msg = "It caused an exception.";
- throw new ArgumentException (msg, "initializer", ex);
- }
- }
- ws.Accept ();
- return ret;
- }
- #endregion
- }
- }
|