//-----------------------------------------------------------------------
//
// Copyright (c) Outercurve Foundation. All rights reserved.
//
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.Messaging {
using System;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Mime;
using System.ServiceModel.Channels;
using System.Web;
using Validation;
///
/// A property store of details of an incoming HTTP request.
///
///
/// This serves a very similar purpose to , except that
/// ASP.NET does not let us fully initialize that class, so we have to write one
/// of our one.
///
public class HttpRequestInfo : HttpRequestBase {
///
/// The HTTP verb in the request.
///
private readonly string httpMethod;
///
/// The full request URL.
///
private readonly Uri requestUri;
///
/// The HTTP headers.
///
private readonly NameValueCollection headers;
///
/// The variables defined in the query part of the URL.
///
private readonly NameValueCollection queryString;
///
/// The POSTed form variables.
///
private readonly NameValueCollection form;
///
/// The server variables collection.
///
private readonly NameValueCollection serverVariables;
///
/// The backing field for the property.
///
private readonly HttpCookieCollection cookies;
///
/// Initializes a new instance of the class.
///
/// The request.
/// The request URI.
internal HttpRequestInfo(HttpRequestMessageProperty request, Uri requestUri) {
Requires.NotNull(request, "request");
Requires.NotNull(requestUri, "requestUri");
this.httpMethod = request.Method;
this.headers = request.Headers;
this.requestUri = requestUri;
this.form = new NameValueCollection();
this.queryString = HttpUtility.ParseQueryString(requestUri.Query);
this.serverVariables = new NameValueCollection();
this.cookies = new HttpCookieCollection();
Reporting.RecordRequestStatistics(this);
}
///
/// Initializes a new instance of the class.
///
/// The HTTP method.
/// The request URI.
/// The form variables.
/// The HTTP headers.
/// The cookies in the request.
internal HttpRequestInfo(string httpMethod, Uri requestUri, NameValueCollection form = null, NameValueCollection headers = null, HttpCookieCollection cookies = null) {
Requires.NotNullOrEmpty(httpMethod, "httpMethod");
Requires.NotNull(requestUri, "requestUri");
this.httpMethod = httpMethod;
this.requestUri = requestUri;
this.form = form ?? new NameValueCollection();
this.queryString = HttpUtility.ParseQueryString(requestUri.Query);
this.headers = headers ?? new WebHeaderCollection();
this.serverVariables = new NameValueCollection();
this.cookies = cookies ?? new HttpCookieCollection();
}
///
/// Initializes a new instance of the class.
///
/// Details on the incoming HTTP request.
internal HttpRequestInfo(HttpListenerRequest listenerRequest) {
Requires.NotNull(listenerRequest, "listenerRequest");
this.httpMethod = listenerRequest.HttpMethod;
this.requestUri = listenerRequest.Url;
this.queryString = listenerRequest.QueryString;
this.headers = listenerRequest.Headers;
this.form = ParseFormData(listenerRequest.HttpMethod, listenerRequest.Headers, () => listenerRequest.InputStream);
this.serverVariables = new NameValueCollection();
this.cookies = new HttpCookieCollection();
Reporting.RecordRequestStatistics(this);
}
///
/// Initializes a new instance of the class.
///
/// The request.
internal HttpRequestInfo(HttpRequestMessage request) {
Requires.NotNull(request, "request");
this.httpMethod = request.Method.ToString();
this.requestUri = request.RequestUri;
this.queryString = HttpUtility.ParseQueryString(request.RequestUri.Query);
this.headers = new NameValueCollection();
AddHeaders(this.headers, request.Headers);
AddHeaders(this.headers, request.Content.Headers);
this.form = ParseFormData(this.httpMethod, this.headers, () => request.Content.ReadAsStreamAsync().Result);
this.serverVariables = new NameValueCollection();
this.cookies = new HttpCookieCollection();
Reporting.RecordRequestStatistics(this);
}
///
/// Initializes a new instance of the class.
///
/// The HTTP method.
/// The request URI.
/// The headers.
/// The input stream.
internal HttpRequestInfo(string httpMethod, Uri requestUri, NameValueCollection headers, Stream inputStream) {
Requires.NotNullOrEmpty(httpMethod, "httpMethod");
Requires.NotNull(requestUri, "requestUri");
this.httpMethod = httpMethod;
this.requestUri = requestUri;
this.headers = headers;
this.queryString = HttpUtility.ParseQueryString(requestUri.Query);
this.form = ParseFormData(httpMethod, headers, () => inputStream);
this.serverVariables = new NameValueCollection();
this.cookies = new HttpCookieCollection();
Reporting.RecordRequestStatistics(this);
}
///
/// Gets the HTTP method.
///
public override string HttpMethod {
get { return this.httpMethod; }
}
///
/// Gets the headers.
///
public override NameValueCollection Headers {
get { return this.headers; }
}
///
/// Gets the URL.
///
public override Uri Url {
get { return this.requestUri; }
}
///
/// Gets the raw URL.
///
public override string RawUrl {
get { return this.requestUri.AbsolutePath + this.requestUri.Query; }
}
///
/// Gets the form.
///
public override NameValueCollection Form {
get { return this.form; }
}
///
/// Gets the query string.
///
public override NameValueCollection QueryString {
get { return this.queryString; }
}
///
/// Gets the server variables.
///
public override NameValueCollection ServerVariables {
get { return this.serverVariables; }
}
///
/// Gets the collection of cookies that were sent by the client.
///
/// The client's cookies.
public override HttpCookieCollection Cookies {
get { return this.cookies; }
}
///
/// When overridden in a derived class, gets an array of client-supported MIME accept types.
///
/// An array of client-supported MIME accept types.
public override string[] AcceptTypes {
get {
if (this.Headers["Accept"] != null) {
return this.headers["Accept"].Split(',');
}
return new string[0];
}
}
///
/// When overridden in a derived class, gets information about the URL of the client request that linked to the current URL.
///
/// The URL of the page that linked to the current request.
public override Uri UrlReferrer {
get {
if (this.Headers["Referer"] != null) { // misspelled word intentional, per RFC
return new Uri(this.Headers["Referer"]);
}
return null;
}
}
///
/// When overridden in a derived class, gets the length, in bytes, of content that was sent by the client.
///
/// The length, in bytes, of content that was sent by the client.
public override int ContentLength {
get {
if (this.Headers["Content-Length"] != null) {
return int.Parse(this.headers["Content-Length"]);
}
return 0;
}
}
///
/// When overridden in a derived class, gets or sets the MIME content type of the request.
///
/// The MIME content type of the request, such as "text/html".
/// Setter always throws
public override string ContentType {
get { return this.Headers["Content-Type"]; }
set { throw new NotImplementedException(); }
}
///
/// Creates an instance that describes the specified HTTP request.
///
/// The request.
/// The request URI.
/// An instance of .
public static HttpRequestBase Create(HttpRequestMessageProperty request, Uri requestUri) {
return new HttpRequestInfo(request, requestUri);
}
///
/// Creates an instance that describes the specified HTTP request.
///
/// The listener request.
/// An instance of .
public static HttpRequestBase Create(HttpListenerRequest listenerRequest) {
return new HttpRequestInfo(listenerRequest);
}
#if CLR4
///
/// Creates an instance that describes the specified HTTP request.
///
/// The HTTP request.
/// An instance of .
public static HttpRequestBase Create(HttpRequestMessage request) {
return new HttpRequestInfo(request);
}
#endif
///
/// Creates an instance that describes the specified HTTP request.
///
/// The HTTP method.
/// The request URI.
/// The form variables.
/// The HTTP headers.
/// An instance of .
public static HttpRequestBase Create(string httpMethod, Uri requestUri, NameValueCollection form = null, NameValueCollection headers = null) {
return new HttpRequestInfo(httpMethod, requestUri, form, headers);
}
///
/// Creates an instance that describes the specified HTTP request.
///
/// The HTTP method.
/// The request URI.
/// The headers.
/// The input stream.
/// An instance of .
public static HttpRequestBase Create(string httpMethod, Uri requestUri, NameValueCollection headers, Stream inputStream) {
return new HttpRequestInfo(httpMethod, requestUri, headers, inputStream);
}
///
/// Reads name=value pairs from the POSTed form entity when the HTTP headers indicate that that is the payload of the entity.
///
/// The HTTP method.
/// The headers.
/// A function that returns the input stream.
/// The non-null collection of form variables.
private static NameValueCollection ParseFormData(string httpMethod, NameValueCollection headers, Func inputStreamFunc) {
Requires.NotNullOrEmpty(httpMethod, "httpMethod");
Requires.NotNull(headers, "headers");
ContentType contentType = string.IsNullOrEmpty(headers[HttpRequestHeaders.ContentType]) ? null : new ContentType(headers[HttpRequestHeaders.ContentType]);
if (httpMethod == "POST" && contentType != null && string.Equals(contentType.MediaType, Channel.HttpFormUrlEncoded, StringComparison.Ordinal) && inputStreamFunc != null) {
var inputStream = inputStreamFunc();
var reader = new StreamReader(inputStream);
long originalPosition = 0;
if (inputStream.CanSeek) {
originalPosition = inputStream.Position;
}
string postEntity = reader.ReadToEnd();
if (inputStream.CanSeek) {
inputStream.Seek(originalPosition, SeekOrigin.Begin);
}
return HttpUtility.ParseQueryString(postEntity);
}
return new NameValueCollection();
}
///
/// Adds HTTP headers to a .
///
/// The collection to be modified with added entries.
/// The collection to read from.
private static void AddHeaders(NameValueCollection collectionToFill, HttpHeaders headers) {
Requires.NotNull(collectionToFill, "collectionToFill");
Requires.NotNull(headers, "headers");
foreach (var header in headers) {
foreach (var value in header.Value) {
collectionToFill.Add(header.Key, value);
}
}
}
}
}