You've already forked linux-packaging-mono
							
							
		
			
				
	
	
		
			457 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			457 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| //------------------------------------------------------------
 | |
| // Copyright (c) Microsoft Corporation.  All rights reserved.
 | |
| //------------------------------------------------------------
 | |
| #pragma warning disable 1634, 1691
 | |
| 
 | |
| namespace System.ServiceModel.Web
 | |
| {
 | |
|     using System;
 | |
|     using System.Collections.Generic;
 | |
|     using System.Collections.ObjectModel;
 | |
|     using System.Globalization;
 | |
|     using System.Net;
 | |
|     using System.Net.Mime;
 | |
|     using System.Runtime;
 | |
|     using System.ServiceModel;
 | |
|     using System.ServiceModel.Channels;
 | |
|     using System.ServiceModel.Dispatcher;
 | |
| 
 | |
|     public class IncomingWebRequestContext
 | |
|     {
 | |
|         static readonly string HttpGetMethod = "GET";
 | |
|         static readonly string HttpHeadMethod = "HEAD";
 | |
|         static readonly string HttpPutMethod = "PUT";
 | |
|         static readonly string HttpPostMethod = "POST";
 | |
|         static readonly string HttpDeleteMethod = "DELETE";
 | |
| 
 | |
|         Collection<ContentType> cachedAcceptHeaderElements;
 | |
|         string acceptHeaderWhenHeaderElementsCached;
 | |
|         internal const string UriTemplateMatchResultsPropertyName = "UriTemplateMatchResults";
 | |
|         OperationContext operationContext;
 | |
| 
 | |
|         internal IncomingWebRequestContext(OperationContext operationContext)
 | |
|         {
 | |
|             Fx.Assert(operationContext != null, "operationContext is null");
 | |
|             this.operationContext = operationContext;
 | |
|         }
 | |
| 
 | |
|         public string Accept
 | |
|         { 
 | |
|             get { return EnsureMessageProperty().Headers[HttpRequestHeader.Accept]; } 
 | |
|         }
 | |
| 
 | |
|         public long ContentLength
 | |
|         { 
 | |
|             get { return long.Parse(this.EnsureMessageProperty().Headers[HttpRequestHeader.ContentLength], CultureInfo.InvariantCulture); } 
 | |
|         }
 | |
| 
 | |
|         public string ContentType
 | |
|         { 
 | |
|             get { return this.EnsureMessageProperty().Headers[HttpRequestHeader.ContentType]; } 
 | |
|         }
 | |
| 
 | |
|         public IEnumerable<string> IfMatch
 | |
|         {
 | |
|             get 
 | |
|             { 
 | |
|                 string ifMatchHeader = MessageProperty.Headers[HttpRequestHeader.IfMatch];
 | |
|                 return (string.IsNullOrEmpty(ifMatchHeader)) ? null : Utility.QuoteAwareStringSplit(ifMatchHeader);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public IEnumerable<string> IfNoneMatch
 | |
|         {
 | |
|             get
 | |
|             {
 | |
|                 string ifNoneMatchHeader = MessageProperty.Headers[HttpRequestHeader.IfNoneMatch];
 | |
|                 return (string.IsNullOrEmpty(ifNoneMatchHeader)) ? null : Utility.QuoteAwareStringSplit(ifNoneMatchHeader);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public DateTime? IfModifiedSince
 | |
|         {
 | |
|             get 
 | |
|             { 
 | |
|                 string dateTime = this.MessageProperty.Headers[HttpRequestHeader.IfModifiedSince];
 | |
|                 if (!string.IsNullOrEmpty(dateTime))
 | |
|                 {
 | |
|                     DateTime parsedDateTime;
 | |
|                     if (HttpDateParse.ParseHttpDate(dateTime, out parsedDateTime))
 | |
|                     {
 | |
|                         return parsedDateTime;
 | |
|                     }
 | |
|                 }
 | |
|                 return null;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public DateTime? IfUnmodifiedSince
 | |
|         {
 | |
|             get 
 | |
|             { 
 | |
|                 string dateTime = this.MessageProperty.Headers[HttpRequestHeader.IfUnmodifiedSince];
 | |
|                 if (!string.IsNullOrEmpty(dateTime))
 | |
|                 {
 | |
|                     DateTime parsedDateTime;
 | |
|                     if (HttpDateParse.ParseHttpDate(dateTime, out parsedDateTime))
 | |
|                     {
 | |
|                         return parsedDateTime;
 | |
|                     }
 | |
|                 }
 | |
|                 return null;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public WebHeaderCollection Headers
 | |
|         { 
 | |
|             get { return this.EnsureMessageProperty().Headers; } 
 | |
|         }
 | |
| 
 | |
|         public string Method
 | |
|         { 
 | |
|             get { return this.EnsureMessageProperty().Method; } 
 | |
|         }
 | |
| 
 | |
|         public UriTemplateMatch UriTemplateMatch
 | |
|         {
 | |
|             get
 | |
|             {
 | |
|                 if (this.operationContext.IncomingMessageProperties.ContainsKey(UriTemplateMatchResultsPropertyName))
 | |
|                 {
 | |
|                     return this.operationContext.IncomingMessageProperties[UriTemplateMatchResultsPropertyName] as UriTemplateMatch;
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     return null;
 | |
|                 }
 | |
|             }
 | |
|             set
 | |
|             {
 | |
|                 this.operationContext.IncomingMessageProperties[UriTemplateMatchResultsPropertyName] = value;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public string UserAgent
 | |
|         { 
 | |
|             get { return this.EnsureMessageProperty().Headers[HttpRequestHeader.UserAgent]; } 
 | |
|         }
 | |
| 
 | |
|         HttpRequestMessageProperty MessageProperty
 | |
|         {
 | |
|             get
 | |
|             {
 | |
|                 if (operationContext.IncomingMessageProperties == null)
 | |
|                 {
 | |
|                     return null;
 | |
|                 }
 | |
|                 if (!operationContext.IncomingMessageProperties.ContainsKey(HttpRequestMessageProperty.Name))
 | |
|                 {
 | |
|                     return null;
 | |
|                 }
 | |
|                 return operationContext.IncomingMessageProperties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public void CheckConditionalRetrieve(string entityTag)
 | |
|         {
 | |
|             string validEtag = OutgoingWebResponseContext.GenerateValidEtagFromString(entityTag);
 | |
|             CheckConditionalRetrieveWithValidatedEtag(validEtag);
 | |
|         }
 | |
| 
 | |
|         public void CheckConditionalRetrieve(int entityTag)
 | |
|         {
 | |
|             string validEtag = OutgoingWebResponseContext.GenerateValidEtag(entityTag);
 | |
|             CheckConditionalRetrieveWithValidatedEtag(validEtag);
 | |
|         }
 | |
| 
 | |
|         public void CheckConditionalRetrieve(long entityTag)
 | |
|         {
 | |
|             string validEtag = OutgoingWebResponseContext.GenerateValidEtag(entityTag);
 | |
|             CheckConditionalRetrieveWithValidatedEtag(validEtag);
 | |
|         }
 | |
| 
 | |
|         public void CheckConditionalRetrieve(Guid entityTag)
 | |
|         {
 | |
|             string validEtag = OutgoingWebResponseContext.GenerateValidEtag(entityTag);
 | |
|             CheckConditionalRetrieveWithValidatedEtag(validEtag);
 | |
|         }
 | |
| 
 | |
|         public void CheckConditionalRetrieve(DateTime lastModified)
 | |
|         {
 | |
|             if (!string.Equals(this.Method, IncomingWebRequestContext.HttpGetMethod, StringComparison.OrdinalIgnoreCase) &&
 | |
|                 !string.Equals(this.Method, IncomingWebRequestContext.HttpHeadMethod, StringComparison.OrdinalIgnoreCase))
 | |
|             {
 | |
|                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
 | |
|                     SR2.GetString(SR2.ConditionalRetrieveGetAndHeadOnly, this.Method)));
 | |
|             }
 | |
| 
 | |
|             DateTime? ifModifiedSince = this.IfModifiedSince;
 | |
|             if (ifModifiedSince.HasValue)
 | |
|             {
 | |
|                 long ticksDifference = lastModified.ToUniversalTime().Ticks - ifModifiedSince.Value.ToUniversalTime().Ticks;
 | |
|                 if (ticksDifference < TimeSpan.TicksPerSecond)
 | |
|                 {
 | |
|                     WebOperationContext.Current.OutgoingResponse.LastModified = lastModified;
 | |
|                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new WebFaultException(HttpStatusCode.NotModified));
 | |
|                 }
 | |
|             }            
 | |
|         }
 | |
| 
 | |
|         public void CheckConditionalUpdate(string entityTag)
 | |
|         {
 | |
|             string validEtag = OutgoingWebResponseContext.GenerateValidEtagFromString(entityTag);
 | |
|             CheckConditionalUpdateWithValidatedEtag(validEtag);
 | |
|         }
 | |
| 
 | |
|         public void CheckConditionalUpdate(int entityTag)
 | |
|         {
 | |
|             string validEtag = OutgoingWebResponseContext.GenerateValidEtag(entityTag);
 | |
|             CheckConditionalUpdateWithValidatedEtag(validEtag);
 | |
|         }
 | |
| 
 | |
|         public void CheckConditionalUpdate(long entityTag)
 | |
|         {
 | |
|             string validEtag = OutgoingWebResponseContext.GenerateValidEtag(entityTag);
 | |
|             CheckConditionalUpdateWithValidatedEtag(validEtag);
 | |
|         }
 | |
| 
 | |
|         public void CheckConditionalUpdate(Guid entityTag)
 | |
|         {
 | |
|             string validEtag = OutgoingWebResponseContext.GenerateValidEtag(entityTag);
 | |
|             CheckConditionalUpdateWithValidatedEtag(validEtag);
 | |
|         }
 | |
| 
 | |
|         public Collection<ContentType> GetAcceptHeaderElements()
 | |
|         {
 | |
|             string acceptHeader = this.Accept;
 | |
|             if (cachedAcceptHeaderElements == null ||
 | |
|                 (!string.Equals(acceptHeaderWhenHeaderElementsCached, acceptHeader, StringComparison.OrdinalIgnoreCase)))
 | |
|             {
 | |
|                 if (string.IsNullOrEmpty(acceptHeader))
 | |
|                 {
 | |
|                     cachedAcceptHeaderElements = new Collection<ContentType>();
 | |
|                     acceptHeaderWhenHeaderElementsCached = acceptHeader;
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     List<ContentType> contentTypeList = new List<ContentType>();
 | |
|                     int offset = 0;
 | |
|                     while (true)
 | |
|                     {
 | |
|                         string nextItem = Utility.QuoteAwareSubString(acceptHeader, ref offset);
 | |
|                         if (nextItem == null)
 | |
|                         {
 | |
|                             break;
 | |
|                         }
 | |
| 
 | |
|                         ContentType contentType = Utility.GetContentTypeOrNull(nextItem);
 | |
|                         if (contentType != null)
 | |
|                         {
 | |
|                             contentTypeList.Add(contentType);
 | |
|                         }
 | |
|                     }
 | |
|                     
 | |
|                     contentTypeList.Sort(new AcceptHeaderElementComparer());
 | |
|                     cachedAcceptHeaderElements = new Collection<ContentType>(contentTypeList);
 | |
|                     acceptHeaderWhenHeaderElementsCached = acceptHeader;
 | |
|                 }
 | |
|             }
 | |
|             return cachedAcceptHeaderElements;
 | |
|         }
 | |
| 
 | |
|         HttpRequestMessageProperty EnsureMessageProperty()
 | |
|         {
 | |
|             if (this.MessageProperty == null)
 | |
|             {
 | |
|                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
 | |
|                     SR2.GetString(SR2.HttpContextNoIncomingMessageProperty, typeof(HttpRequestMessageProperty).Name)));
 | |
|             }
 | |
|             return this.MessageProperty;
 | |
|         }
 | |
| 
 | |
| 
 | |
|         void CheckConditionalRetrieveWithValidatedEtag(string entityTag)
 | |
|         {
 | |
|             if (!string.Equals(this.Method, IncomingWebRequestContext.HttpGetMethod, StringComparison.OrdinalIgnoreCase) &&
 | |
|                 !string.Equals(this.Method, IncomingWebRequestContext.HttpHeadMethod, StringComparison.OrdinalIgnoreCase))
 | |
|             {
 | |
|                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
 | |
|                     SR2.GetString(SR2.ConditionalRetrieveGetAndHeadOnly, this.Method)));
 | |
|             }
 | |
| 
 | |
|             if (!string.IsNullOrEmpty(entityTag))
 | |
|             {               
 | |
|                 string entityTagHeader = this.Headers[HttpRequestHeader.IfNoneMatch];
 | |
|                 if (!string.IsNullOrEmpty(entityTagHeader))
 | |
|                 {
 | |
|                     if (IsWildCardCharacter(entityTagHeader) ||
 | |
|                         DoesHeaderContainEtag(entityTagHeader, entityTag))
 | |
|                     {
 | |
|                         // set response entityTag directly because it has already been validated
 | |
|                         WebOperationContext.Current.OutgoingResponse.ETag = entityTag;
 | |
|                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new WebFaultException(HttpStatusCode.NotModified));
 | |
|                     }
 | |
|                 }               
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         void CheckConditionalUpdateWithValidatedEtag(string entityTag)
 | |
|         {
 | |
|             bool isPutMethod = string.Equals(this.Method, IncomingWebRequestContext.HttpPutMethod, StringComparison.OrdinalIgnoreCase);
 | |
|             if (!isPutMethod &&
 | |
|                 !string.Equals(this.Method, IncomingWebRequestContext.HttpPostMethod, StringComparison.OrdinalIgnoreCase) &&
 | |
|                 !string.Equals(this.Method, IncomingWebRequestContext.HttpDeleteMethod, StringComparison.OrdinalIgnoreCase))
 | |
|             {
 | |
|                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
 | |
|                     SR2.GetString(SR2.ConditionalUpdatePutPostAndDeleteOnly, this.Method)));
 | |
|             }
 | |
| 
 | |
|             string headerOfInterest;
 | |
| 
 | |
|             // if the current entityTag is null then the resource doesn't currently exist and the
 | |
|             //   a PUT request should only succeed if If-None-Match equals '*'.  
 | |
|             if (isPutMethod && string.IsNullOrEmpty(entityTag))
 | |
|             {
 | |
|                 headerOfInterest = this.Headers[HttpRequestHeader.IfNoneMatch];
 | |
|                 if (string.IsNullOrEmpty(headerOfInterest) ||
 | |
|                     !IsWildCardCharacter(headerOfInterest))
 | |
|                 {
 | |
|                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new WebFaultException(HttpStatusCode.PreconditionFailed));
 | |
|                 }
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 // all remaining cases are with an If-Match header
 | |
|                 headerOfInterest = this.Headers[HttpRequestHeader.IfMatch];
 | |
|                 if (string.IsNullOrEmpty(headerOfInterest) ||
 | |
|                     (!IsWildCardCharacter(headerOfInterest) &&
 | |
|                     !DoesHeaderContainEtag(headerOfInterest, entityTag)))
 | |
|                 {
 | |
|                     // set response entityTag directly because it has already been validated
 | |
|                     WebOperationContext.Current.OutgoingResponse.ETag = entityTag;
 | |
|                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new WebFaultException(HttpStatusCode.PreconditionFailed));
 | |
|                 }
 | |
|             }
 | |
|             
 | |
|         }
 | |
| 
 | |
|         static bool DoesHeaderContainEtag(string header, string entityTag)
 | |
|         {
 | |
|             int offset = 0;
 | |
|             while (true)
 | |
|             {
 | |
|                 string nextEntityTag = Utility.QuoteAwareSubString(header, ref offset);
 | |
|                 if (nextEntityTag == null)
 | |
|                 {
 | |
|                     break;
 | |
|                 }
 | |
|                 if (string.Equals(nextEntityTag, entityTag, StringComparison.Ordinal))
 | |
|                 {
 | |
|                     return true;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         static bool IsWildCardCharacter(string header)
 | |
|         {
 | |
|             return (header.Trim() == "*");
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     class AcceptHeaderElementComparer : IComparer<ContentType>
 | |
|     {
 | |
|         static NumberStyles numberStyles = NumberStyles.AllowDecimalPoint;
 | |
| 
 | |
|         public int Compare(ContentType x, ContentType y)
 | |
|         {
 | |
|             string[] xTypeSubType = x.MediaType.Split('/');
 | |
|             string[] yTypeSubType = y.MediaType.Split('/');
 | |
| 
 | |
|             Fx.Assert(xTypeSubType.Length == 2, "The creation of the ContentType would have failed if there wasn't a type and subtype.");
 | |
|             Fx.Assert(yTypeSubType.Length == 2, "The creation of the ContentType would have failed if there wasn't a type and subtype.");
 | |
| 
 | |
|             if (string.Equals(xTypeSubType[0], yTypeSubType[0], StringComparison.OrdinalIgnoreCase))
 | |
|             {
 | |
|                 if (string.Equals(xTypeSubType[1], yTypeSubType[1], StringComparison.OrdinalIgnoreCase))
 | |
|                 {
 | |
|                     // need to check the number of parameters to determine which is more specific
 | |
|                     bool xHasParam = HasParameters(x);
 | |
|                     bool yHasParam = HasParameters(y);
 | |
|                     if (xHasParam && !yHasParam)
 | |
|                     {
 | |
|                         return 1;
 | |
|                     }
 | |
|                     else if (!xHasParam && yHasParam)
 | |
|                     {
 | |
|                         return -1;
 | |
|                     }
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     if (xTypeSubType[1][0] == '*' && xTypeSubType[1].Length == 1)
 | |
|                     {
 | |
|                         return 1;
 | |
|                     }
 | |
|                     if (yTypeSubType[1][0] == '*' && yTypeSubType[1].Length == 1)
 | |
|                     {
 | |
|                         return -1;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             else if (xTypeSubType[0][0] == '*' && xTypeSubType[0].Length == 1)
 | |
|             {
 | |
|                 return 1;
 | |
|             }
 | |
|             else if (yTypeSubType[0][0] == '*' && yTypeSubType[0].Length == 1)
 | |
|             {
 | |
|                 return -1;
 | |
|             }
 | |
| 
 | |
|             decimal qualityDifference = GetQualityFactor(x) - GetQualityFactor(y);
 | |
|             if (qualityDifference < 0)
 | |
|             {
 | |
|                 return 1;
 | |
|             }
 | |
|             else if (qualityDifference > 0)
 | |
|             {
 | |
|                 return -1;
 | |
|             }
 | |
|             return 0;
 | |
|         }
 | |
| 
 | |
|         decimal GetQualityFactor(ContentType contentType)
 | |
|         {
 | |
|             decimal result;
 | |
|             foreach (string key in contentType.Parameters.Keys)
 | |
|             {
 | |
|                 if (string.Equals("q", key, StringComparison.OrdinalIgnoreCase))
 | |
|                 {
 | |
|                     if (decimal.TryParse(contentType.Parameters[key], numberStyles, CultureInfo.InvariantCulture, out result) &&
 | |
|                         (result <= (decimal)1.0))
 | |
|                     {
 | |
|                         return result;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             return (decimal)1.0;
 | |
|         }
 | |
| 
 | |
|         bool HasParameters(ContentType contentType)
 | |
|         {
 | |
|             int number = 0;
 | |
|             foreach (string param in contentType.Parameters.Keys)
 | |
|             {
 | |
|                 if (!string.Equals("q", param, StringComparison.OrdinalIgnoreCase))
 | |
|                 {
 | |
|                     number++;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             return (number > 0);
 | |
|         }
 | |
|     }
 | |
| }
 |