123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237 |
- using System;
- namespace NetCoreServer
- {
- /// <summary>
- /// HTTPS session is used to receive/send HTTP requests/responses from the connected HTTPS client.
- /// </summary>
- /// <remarks>Thread-safe.</remarks>
- public class HttpsSession : SslSession
- {
- public HttpsSession(HttpsServer server) : base(server)
- {
- Cache = server.Cache;
- Request = new HttpRequest();
- Response = new HttpResponse();
- }
- /// <summary>
- /// Get the static content cache
- /// </summary>
- public FileCache Cache { get; }
- /// <summary>
- /// Get the HTTP request
- /// </summary>
- protected HttpRequest Request { get; }
- /// <summary>
- /// Get the HTTP response
- /// </summary>
- public HttpResponse Response { get; }
- #region Send response / Send response body
- /// <summary>
- /// Send the current HTTP response (synchronous)
- /// </summary>
- /// <returns>Size of sent data</returns>
- public long SendResponse() => SendResponse(Response);
- /// <summary>
- /// Send the HTTP response (synchronous)
- /// </summary>
- /// <param name="response">HTTP response</param>
- /// <returns>Size of sent data</returns>
- public long SendResponse(HttpResponse response) => Send(response.Cache.Data, response.Cache.Offset, response.Cache.Size);
- /// <summary>
- /// Send the HTTP response body (synchronous)
- /// </summary>
- /// <param name="body">HTTP response body</param>
- /// <returns>Size of sent data</returns>
- public long SendResponseBody(string body) => Send(body);
- /// <summary>
- /// Send the HTTP response body (synchronous)
- /// </summary>
- /// <param name="body">HTTP response body as a span of characters</param>
- /// <returns>Size of sent data</returns>
- public long SendResponseBody(ReadOnlySpan<char> body) => Send(body);
- /// <summary>
- /// Send the HTTP response body (synchronous)
- /// </summary>
- /// <param name="buffer">HTTP response body buffer</param>
- /// <returns>Size of sent data</returns>
- public long SendResponseBody(byte[] buffer) => Send(buffer);
- /// <summary>
- /// Send the HTTP response body (synchronous)
- /// </summary>
- /// <param name="buffer">HTTP response body buffer</param>
- /// <param name="offset">HTTP response body buffer offset</param>
- /// <param name="size">HTTP response body size</param>
- /// <returns>Size of sent data</returns>
- public long SendResponseBody(byte[] buffer, long offset, long size) => Send(buffer, offset, size);
- /// <summary>
- /// Send the HTTP response body (synchronous)
- /// </summary>
- /// <param name="buffer">HTTP response body buffer as a span of bytes</param>
- /// <returns>Size of sent data</returns>
- public long SendResponseBody(ReadOnlySpan<byte> buffer) => Send(buffer);
- /// <summary>
- /// Send the current HTTP response (asynchronous)
- /// </summary>
- /// <returns>'true' if the current HTTP response was successfully sent, 'false' if the session is not connected</returns>
- public bool SendResponseAsync() => SendResponseAsync(Response);
- /// <summary>
- /// Send the HTTP response (asynchronous)
- /// </summary>
- /// <param name="response">HTTP response</param>
- /// <returns>'true' if the current HTTP response was successfully sent, 'false' if the session is not connected</returns>
- public bool SendResponseAsync(HttpResponse response) => SendAsync(response.Cache.Data, response.Cache.Offset, response.Cache.Size);
- /// <summary>
- /// Send the HTTP response body (asynchronous)
- /// </summary>
- /// <param name="body">HTTP response body</param>
- /// <returns>'true' if the HTTP response body was successfully sent, 'false' if the session is not connected</returns>
- public bool SendResponseBodyAsync(string body) => SendAsync(body);
- /// <summary>
- /// Send the HTTP response body (asynchronous)
- /// </summary>
- /// <param name="body">HTTP response body as a span of characters</param>
- /// <returns>'true' if the HTTP response body was successfully sent, 'false' if the session is not connected</returns>
- public bool SendResponseBodyAsync(ReadOnlySpan<char> body) => SendAsync(body);
- /// <summary>
- /// Send the HTTP response body (asynchronous)
- /// </summary>
- /// <param name="buffer">HTTP response body buffer</param>
- /// <returns>'true' if the HTTP response body was successfully sent, 'false' if the session is not connected</returns>
- public bool SendResponseBodyAsync(byte[] buffer) => SendAsync(buffer);
- /// <summary>
- /// Send the HTTP response body (asynchronous)
- /// </summary>
- /// <param name="buffer">HTTP response body buffer</param>
- /// <param name="offset">HTTP response body buffer offset</param>
- /// <param name="size">HTTP response body size</param>
- /// <returns>'true' if the HTTP response body was successfully sent, 'false' if the session is not connected</returns>
- public bool SendResponseBodyAsync(byte[] buffer, long offset, long size) => SendAsync(buffer, offset, size);
- /// <summary>
- /// Send the HTTP response body (asynchronous)
- /// </summary>
- /// <param name="buffer">HTTP response body buffer as a span of bytes</param>
- /// <returns>'true' if the HTTP response body was successfully sent, 'false' if the session is not connected</returns>
- public bool SendResponseBodyAsync(ReadOnlySpan<byte> buffer) => SendAsync(buffer);
- #endregion
- #region Session handlers
- protected override void OnReceived(byte[] buffer, long offset, long size)
- {
- // Receive HTTP request header
- if (Request.IsPendingHeader())
- {
- if (Request.ReceiveHeader(buffer, (int)offset, (int)size))
- OnReceivedRequestHeader(Request);
- size = 0;
- }
- // Check for HTTP request error
- if (Request.IsErrorSet)
- {
- OnReceivedRequestError(Request, "Invalid HTTP request!");
- Request.Clear();
- Disconnect();
- return;
- }
- // Receive HTTP request body
- if (Request.ReceiveBody(buffer, (int)offset, (int)size))
- {
- OnReceivedRequestInternal(Request);
- Request.Clear();
- return;
- }
- // Check for HTTP request error
- if (Request.IsErrorSet)
- {
- OnReceivedRequestError(Request, "Invalid HTTP request!");
- Request.Clear();
- Disconnect();
- return;
- }
- }
- protected override void OnDisconnected()
- {
- // Receive HTTP request body
- if (Request.IsPendingBody())
- {
- OnReceivedRequestInternal(Request);
- Request.Clear();
- return;
- }
- }
- /// <summary>
- /// Handle HTTP request header received notification
- /// </summary>
- /// <remarks>Notification is called when HTTP request header was received from the client.</remarks>
- /// <param name="request">HTTP request</param>
- protected virtual void OnReceivedRequestHeader(HttpRequest request) {}
- /// <summary>
- /// Handle HTTP request received notification
- /// </summary>
- /// <remarks>Notification is called when HTTP request was received from the client.</remarks>
- /// <param name="request">HTTP request</param>
- protected virtual void OnReceivedRequest(HttpRequest request) {}
- /// <summary>
- /// Handle HTTP cached request received notification
- /// </summary>
- /// <remarks>
- /// Notification is called when HTTP request was received
- /// from the client and the corresponding cached content
- /// was found.
- ///
- /// Default behavior is just send cached response content
- /// to the client.
- /// </remarks>
- /// <param name="request">HTTP request</param>
- /// <param name="content">Cached response content</param>
- protected virtual void OnReceivedCachedRequest(HttpRequest request, byte[] content) { SendAsync(content); }
- /// <summary>
- /// Handle HTTP request error notification
- /// </summary>
- /// <remarks>Notification is called when HTTP request error was received from the client.</remarks>
- /// <param name="request">HTTP request</param>
- /// <param name="error">HTTP request error</param>
- protected virtual void OnReceivedRequestError(HttpRequest request, string error) {}
- #endregion
- private void OnReceivedRequestInternal(HttpRequest request)
- {
- // Try to get the cached response
- if (request.Method == "GET")
- {
- var index = request.Url.IndexOf('?');
- var response = Cache.Find((index < 0) ? request.Url : request.Url.Substring(0, index));
- if (response.Item1)
- {
- // Process the request with the cached response
- OnReceivedCachedRequest(request, response.Item2);
- return;
- }
- }
- // Process the request
- OnReceivedRequest(request);
- }
- }
- }
|