/*
* Copyright (C) 2011 uhttpsharp project - http://github.com/raistlinthewiz/uhttpsharp
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using uhttpsharp.Headers;
namespace uhttpsharp
{
public interface IHttpResponse
{
Task WriteBody(StreamWriter writer);
///
/// Gets the status line of this http response,
/// The first line that will be sent to the client.
///
HttpResponseCode ResponseCode { get; }
IHttpHeaders Headers { get; }
bool CloseConnection { get; }
}
public abstract class HttpResponseBase : IHttpResponse
{
private readonly HttpResponseCode _code;
private readonly IHttpHeaders _headers;
protected HttpResponseBase(HttpResponseCode code, IHttpHeaders headers)
{
_code = code;
_headers = headers;
}
public abstract Task WriteBody(StreamWriter writer);
public HttpResponseCode ResponseCode
{
get { return _code; }
}
public IHttpHeaders Headers
{
get { return _headers; }
}
public bool CloseConnection
{
get
{
string value;
return !(_headers.TryGetByName("Connection", out value) &&
value.Equals("Keep-Alive", StringComparison.InvariantCultureIgnoreCase));
}
}
}
public sealed class StreamHttpResponse : HttpResponseBase
{
private readonly Stream _body;
public StreamHttpResponse(Stream body, HttpResponseCode code, IHttpHeaders headers) : base(code, headers)
{
_body = body;
}
public static IHttpResponse Create(Stream body, HttpResponseCode code = HttpResponseCode.Ok, string contentType = "text/html; charset=utf-8", bool keepAlive = true)
{
return new StreamHttpResponse(body, code, new ListHttpHeaders(new[]
{
new KeyValuePair("Date", DateTime.UtcNow.ToString("R")),
new KeyValuePair("content-type", contentType),
new KeyValuePair("connection", keepAlive ? "keep-alive" : "close"),
new KeyValuePair("content-length", body.Length.ToString(CultureInfo.InvariantCulture)),
}));
}
public async override Task WriteBody(StreamWriter writer)
{
await writer.FlushAsync().ConfigureAwait(false);
await _body.CopyToAsync(writer.BaseStream).ConfigureAwait(false);
await writer.BaseStream.FlushAsync().ConfigureAwait(false);
}
}
public sealed class EmptyHttpResponse : HttpResponseBase
{
public EmptyHttpResponse(HttpResponseCode code, IHttpHeaders headers) : base(code, headers)
{
}
public static IHttpResponse Create(HttpResponseCode code = HttpResponseCode.Ok, bool keepAlive = true)
{
return new EmptyHttpResponse(code, new ListHttpHeaders(new[]
{
new KeyValuePair("Date", DateTime.UtcNow.ToString("R")),
new KeyValuePair("content-type", "text/html"),
new KeyValuePair("connection", keepAlive ? "keep-alive" : "close"),
new KeyValuePair("content-length", "0"),
}));
}
public override Task WriteBody(StreamWriter writer)
{
return Task.Factory.GetCompleted();
}
}
public sealed class StringHttpResponse : HttpResponseBase
{
private readonly string _body;
public StringHttpResponse(string body, HttpResponseCode code, IHttpHeaders headers) : base(code, headers)
{
_body = body;
}
public static IHttpResponse Create(string body, HttpResponseCode code = HttpResponseCode.Ok, string contentType = "text/html; charset=utf-8", bool keepAlive = true, IHttpHeaders headers = null)
{
// TODO : Add Overload
if (headers == null)
{
headers = EmptyHttpHeaders.Empty;
}
return new StringHttpResponse(body, code, new CompositeHttpHeaders(new ListHttpHeaders(new[]
{
new KeyValuePair("Date", DateTime.UtcNow.ToString("R")),
new KeyValuePair("content-type", contentType),
new KeyValuePair("connection", keepAlive ? "keep-alive" : "close"),
new KeyValuePair("content-length", Encoding.UTF8.GetByteCount(body).ToString(CultureInfo.InvariantCulture)),
}), headers));
}
public async override Task WriteBody(StreamWriter writer)
{
await writer.WriteAsync(_body).ConfigureAwait(false);
}
}
public sealed class HttpResponse : IHttpResponse
{
private Stream ContentStream { get; set; }
private readonly Stream _headerStream = new MemoryStream();
private readonly bool _closeConnection;
private readonly IHttpHeaders _headers;
private readonly HttpResponseCode _responseCode;
public HttpResponse(HttpResponseCode code, string content, bool closeConnection)
: this(code, "text/html; charset=utf-8", StringToStream(content), closeConnection)
{
}
public HttpResponse(HttpResponseCode code, string content, IEnumerable> headers, bool closeConnection)
: this(code, "text/html; charset=utf-8", StringToStream(content), closeConnection,headers)
{
}
public HttpResponse(string contentType, Stream contentStream, bool closeConnection)
: this(HttpResponseCode.Ok, contentType, contentStream, closeConnection)
{
}
public HttpResponse(HttpResponseCode code, string contentType, Stream contentStream, bool keepAliveConnection, IEnumerable> headers)
{
ContentStream = contentStream;
_closeConnection = !keepAliveConnection;
_responseCode = code;
_headers = new ListHttpHeaders(new[]
{
new KeyValuePair("Date", DateTime.UtcNow.ToString("R")),
new KeyValuePair("Connection", _closeConnection ? "Close" : "Keep-Alive"),
new KeyValuePair("Content-Type", contentType),
new KeyValuePair("Content-Length", ContentStream.Length.ToString(CultureInfo.InvariantCulture)),
}.Concat(headers).ToList());
}
public HttpResponse(HttpResponseCode code, string contentType, Stream contentStream, bool keepAliveConnection) :
this(code, contentType, contentStream, keepAliveConnection, Enumerable.Empty>())
{
}
public HttpResponse(HttpResponseCode code, byte[] contentStream, bool keepAliveConnection)
: this (code, "text/html; charset=utf-8", new MemoryStream(contentStream), keepAliveConnection)
{
}
public static HttpResponse CreateWithMessage(HttpResponseCode code, string message, bool keepAliveConnection, string body = "")
{
return new HttpResponse(
code,
string.Format(
"{0}{0}
{1}",
message, body), keepAliveConnection);
}
private static MemoryStream StringToStream(string content)
{
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
writer.Write(content);
writer.Flush();
return stream;
}
public async Task WriteBody(StreamWriter writer)
{
ContentStream.Position = 0;
await ContentStream.CopyToAsync(writer.BaseStream).ConfigureAwait(false);
}
public HttpResponseCode ResponseCode
{
get { return _responseCode; }
}
public IHttpHeaders Headers
{
get { return _headers; }
}
public bool CloseConnection
{
get { return _closeConnection; }
}
public async Task WriteHeaders(StreamWriter writer)
{
_headerStream.Position = 0;
await _headerStream.CopyToAsync(writer.BaseStream).ConfigureAwait(false);
}
}
}