912 lines
24 KiB
C#
912 lines
24 KiB
C#
#region License
|
|
/*
|
|
* HttpListenerRequest.cs
|
|
*
|
|
* This code is derived from HttpListenerRequest.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-2018 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.Collections.Generic;
|
|
using System.Collections.Specialized;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.Security.Cryptography.X509Certificates;
|
|
using System.Text;
|
|
|
|
namespace WebSocketSharp.Net
|
|
{
|
|
/// <summary>
|
|
/// Represents an incoming HTTP request to a <see cref="HttpListener"/>
|
|
/// instance.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This class cannot be inherited.
|
|
/// </remarks>
|
|
public sealed class HttpListenerRequest
|
|
{
|
|
#region Private Fields
|
|
|
|
private static readonly byte[] _100continue;
|
|
private string[] _acceptTypes;
|
|
private bool _chunked;
|
|
private HttpConnection _connection;
|
|
private Encoding _contentEncoding;
|
|
private long _contentLength;
|
|
private HttpListenerContext _context;
|
|
private CookieCollection _cookies;
|
|
private WebHeaderCollection _headers;
|
|
private string _httpMethod;
|
|
private Stream _inputStream;
|
|
private Version _protocolVersion;
|
|
private NameValueCollection _queryString;
|
|
private string _rawUrl;
|
|
private Guid _requestTraceIdentifier;
|
|
private Uri _url;
|
|
private Uri _urlReferrer;
|
|
private bool _urlSet;
|
|
private string _userHostName;
|
|
private string[] _userLanguages;
|
|
|
|
#endregion
|
|
|
|
#region Static Constructor
|
|
|
|
static HttpListenerRequest ()
|
|
{
|
|
_100continue = Encoding.ASCII.GetBytes ("HTTP/1.1 100 Continue\r\n\r\n");
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Internal Constructors
|
|
|
|
internal HttpListenerRequest (HttpListenerContext context)
|
|
{
|
|
_context = context;
|
|
|
|
_connection = context.Connection;
|
|
_contentLength = -1;
|
|
_headers = new WebHeaderCollection ();
|
|
_requestTraceIdentifier = Guid.NewGuid ();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public Properties
|
|
|
|
/// <summary>
|
|
/// Gets the media types that are acceptable for the client.
|
|
/// </summary>
|
|
/// <value>
|
|
/// <para>
|
|
/// An array of <see cref="string"/> that contains the names of the media
|
|
/// types specified in the value of the Accept header.
|
|
/// </para>
|
|
/// <para>
|
|
/// <see langword="null"/> if the header is not present.
|
|
/// </para>
|
|
/// </value>
|
|
public string[] AcceptTypes {
|
|
get {
|
|
var val = _headers["Accept"];
|
|
if (val == null)
|
|
return null;
|
|
|
|
if (_acceptTypes == null) {
|
|
_acceptTypes = val
|
|
.SplitHeaderValue (',')
|
|
.Trim ()
|
|
.ToList ()
|
|
.ToArray ();
|
|
}
|
|
|
|
return _acceptTypes;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets an error code that identifies a problem with the certificate
|
|
/// provided by the client.
|
|
/// </summary>
|
|
/// <value>
|
|
/// An <see cref="int"/> that represents an error code.
|
|
/// </value>
|
|
/// <exception cref="NotSupportedException">
|
|
/// This property is not supported.
|
|
/// </exception>
|
|
public int ClientCertificateError {
|
|
get {
|
|
throw new NotSupportedException ();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the encoding for the entity body data included in the request.
|
|
/// </summary>
|
|
/// <value>
|
|
/// <para>
|
|
/// A <see cref="Encoding"/> converted from the charset value of the
|
|
/// Content-Type header.
|
|
/// </para>
|
|
/// <para>
|
|
/// <see cref="Encoding.UTF8"/> if the charset value is not available.
|
|
/// </para>
|
|
/// </value>
|
|
public Encoding ContentEncoding {
|
|
get {
|
|
if (_contentEncoding == null)
|
|
_contentEncoding = getContentEncoding () ?? Encoding.UTF8;
|
|
|
|
return _contentEncoding;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the length in bytes of the entity body data included in the
|
|
/// request.
|
|
/// </summary>
|
|
/// <value>
|
|
/// <para>
|
|
/// A <see cref="long"/> converted from the value of the Content-Length
|
|
/// header.
|
|
/// </para>
|
|
/// <para>
|
|
/// -1 if the header is not present.
|
|
/// </para>
|
|
/// </value>
|
|
public long ContentLength64 {
|
|
get {
|
|
return _contentLength;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the media type of the entity body data included in the request.
|
|
/// </summary>
|
|
/// <value>
|
|
/// <para>
|
|
/// A <see cref="string"/> that represents the value of the Content-Type
|
|
/// header.
|
|
/// </para>
|
|
/// <para>
|
|
/// <see langword="null"/> if the header is not present.
|
|
/// </para>
|
|
/// </value>
|
|
public string ContentType {
|
|
get {
|
|
return _headers["Content-Type"];
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the cookies included in the request.
|
|
/// </summary>
|
|
/// <value>
|
|
/// <para>
|
|
/// A <see cref="CookieCollection"/> that contains the cookies.
|
|
/// </para>
|
|
/// <para>
|
|
/// An empty collection if not included.
|
|
/// </para>
|
|
/// </value>
|
|
public CookieCollection Cookies {
|
|
get {
|
|
if (_cookies == null)
|
|
_cookies = _headers.GetCookies (false);
|
|
|
|
return _cookies;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether the request has the entity body data.
|
|
/// </summary>
|
|
/// <value>
|
|
/// <c>true</c> if the request has the entity body data; otherwise,
|
|
/// <c>false</c>.
|
|
/// </value>
|
|
public bool HasEntityBody {
|
|
get {
|
|
return _contentLength > 0 || _chunked;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the headers included in the request.
|
|
/// </summary>
|
|
/// <value>
|
|
/// A <see cref="NameValueCollection"/> that contains the headers.
|
|
/// </value>
|
|
public NameValueCollection Headers {
|
|
get {
|
|
return _headers;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the HTTP method specified by the client.
|
|
/// </summary>
|
|
/// <value>
|
|
/// A <see cref="string"/> that represents the HTTP method specified in
|
|
/// the request line.
|
|
/// </value>
|
|
public string HttpMethod {
|
|
get {
|
|
return _httpMethod;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a stream that contains the entity body data included in
|
|
/// the request.
|
|
/// </summary>
|
|
/// <value>
|
|
/// <para>
|
|
/// A <see cref="Stream"/> that contains the entity body data.
|
|
/// </para>
|
|
/// <para>
|
|
/// <see cref="Stream.Null"/> if the entity body data is not available.
|
|
/// </para>
|
|
/// </value>
|
|
public Stream InputStream {
|
|
get {
|
|
if (_inputStream == null)
|
|
_inputStream = getInputStream () ?? Stream.Null;
|
|
|
|
return _inputStream;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether the client is authenticated.
|
|
/// </summary>
|
|
/// <value>
|
|
/// <c>true</c> if the client is authenticated; otherwise, <c>false</c>.
|
|
/// </value>
|
|
public bool IsAuthenticated {
|
|
get {
|
|
return _context.User != null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether the request is sent from the local
|
|
/// computer.
|
|
/// </summary>
|
|
/// <value>
|
|
/// <c>true</c> if the request is sent from the same computer as the server;
|
|
/// otherwise, <c>false</c>.
|
|
/// </value>
|
|
public bool IsLocal {
|
|
get {
|
|
return _connection.IsLocal;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether a secure connection is used to send
|
|
/// the request.
|
|
/// </summary>
|
|
/// <value>
|
|
/// <c>true</c> if the connection is secure; otherwise, <c>false</c>.
|
|
/// </value>
|
|
public bool IsSecureConnection {
|
|
get {
|
|
return _connection.IsSecure;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether the request is a WebSocket handshake
|
|
/// request.
|
|
/// </summary>
|
|
/// <value>
|
|
/// <c>true</c> if the request is a WebSocket handshake request; otherwise,
|
|
/// <c>false</c>.
|
|
/// </value>
|
|
public bool IsWebSocketRequest {
|
|
get {
|
|
return _httpMethod == "GET"
|
|
&& _protocolVersion > HttpVersion.Version10
|
|
&& _headers.Upgrades ("websocket");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether a persistent connection is requested.
|
|
/// </summary>
|
|
/// <value>
|
|
/// <c>true</c> if the request specifies that the connection is kept open;
|
|
/// otherwise, <c>false</c>.
|
|
/// </value>
|
|
public bool KeepAlive {
|
|
get {
|
|
return _headers.KeepsAlive (_protocolVersion);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the endpoint to which the request is sent.
|
|
/// </summary>
|
|
/// <value>
|
|
/// A <see cref="System.Net.IPEndPoint"/> that represents the server IP
|
|
/// address and port number.
|
|
/// </value>
|
|
public System.Net.IPEndPoint LocalEndPoint {
|
|
get {
|
|
return _connection.LocalEndPoint;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the HTTP version specified by the client.
|
|
/// </summary>
|
|
/// <value>
|
|
/// A <see cref="Version"/> that represents the HTTP version specified in
|
|
/// the request line.
|
|
/// </value>
|
|
public Version ProtocolVersion {
|
|
get {
|
|
return _protocolVersion;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the query string included in the request.
|
|
/// </summary>
|
|
/// <value>
|
|
/// <para>
|
|
/// A <see cref="NameValueCollection"/> that contains the query
|
|
/// parameters.
|
|
/// </para>
|
|
/// <para>
|
|
/// An empty collection if not included.
|
|
/// </para>
|
|
/// </value>
|
|
public NameValueCollection QueryString {
|
|
get {
|
|
if (_queryString == null) {
|
|
var url = Url;
|
|
_queryString = QueryStringCollection.Parse (
|
|
url != null ? url.Query : null,
|
|
Encoding.UTF8
|
|
);
|
|
}
|
|
|
|
return _queryString;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the raw URL specified by the client.
|
|
/// </summary>
|
|
/// <value>
|
|
/// A <see cref="string"/> that represents the request target specified in
|
|
/// the request line.
|
|
/// </value>
|
|
public string RawUrl {
|
|
get {
|
|
return _rawUrl;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the endpoint from which the request is sent.
|
|
/// </summary>
|
|
/// <value>
|
|
/// A <see cref="System.Net.IPEndPoint"/> that represents the client IP
|
|
/// address and port number.
|
|
/// </value>
|
|
public System.Net.IPEndPoint RemoteEndPoint {
|
|
get {
|
|
return _connection.RemoteEndPoint;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the trace identifier of the request.
|
|
/// </summary>
|
|
/// <value>
|
|
/// A <see cref="Guid"/> that represents the trace identifier.
|
|
/// </value>
|
|
public Guid RequestTraceIdentifier {
|
|
get {
|
|
return _requestTraceIdentifier;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the URL requested by the client.
|
|
/// </summary>
|
|
/// <value>
|
|
/// <para>
|
|
/// A <see cref="Uri"/> that represents the URL parsed from the request.
|
|
/// </para>
|
|
/// <para>
|
|
/// <see langword="null"/> if the URL cannot be parsed.
|
|
/// </para>
|
|
/// </value>
|
|
public Uri Url {
|
|
get {
|
|
if (!_urlSet) {
|
|
_url = HttpUtility.CreateRequestUrl (
|
|
_rawUrl,
|
|
_userHostName ?? UserHostAddress,
|
|
IsWebSocketRequest,
|
|
IsSecureConnection
|
|
);
|
|
|
|
_urlSet = true;
|
|
}
|
|
|
|
return _url;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the URI of the resource from which the requested URL was obtained.
|
|
/// </summary>
|
|
/// <value>
|
|
/// <para>
|
|
/// A <see cref="Uri"/> converted from the value of the Referer header.
|
|
/// </para>
|
|
/// <para>
|
|
/// <see langword="null"/> if the header value is not available.
|
|
/// </para>
|
|
/// </value>
|
|
public Uri UrlReferrer {
|
|
get {
|
|
var val = _headers["Referer"];
|
|
if (val == null)
|
|
return null;
|
|
|
|
if (_urlReferrer == null)
|
|
_urlReferrer = val.ToUri ();
|
|
|
|
return _urlReferrer;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the user agent from which the request is originated.
|
|
/// </summary>
|
|
/// <value>
|
|
/// <para>
|
|
/// A <see cref="string"/> that represents the value of the User-Agent
|
|
/// header.
|
|
/// </para>
|
|
/// <para>
|
|
/// <see langword="null"/> if the header is not present.
|
|
/// </para>
|
|
/// </value>
|
|
public string UserAgent {
|
|
get {
|
|
return _headers["User-Agent"];
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the IP address and port number to which the request is sent.
|
|
/// </summary>
|
|
/// <value>
|
|
/// A <see cref="string"/> that represents the server IP address and port
|
|
/// number.
|
|
/// </value>
|
|
public string UserHostAddress {
|
|
get {
|
|
return _connection.LocalEndPoint.ToString ();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the server host name requested by the client.
|
|
/// </summary>
|
|
/// <value>
|
|
/// <para>
|
|
/// A <see cref="string"/> that represents the value of the Host header.
|
|
/// </para>
|
|
/// <para>
|
|
/// It includes the port number if provided.
|
|
/// </para>
|
|
/// <para>
|
|
/// <see langword="null"/> if the header is not present.
|
|
/// </para>
|
|
/// </value>
|
|
public string UserHostName {
|
|
get {
|
|
return _userHostName;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the natural languages that are acceptable for the client.
|
|
/// </summary>
|
|
/// <value>
|
|
/// <para>
|
|
/// An array of <see cref="string"/> that contains the names of the
|
|
/// natural languages specified in the value of the Accept-Language
|
|
/// header.
|
|
/// </para>
|
|
/// <para>
|
|
/// <see langword="null"/> if the header is not present.
|
|
/// </para>
|
|
/// </value>
|
|
public string[] UserLanguages {
|
|
get {
|
|
var val = _headers["Accept-Language"];
|
|
if (val == null)
|
|
return null;
|
|
|
|
if (_userLanguages == null)
|
|
_userLanguages = val.Split (',').Trim ().ToList ().ToArray ();
|
|
|
|
return _userLanguages;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private Methods
|
|
|
|
private void finishInitialization10 ()
|
|
{
|
|
var transferEnc = _headers["Transfer-Encoding"];
|
|
if (transferEnc != null) {
|
|
_context.ErrorMessage = "Invalid Transfer-Encoding header";
|
|
return;
|
|
}
|
|
|
|
if (_httpMethod == "POST") {
|
|
if (_contentLength == -1) {
|
|
_context.ErrorMessage = "Content-Length header required";
|
|
return;
|
|
}
|
|
|
|
if (_contentLength == 0) {
|
|
_context.ErrorMessage = "Invalid Content-Length header";
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
private Encoding getContentEncoding ()
|
|
{
|
|
var val = _headers["Content-Type"];
|
|
if (val == null)
|
|
return null;
|
|
|
|
Encoding ret;
|
|
HttpUtility.TryGetEncoding (val, out ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
private RequestStream getInputStream ()
|
|
{
|
|
return _contentLength > 0 || _chunked
|
|
? _connection.GetRequestStream (_contentLength, _chunked)
|
|
: null;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Internal Methods
|
|
|
|
internal void AddHeader (string headerField)
|
|
{
|
|
var start = headerField[0];
|
|
if (start == ' ' || start == '\t') {
|
|
_context.ErrorMessage = "Invalid header field";
|
|
return;
|
|
}
|
|
|
|
var colon = headerField.IndexOf (':');
|
|
if (colon < 1) {
|
|
_context.ErrorMessage = "Invalid header field";
|
|
return;
|
|
}
|
|
|
|
var name = headerField.Substring (0, colon).Trim ();
|
|
if (name.Length == 0 || !name.IsToken ()) {
|
|
_context.ErrorMessage = "Invalid header name";
|
|
return;
|
|
}
|
|
|
|
var val = colon < headerField.Length - 1
|
|
? headerField.Substring (colon + 1).Trim ()
|
|
: String.Empty;
|
|
|
|
_headers.InternalSet (name, val, false);
|
|
|
|
var lower = name.ToLower (CultureInfo.InvariantCulture);
|
|
if (lower == "host") {
|
|
if (_userHostName != null) {
|
|
_context.ErrorMessage = "Invalid Host header";
|
|
return;
|
|
}
|
|
|
|
if (val.Length == 0) {
|
|
_context.ErrorMessage = "Invalid Host header";
|
|
return;
|
|
}
|
|
|
|
_userHostName = val;
|
|
return;
|
|
}
|
|
|
|
if (lower == "content-length") {
|
|
if (_contentLength > -1) {
|
|
_context.ErrorMessage = "Invalid Content-Length header";
|
|
return;
|
|
}
|
|
|
|
long len;
|
|
if (!Int64.TryParse (val, out len)) {
|
|
_context.ErrorMessage = "Invalid Content-Length header";
|
|
return;
|
|
}
|
|
|
|
if (len < 0) {
|
|
_context.ErrorMessage = "Invalid Content-Length header";
|
|
return;
|
|
}
|
|
|
|
_contentLength = len;
|
|
return;
|
|
}
|
|
}
|
|
|
|
internal void FinishInitialization ()
|
|
{
|
|
if (_protocolVersion == HttpVersion.Version10) {
|
|
finishInitialization10 ();
|
|
return;
|
|
}
|
|
|
|
if (_userHostName == null) {
|
|
_context.ErrorMessage = "Host header required";
|
|
return;
|
|
}
|
|
|
|
var transferEnc = _headers["Transfer-Encoding"];
|
|
if (transferEnc != null) {
|
|
var comparison = StringComparison.OrdinalIgnoreCase;
|
|
if (!transferEnc.Equals ("chunked", comparison)) {
|
|
_context.ErrorMessage = String.Empty;
|
|
_context.ErrorStatus = 501;
|
|
|
|
return;
|
|
}
|
|
|
|
_chunked = true;
|
|
}
|
|
|
|
if (_httpMethod == "POST" || _httpMethod == "PUT") {
|
|
if (_contentLength <= 0 && !_chunked) {
|
|
_context.ErrorMessage = String.Empty;
|
|
_context.ErrorStatus = 411;
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
var expect = _headers["Expect"];
|
|
if (expect != null) {
|
|
var comparison = StringComparison.OrdinalIgnoreCase;
|
|
if (!expect.Equals ("100-continue", comparison)) {
|
|
_context.ErrorMessage = "Invalid Expect header";
|
|
return;
|
|
}
|
|
|
|
var output = _connection.GetResponseStream ();
|
|
output.InternalWrite (_100continue, 0, _100continue.Length);
|
|
}
|
|
}
|
|
|
|
internal bool FlushInput ()
|
|
{
|
|
var input = InputStream;
|
|
if (input == Stream.Null)
|
|
return true;
|
|
|
|
var len = 2048;
|
|
if (_contentLength > 0 && _contentLength < len)
|
|
len = (int) _contentLength;
|
|
|
|
var buff = new byte[len];
|
|
|
|
while (true) {
|
|
try {
|
|
var ares = input.BeginRead (buff, 0, len, null, null);
|
|
if (!ares.IsCompleted) {
|
|
var timeout = 100;
|
|
if (!ares.AsyncWaitHandle.WaitOne (timeout))
|
|
return false;
|
|
}
|
|
|
|
if (input.EndRead (ares) <= 0)
|
|
return true;
|
|
}
|
|
catch {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal bool IsUpgradeRequest (string protocol)
|
|
{
|
|
return _headers.Upgrades (protocol);
|
|
}
|
|
|
|
internal void SetRequestLine (string requestLine)
|
|
{
|
|
var parts = requestLine.Split (new[] { ' ' }, 3);
|
|
if (parts.Length < 3) {
|
|
_context.ErrorMessage = "Invalid request line (parts)";
|
|
return;
|
|
}
|
|
|
|
var method = parts[0];
|
|
if (method.Length == 0) {
|
|
_context.ErrorMessage = "Invalid request line (method)";
|
|
return;
|
|
}
|
|
|
|
var target = parts[1];
|
|
if (target.Length == 0) {
|
|
_context.ErrorMessage = "Invalid request line (target)";
|
|
return;
|
|
}
|
|
|
|
var rawVer = parts[2];
|
|
if (rawVer.Length != 8) {
|
|
_context.ErrorMessage = "Invalid request line (version)";
|
|
return;
|
|
}
|
|
|
|
if (rawVer.IndexOf ("HTTP/") != 0) {
|
|
_context.ErrorMessage = "Invalid request line (version)";
|
|
return;
|
|
}
|
|
|
|
Version ver;
|
|
if (!rawVer.Substring (5).TryCreateVersion (out ver)) {
|
|
_context.ErrorMessage = "Invalid request line (version)";
|
|
return;
|
|
}
|
|
|
|
if (ver.Major < 1) {
|
|
_context.ErrorMessage = "Invalid request line (version)";
|
|
return;
|
|
}
|
|
|
|
if (!method.IsHttpMethod (ver)) {
|
|
_context.ErrorMessage = "Invalid request line (method)";
|
|
return;
|
|
}
|
|
|
|
_httpMethod = method;
|
|
_rawUrl = target;
|
|
_protocolVersion = ver;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public Methods
|
|
|
|
/// <summary>
|
|
/// Begins getting the certificate provided by the client asynchronously.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// An <see cref="IAsyncResult"/> instance that indicates the status of the
|
|
/// operation.
|
|
/// </returns>
|
|
/// <param name="requestCallback">
|
|
/// An <see cref="AsyncCallback"/> delegate that invokes the method called
|
|
/// when the operation is complete.
|
|
/// </param>
|
|
/// <param name="state">
|
|
/// An <see cref="object"/> that represents a user defined object to pass to
|
|
/// the callback delegate.
|
|
/// </param>
|
|
/// <exception cref="NotSupportedException">
|
|
/// This method is not supported.
|
|
/// </exception>
|
|
public IAsyncResult BeginGetClientCertificate (
|
|
AsyncCallback requestCallback, object state
|
|
)
|
|
{
|
|
throw new NotSupportedException ();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ends an asynchronous operation to get the certificate provided by the
|
|
/// client.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// A <see cref="X509Certificate2"/> that represents an X.509 certificate
|
|
/// provided by the client.
|
|
/// </returns>
|
|
/// <param name="asyncResult">
|
|
/// An <see cref="IAsyncResult"/> instance returned when the operation
|
|
/// started.
|
|
/// </param>
|
|
/// <exception cref="NotSupportedException">
|
|
/// This method is not supported.
|
|
/// </exception>
|
|
public X509Certificate2 EndGetClientCertificate (IAsyncResult asyncResult)
|
|
{
|
|
throw new NotSupportedException ();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the certificate provided by the client.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// A <see cref="X509Certificate2"/> that represents an X.509 certificate
|
|
/// provided by the client.
|
|
/// </returns>
|
|
/// <exception cref="NotSupportedException">
|
|
/// This method is not supported.
|
|
/// </exception>
|
|
public X509Certificate2 GetClientCertificate ()
|
|
{
|
|
throw new NotSupportedException ();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a string that represents the current instance.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// A <see cref="string"/> that contains the request line and headers
|
|
/// included in the request.
|
|
/// </returns>
|
|
public override string ToString ()
|
|
{
|
|
var buff = new StringBuilder (64);
|
|
|
|
buff
|
|
.AppendFormat (
|
|
"{0} {1} HTTP/{2}\r\n", _httpMethod, _rawUrl, _protocolVersion
|
|
)
|
|
.Append (_headers.ToString ());
|
|
|
|
return buff.ToString ();
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|