using System;
namespace NetCoreServer
{
///
/// HTTP session is used to receive/send HTTP requests/responses from the connected HTTP client.
///
/// Thread-safe.
public class HttpSession : TcpSession
{
public HttpSession(HttpServer server) : base(server)
{
Cache = server.Cache;
Request = new HttpRequest();
Response = new HttpResponse();
}
///
/// Get the static content cache
///
public FileCache Cache { get; }
///
/// Get the HTTP request
///
protected HttpRequest Request { get; }
///
/// Get the HTTP response
///
public HttpResponse Response { get; }
#region Send response / Send response body
///
/// Send the current HTTP response (synchronous)
///
/// Size of sent data
public long SendResponse() => SendResponse(Response);
///
/// Send the HTTP response (synchronous)
///
/// HTTP response
/// Size of sent data
public long SendResponse(HttpResponse response) => Send(response.Cache.Data, response.Cache.Offset, response.Cache.Size);
///
/// Send the HTTP response body (synchronous)
///
/// HTTP response body
/// Size of sent data
public long SendResponseBody(string body) => Send(body);
///
/// Send the HTTP response body (synchronous)
///
/// HTTP response body as a span of characters
/// Size of sent data
public long SendResponseBody(ReadOnlySpan body) => Send(body);
///
/// Send the HTTP response body (synchronous)
///
/// HTTP response body buffer
/// Size of sent data
public long SendResponseBody(byte[] buffer) => Send(buffer);
///
/// Send the HTTP response body (synchronous)
///
/// HTTP response body buffer
/// HTTP response body buffer offset
/// HTTP response body size
/// Size of sent data
public long SendResponseBody(byte[] buffer, long offset, long size) => Send(buffer, offset, size);
///
/// Send the HTTP response body (synchronous)
///
/// HTTP response body buffer as a span of bytes
/// Size of sent data
public long SendResponseBody(ReadOnlySpan buffer) => Send(buffer);
///
/// Send the current HTTP response (asynchronous)
///
/// 'true' if the current HTTP response was successfully sent, 'false' if the session is not connected
public bool SendResponseAsync() => SendResponseAsync(Response);
///
/// Send the HTTP response (asynchronous)
///
/// HTTP response
/// 'true' if the current HTTP response was successfully sent, 'false' if the session is not connected
public bool SendResponseAsync(HttpResponse response) => SendAsync(response.Cache.Data, response.Cache.Offset, response.Cache.Size);
///
/// Send the HTTP response body (asynchronous)
///
/// HTTP response body
/// 'true' if the HTTP response body was successfully sent, 'false' if the session is not connected
public bool SendResponseBodyAsync(string body) => SendAsync(body);
///
/// Send the HTTP response body (asynchronous)
///
/// HTTP response body as a span of characters
/// 'true' if the HTTP response body was successfully sent, 'false' if the session is not connected
public bool SendResponseBodyAsync(ReadOnlySpan body) => SendAsync(body);
///
/// Send the HTTP response body (asynchronous)
///
/// HTTP response body buffer
/// 'true' if the HTTP response body was successfully sent, 'false' if the session is not connected
public bool SendResponseBodyAsync(byte[] buffer) => SendAsync(buffer);
///
/// Send the HTTP response body (asynchronous)
///
/// HTTP response body buffer
/// HTTP response body buffer offset
/// HTTP response body size
/// 'true' if the HTTP response body was successfully sent, 'false' if the session is not connected
public bool SendResponseBodyAsync(byte[] buffer, long offset, long size) => SendAsync(buffer, offset, size);
///
/// Send the HTTP response body (asynchronous)
///
/// HTTP response body buffer as a span of bytes
/// 'true' if the HTTP response body was successfully sent, 'false' if the session is not connected
public bool SendResponseBodyAsync(ReadOnlySpan 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;
}
}
///
/// Handle HTTP request header received notification
///
/// Notification is called when HTTP request header was received from the client.
/// HTTP request
protected virtual void OnReceivedRequestHeader(HttpRequest request) {}
///
/// Handle HTTP request received notification
///
/// Notification is called when HTTP request was received from the client.
/// HTTP request
protected virtual void OnReceivedRequest(HttpRequest request) {}
///
/// Handle HTTP cached request received notification
///
///
/// 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.
///
/// HTTP request
/// Cached response content
protected virtual void OnReceivedCachedRequest(HttpRequest request, byte[] content) { SendAsync(content); }
///
/// Handle HTTP request error notification
///
/// Notification is called when HTTP request error was received from the client.
/// HTTP request
/// HTTP request error
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);
}
}
}