Refactored HttpListenerResponse.cs

This commit is contained in:
sta 2015-06-16 21:37:55 +09:00
parent dc9b2ae488
commit 0c610dd41f
2 changed files with 84 additions and 125 deletions

View File

@ -56,6 +56,7 @@ namespace WebSocketSharp.Net
#region Private Fields #region Private Fields
private bool _chunked; private bool _chunked;
private bool _closeConnection;
private Encoding _contentEncoding; private Encoding _contentEncoding;
private long _contentLength; private long _contentLength;
private bool _contentLengthSet; private bool _contentLengthSet;
@ -79,7 +80,6 @@ namespace WebSocketSharp.Net
internal HttpListenerResponse (HttpListenerContext context) internal HttpListenerResponse (HttpListenerContext context)
{ {
_context = context; _context = context;
_headers = new WebHeaderCollection ();
_keepAlive = true; _keepAlive = true;
_statusCode = 200; _statusCode = 200;
_statusDescription = "OK"; _statusDescription = "OK";
@ -92,7 +92,11 @@ namespace WebSocketSharp.Net
internal bool CloseConnection { internal bool CloseConnection {
get { get {
return _headers["Connection"] == "close"; return _closeConnection;
}
set {
_closeConnection = value;
} }
} }
@ -171,12 +175,12 @@ namespace WebSocketSharp.Net
/// <value> /// <value>
/// A <see cref="string"/> that represents the value of the Content-Type entity-header. /// A <see cref="string"/> that represents the value of the Content-Type entity-header.
/// </value> /// </value>
/// <exception cref="ArgumentException">
/// The value specified for a set operation is empty.
/// </exception>
/// <exception cref="ArgumentNullException"> /// <exception cref="ArgumentNullException">
/// The value specified for a set operation is <see langword="null"/>. /// The value specified for a set operation is <see langword="null"/>.
/// </exception> /// </exception>
/// <exception cref="ArgumentException">
/// The value specified for a set operation is empty.
/// </exception>
/// <exception cref="InvalidOperationException"> /// <exception cref="InvalidOperationException">
/// The response has already been sent. /// The response has already been sent.
/// </exception> /// </exception>
@ -206,19 +210,12 @@ namespace WebSocketSharp.Net
/// <value> /// <value>
/// A <see cref="CookieCollection"/> that contains the cookies sent with the response. /// A <see cref="CookieCollection"/> that contains the cookies sent with the response.
/// </value> /// </value>
/// <exception cref="InvalidOperationException">
/// The response has already been sent.
/// </exception>
/// <exception cref="ObjectDisposedException">
/// This object is closed.
/// </exception>
public CookieCollection Cookies { public CookieCollection Cookies {
get { get {
return _cookies ?? (_cookies = new CookieCollection ()); return _cookies ?? (_cookies = new CookieCollection ());
} }
set { set {
checkDisposedOrHeadersSent ();
_cookies = value; _cookies = value;
} }
} }
@ -229,31 +226,18 @@ namespace WebSocketSharp.Net
/// <value> /// <value>
/// A <see cref="WebHeaderCollection"/> that contains the headers sent to the client. /// A <see cref="WebHeaderCollection"/> that contains the headers sent to the client.
/// </value> /// </value>
/// <exception cref="ArgumentNullException">
/// The value specified for a set operation is <see langword="null"/>.
/// </exception>
/// <exception cref="InvalidOperationException"> /// <exception cref="InvalidOperationException">
/// The headers has already been sent. /// The value specified for a set operation isn't valid for a response.
/// </exception> /// </exception>
public WebHeaderCollection Headers { public WebHeaderCollection Headers {
get { get {
return _headers; return _headers ?? (_headers = new WebHeaderCollection (HttpHeaderType.Response, false));
} }
set { set {
/* if (value != null && value.State != HttpHeaderType.Response)
* "If you attempt to set a Content-Length, Keep-Alive, Transfer-Encoding, throw new InvalidOperationException (
* or WWW-Authenticate header using the Headers property, an exception "The specified headers aren't valid for a response.");
* will be thrown. Use the ContentLength64 or KeepAlive properties to set
* these headers. You cannot set the Transfer-Encoding or WWW-Authenticate
* headers manually."
*/
// TODO: Check if this is marked readonly after the headers are sent.
checkHeadersSent ();
if (value == null)
throw new ArgumentNullException ("value");
_headers = value; _headers = value;
} }
@ -305,13 +289,13 @@ namespace WebSocketSharp.Net
/// <value> /// <value>
/// A <see cref="Version"/> that represents the version used in the response. /// A <see cref="Version"/> that represents the version used in the response.
/// </value> /// </value>
/// <exception cref="ArgumentNullException">
/// The value specified for a set operation is <see langword="null"/>.
/// </exception>
/// <exception cref="ArgumentException"> /// <exception cref="ArgumentException">
/// The value specified for a set operation doesn't have its <c>Major</c> property set to 1 or /// The value specified for a set operation doesn't have its <c>Major</c> property set to 1 or
/// doesn't have its <c>Minor</c> property set to either 0 or 1. /// doesn't have its <c>Minor</c> property set to either 0 or 1.
/// </exception> /// </exception>
/// <exception cref="ArgumentNullException">
/// The value specified for a set operation is <see langword="null"/>.
/// </exception>
/// <exception cref="InvalidOperationException"> /// <exception cref="InvalidOperationException">
/// The response has already been sent. /// The response has already been sent.
/// </exception> /// </exception>
@ -344,9 +328,6 @@ namespace WebSocketSharp.Net
/// <exception cref="ArgumentException"> /// <exception cref="ArgumentException">
/// The value specified for a set operation is empty. /// The value specified for a set operation is empty.
/// </exception> /// </exception>
/// <exception cref="InvalidOperationException">
/// The response has already been sent.
/// </exception>
/// <exception cref="ObjectDisposedException"> /// <exception cref="ObjectDisposedException">
/// This object is closed. /// This object is closed.
/// </exception> /// </exception>
@ -356,8 +337,8 @@ namespace WebSocketSharp.Net
} }
set { set {
checkDisposedOrHeadersSent (); checkDisposed ();
if (value.Length == 0) if (value != null && value.Length == 0)
throw new ArgumentException ("An empty string.", "value"); throw new ArgumentException ("An empty string.", "value");
_location = value; _location = value;
@ -397,12 +378,12 @@ namespace WebSocketSharp.Net
/// <exception cref="InvalidOperationException"> /// <exception cref="InvalidOperationException">
/// The response has already been sent. /// The response has already been sent.
/// </exception> /// </exception>
/// <exception cref="System.Net.ProtocolViolationException">
/// The value specified for a set operation is invalid. Valid values are between 100 and 999.
/// </exception>
/// <exception cref="ObjectDisposedException"> /// <exception cref="ObjectDisposedException">
/// This object is closed. /// This object is closed.
/// </exception> /// </exception>
/// <exception cref="System.Net.ProtocolViolationException">
/// The value specified for a set operation is invalid. Valid values are between 100 and 999.
/// </exception>
public int StatusCode { public int StatusCode {
get { get {
return _statusCode; return _statusCode;
@ -424,6 +405,9 @@ namespace WebSocketSharp.Net
/// <value> /// <value>
/// A <see cref="string"/> that represents the description of the status code. /// A <see cref="string"/> that represents the description of the status code.
/// </value> /// </value>
/// <exception cref="ArgumentNullException">
/// The value specified for a set operation is <see langword="null"/>.
/// </exception>
/// <exception cref="InvalidOperationException"> /// <exception cref="InvalidOperationException">
/// The response has already been sent. /// The response has already been sent.
/// </exception> /// </exception>
@ -437,7 +421,10 @@ namespace WebSocketSharp.Net
set { set {
checkDisposedOrHeadersSent (); checkDisposedOrHeadersSent ();
_statusDescription = value != null && value.Length > 0 if (value == null)
throw new ArgumentNullException ("value");
_statusDescription = value.Length > 0
? value ? value
: _statusCode.GetStatusDescription (); : _statusCode.GetStatusDescription ();
} }
@ -479,12 +466,6 @@ namespace WebSocketSharp.Net
throw new InvalidOperationException ("Cannot be changed after the headers are sent."); throw new InvalidOperationException ("Cannot be changed after the headers are sent.");
} }
private void checkHeadersSent ()
{
if (_headersSent)
throw new InvalidOperationException ("Cannot be changed after the headers are sent.");
}
private void close (bool force) private void close (bool force)
{ {
_disposed = true; _disposed = true;
@ -510,8 +491,9 @@ namespace WebSocketSharp.Net
internal WebHeaderCollection WriteHeadersTo (MemoryStream destination, bool closing) internal WebHeaderCollection WriteHeadersTo (MemoryStream destination, bool closing)
{ {
var headers = new WebHeaderCollection (); var headers = new WebHeaderCollection (HttpHeaderType.Response, true);
headers.Add (_headers); if (_headers != null)
headers.Add (_headers);
if (_contentType != null) { if (_contentType != null) {
var type = _contentType.IndexOf ("charset=", StringComparison.Ordinal) == -1 && var type = _contentType.IndexOf ("charset=", StringComparison.Ordinal) == -1 &&
@ -546,13 +528,13 @@ namespace WebSocketSharp.Net
/* /*
* Apache forces closing the connection for these status codes: * Apache forces closing the connection for these status codes:
* - HttpStatusCode.BadRequest 400 * - 400 Bad Request
* - HttpStatusCode.RequestTimeout 408 * - 408 Request Timeout
* - HttpStatusCode.LengthRequired 411 * - 411 Length Required
* - HttpStatusCode.RequestEntityTooLarge 413 * - 413 Request Entity Too Large
* - HttpStatusCode.RequestUriTooLong 414 * - 414 Request-Uri Too Long
* - HttpStatusCode.InternalServerError 500 * - 500 Internal Server Error
* - HttpStatusCode.ServiceUnavailable 503 * - 503 Service Unavailable
*/ */
var closeConn = !_context.Request.KeepAlive || var closeConn = !_context.Request.KeepAlive ||
!_keepAlive || !_keepAlive ||
@ -620,6 +602,9 @@ namespace WebSocketSharp.Net
/// <param name="value"> /// <param name="value">
/// A <see cref="string"/> that represents the value of the header to add. /// A <see cref="string"/> that represents the value of the header to add.
/// </param> /// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="name"/> is <see langword="null"/> or empty.
/// </exception>
/// <exception cref="ArgumentException"> /// <exception cref="ArgumentException">
/// <para> /// <para>
/// <paramref name="name"/> or <paramref name="value"/> contains invalid characters. /// <paramref name="name"/> or <paramref name="value"/> contains invalid characters.
@ -631,30 +616,15 @@ namespace WebSocketSharp.Net
/// <paramref name="name"/> is a restricted header name. /// <paramref name="name"/> is a restricted header name.
/// </para> /// </para>
/// </exception> /// </exception>
/// <exception cref="ArgumentNullException">
/// <paramref name="name"/> is <see langword="null"/> or empty.
/// </exception>
/// <exception cref="ArgumentOutOfRangeException"> /// <exception cref="ArgumentOutOfRangeException">
/// The length of <paramref name="value"/> is greater than 65,535 characters. /// The length of <paramref name="value"/> is greater than 65,535 characters.
/// </exception> /// </exception>
/// <exception cref="InvalidOperationException"> /// <exception cref="InvalidOperationException">
/// <para> /// The header cannot be allowed to add to the current headers.
/// The response has already been sent.
/// </para>
/// <para>
/// -or-
/// </para>
/// <para>
/// The header cannot be allowed to add to the current headers.
/// </para>
/// </exception>
/// <exception cref="ObjectDisposedException">
/// This object is closed.
/// </exception> /// </exception>
public void AddHeader (string name, string value) public void AddHeader (string name, string value)
{ {
checkDisposedOrHeadersSent (); Headers.Set (name, value);
_headers.Set (name, value);
} }
/// <summary> /// <summary>
@ -666,15 +636,8 @@ namespace WebSocketSharp.Net
/// <exception cref="ArgumentNullException"> /// <exception cref="ArgumentNullException">
/// <paramref name="cookie"/> is <see langword="null"/>. /// <paramref name="cookie"/> is <see langword="null"/>.
/// </exception> /// </exception>
/// <exception cref="InvalidOperationException">
/// The response has already been sent.
/// </exception>
/// <exception cref="ObjectDisposedException">
/// This object is closed.
/// </exception>
public void AppendCookie (Cookie cookie) public void AppendCookie (Cookie cookie)
{ {
checkDisposedOrHeadersSent ();
Cookies.Add (cookie); Cookies.Add (cookie);
} }
@ -688,6 +651,9 @@ namespace WebSocketSharp.Net
/// <param name="value"> /// <param name="value">
/// A <see cref="string"/> that represents the value to append to the header. /// A <see cref="string"/> that represents the value to append to the header.
/// </param> /// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="name"/> is <see langword="null"/> or empty.
/// </exception>
/// <exception cref="ArgumentException"> /// <exception cref="ArgumentException">
/// <para> /// <para>
/// <paramref name="name"/> or <paramref name="value"/> contains invalid characters. /// <paramref name="name"/> or <paramref name="value"/> contains invalid characters.
@ -699,30 +665,15 @@ namespace WebSocketSharp.Net
/// <paramref name="name"/> is a restricted header name. /// <paramref name="name"/> is a restricted header name.
/// </para> /// </para>
/// </exception> /// </exception>
/// <exception cref="ArgumentNullException">
/// <paramref name="name"/> is <see langword="null"/> or empty.
/// </exception>
/// <exception cref="ArgumentOutOfRangeException"> /// <exception cref="ArgumentOutOfRangeException">
/// The length of <paramref name="value"/> is greater than 65,535 characters. /// The length of <paramref name="value"/> is greater than 65,535 characters.
/// </exception> /// </exception>
/// <exception cref="InvalidOperationException"> /// <exception cref="InvalidOperationException">
/// <para> /// The current headers cannot allow the header to append a value.
/// The response has already been sent.
/// </para>
/// <para>
/// -or-
/// </para>
/// <para>
/// The current headers cannot allow the header to append a value.
/// </para>
/// </exception>
/// <exception cref="ObjectDisposedException">
/// This object is closed.
/// </exception> /// </exception>
public void AppendHeader (string name, string value) public void AppendHeader (string name, string value)
{ {
checkDisposedOrHeadersSent (); Headers.Add (name, value);
_headers.Add (name, value);
} }
/// <summary> /// <summary>
@ -781,24 +732,32 @@ namespace WebSocketSharp.Net
} }
/// <summary> /// <summary>
/// Copies properties from the specified <see cref="HttpListenerResponse"/> to this response. /// Copies some properties from the specified <see cref="HttpListenerResponse"/> to
/// this response.
/// </summary> /// </summary>
/// <param name="templateResponse"> /// <param name="templateResponse">
/// A <see cref="HttpListenerResponse"/> to copy. /// A <see cref="HttpListenerResponse"/> to copy.
/// </param> /// </param>
/// <exception cref="InvalidOperationException"> /// <exception cref="ArgumentNullException">
/// The response has already been sent. /// <paramref name="templateResponse"/> is <see langword="null"/>.
/// </exception>
/// <exception cref="ObjectDisposedException">
/// This object is closed.
/// </exception> /// </exception>
public void CopyFrom (HttpListenerResponse templateResponse) public void CopyFrom (HttpListenerResponse templateResponse)
{ {
checkDisposedOrHeadersSent (); if (templateResponse == null)
throw new ArgumentNullException ("templateResponse");
if (templateResponse._headers != null) {
if (_headers != null)
_headers.Clear ();
Headers.Add (templateResponse._headers);
}
else if (_headers != null) {
_headers = null;
}
_headers.Clear ();
_headers.Add (templateResponse._headers);
_contentLength = templateResponse._contentLength; _contentLength = templateResponse._contentLength;
_contentLengthSet = templateResponse._contentLengthSet;
_statusCode = templateResponse._statusCode; _statusCode = templateResponse._statusCode;
_statusDescription = templateResponse._statusDescription; _statusDescription = templateResponse._statusDescription;
_keepAlive = templateResponse._keepAlive; _keepAlive = templateResponse._keepAlive;
@ -806,22 +765,29 @@ namespace WebSocketSharp.Net
} }
/// <summary> /// <summary>
/// Configures the response to redirect the client's request to the specified /// Configures the response to redirect the client's request to
/// <paramref name="url"/>. /// the specified <paramref name="url"/>.
/// </summary> /// </summary>
/// <param name="url"> /// <param name="url">
/// A <see cref="string"/> that represents the URL to redirect the client's request to. /// A <see cref="string"/> that represents the URL to redirect the client's request to.
/// </param> /// </param>
/// <exception cref="InvalidOperationException"> /// <exception cref="ArgumentNullException">
/// The response has already been sent. /// <paramref name="url"/> is <see langword="null"/>.
/// </exception> /// </exception>
/// <exception cref="ObjectDisposedException"> /// <exception cref="ArgumentException">
/// This object is closed. /// <paramref name="url"/> is empty.
/// </exception> /// </exception>
public void Redirect (string url) public void Redirect (string url)
{ {
StatusCode = (int) HttpStatusCode.Redirect; if (url == null)
throw new ArgumentNullException ("url");
if (url.Length == 0)
throw new ArgumentException ("An empty string.", "url");
_location = url; _location = url;
_statusCode = 302;
_statusDescription = "Found";
} }
/// <summary> /// <summary>
@ -830,21 +796,14 @@ namespace WebSocketSharp.Net
/// <param name="cookie"> /// <param name="cookie">
/// A <see cref="Cookie"/> to set. /// A <see cref="Cookie"/> to set.
/// </param> /// </param>
/// <exception cref="ArgumentException">
/// <paramref name="cookie"/> already exists in the cookies and couldn't be replaced.
/// </exception>
/// <exception cref="ArgumentNullException"> /// <exception cref="ArgumentNullException">
/// <paramref name="cookie"/> is <see langword="null"/>. /// <paramref name="cookie"/> is <see langword="null"/>.
/// </exception> /// </exception>
/// <exception cref="InvalidOperationException"> /// <exception cref="ArgumentException">
/// The response has already been sent. /// <paramref name="cookie"/> already exists in the cookies and couldn't be replaced.
/// </exception>
/// <exception cref="ObjectDisposedException">
/// This object is closed.
/// </exception> /// </exception>
public void SetCookie (Cookie cookie) public void SetCookie (Cookie cookie)
{ {
checkDisposedOrHeadersSent ();
if (cookie == null) if (cookie == null)
throw new ArgumentNullException ("cookie"); throw new ArgumentNullException ("cookie");

View File

@ -130,7 +130,7 @@ namespace WebSocketSharp.Net
if (!_response.HeadersSent) { if (!_response.HeadersSent) {
if (!flushHeaders (closing)) { if (!flushHeaders (closing)) {
if (closing) if (closing)
_response.Headers.InternalSet ("Connection", "close", true); _response.CloseConnection = true;
return false; return false;
} }
@ -181,7 +181,7 @@ namespace WebSocketSharp.Net
return false; return false;
_write (buff.GetBuffer (), (int) start, (int) len); _write (buff.GetBuffer (), (int) start, (int) len);
_response.Headers = headers; _response.CloseConnection = headers["Connection"] == "close";
_response.HeadersSent = true; _response.HeadersSent = true;
} }