private static IEnumerable <KeyValuePair <string, object> > ParseETagValue(IList <ResourceProperty> etagProperties, string ifMatchHeaderValue) { bool flag; if (ifMatchHeaderValue == "*") { return(WebUtil.EmptyKeyValuePairStringObject); } string stringToUnescape = ifMatchHeaderValue.Substring("W/\"".Length, (ifMatchHeaderValue.Length - "W/\"".Length) - 1); KeyInstance instance = null; Exception innerException = null; try { flag = KeyInstance.TryParseNullableTokens(Uri.UnescapeDataString(stringToUnescape), out instance); } catch (DataServiceException exception2) { flag = false; innerException = exception2; } if (!flag) { throw DataServiceException.CreatePreConditionFailedError(System.Data.Services.Strings.Serializer_ETagValueDoesNotMatch, innerException); } if (instance.PositionalValues.Count != etagProperties.Count) { throw DataServiceException.CreatePreConditionFailedError(System.Data.Services.Strings.Serializer_ETagValueDoesNotMatch); } KeyValuePair <string, object>[] pairArray = new KeyValuePair <string, object> [etagProperties.Count]; for (int i = 0; i < pairArray.Length; i++) { ResourceProperty property = etagProperties[i]; object targetValue = null; string text = (string)instance.PositionalValues[i]; if (text != "null") { try { flag = WebConvert.TryKeyStringToPrimitive(text, property.Type, out targetValue); } catch (OverflowException exception3) { flag = false; innerException = exception3; } if (!flag) { throw DataServiceException.CreatePreConditionFailedError(System.Data.Services.Strings.Serializer_ETagValueDoesNotMatch, innerException); } } pairArray[i] = new KeyValuePair <string, object>(etagProperties[i].Name, targetValue); } return(pairArray); }
internal static string CompareAndGetETag(object parentEntityResource, object parentEntityToken, ResourceSetWrapper container, IDataService service, out bool writeResponseForGetMethods) { DataServiceHostWrapper host = service.OperationContext.Host; writeResponseForGetMethods = true; string str = null; if (parentEntityResource == null) { if (!string.IsNullOrEmpty(host.RequestIfMatch)) { throw DataServiceException.CreatePreConditionFailedError(System.Data.Services.Strings.Serializer_ETagValueDoesNotMatch); } return(str); } ResourceType nonPrimitiveResourceType = GetNonPrimitiveResourceType(service.Provider, parentEntityResource); ICollection <ResourceProperty> eTagProperties = service.Provider.GetETagProperties(container.Name, nonPrimitiveResourceType); if (eTagProperties.Count == 0) { if (!string.IsNullOrEmpty(host.RequestIfMatch)) { throw DataServiceException.CreateBadRequestError(System.Data.Services.Strings.Serializer_NoETagPropertiesForType); } } else if ((!string.IsNullOrEmpty(host.RequestIfMatch) || !string.IsNullOrEmpty(host.RequestIfNoneMatch)) && (host.RequestIfMatch != "*")) { if (host.RequestIfNoneMatch == "*") { writeResponseForGetMethods = false; } else { str = GetETagValue(parentEntityToken, nonPrimitiveResourceType, eTagProperties, service, true); if (string.IsNullOrEmpty(host.RequestIfMatch)) { if (host.RequestIfNoneMatch == str) { writeResponseForGetMethods = false; } } else if (str != host.RequestIfMatch) { throw DataServiceException.CreatePreConditionFailedError(System.Data.Services.Strings.Serializer_ETagValueDoesNotMatch); } } } if ((str == null) && (eTagProperties.Count != 0)) { str = GetETagValue(parentEntityResource, nonPrimitiveResourceType, eTagProperties, service, true); } return(str); }
internal void SetETagValues(object resourceCookie, ResourceSetWrapper container) { DataServiceHostWrapper host = this.service.OperationContext.Host; object obj2 = this.ResolveResource(resourceCookie); ResourceType nonPrimitiveResourceType = WebUtil.GetNonPrimitiveResourceType(this.service.Provider, obj2); IList <ResourceProperty> eTagProperties = this.service.Provider.GetETagProperties(container.Name, nonPrimitiveResourceType); if (eTagProperties.Count == 0) { if (!string.IsNullOrEmpty(host.RequestIfMatch)) { throw DataServiceException.CreateBadRequestError(System.Data.Services.Strings.Serializer_NoETagPropertiesForType); } } else { IDataServiceUpdateProvider updateProvider = this.updateProvider as IDataServiceUpdateProvider; if (updateProvider != null) { IEnumerable <KeyValuePair <string, object> > emptyKeyValuePairStringObject; bool?checkForEquality = null; if (!string.IsNullOrEmpty(host.RequestIfMatch)) { checkForEquality = true; emptyKeyValuePairStringObject = ParseETagValue(eTagProperties, host.RequestIfMatch); } else { emptyKeyValuePairStringObject = WebUtil.EmptyKeyValuePairStringObject; } updateProvider.SetConcurrencyValues(resourceCookie, checkForEquality, emptyKeyValuePairStringObject); } else { if (string.IsNullOrEmpty(host.RequestIfMatch)) { throw DataServiceException.CreateBadRequestError(System.Data.Services.Strings.DataService_CannotPerformOperationWithoutETag(nonPrimitiveResourceType.FullName)); } if ((host.RequestIfMatch != "*") && (WebUtil.GetETagValue(resourceCookie, nonPrimitiveResourceType, eTagProperties, this.service, false) != host.RequestIfMatch)) { throw DataServiceException.CreatePreConditionFailedError(System.Data.Services.Strings.Serializer_ETagValueDoesNotMatch); } } } }
/// <summary> /// Parse the given etag value in the If-Match request header. /// </summary> /// <param name="etagProperties">List of etag properties for the type whose etag values we are parsing.</param> /// <param name="ifMatchHeaderValue">value of the If-Match header as specified in the request.</param> /// <returns>returns the etag value as a list containing the property name and its corresponding value. If the If-Match header value is '*', then returns an empty collection.</returns> private static IEnumerable <KeyValuePair <string, object> > ParseETagValue(IList <ResourceProperty> etagProperties, string ifMatchHeaderValue) { Debug.Assert(etagProperties != null && etagProperties.Count != 0, "There must be atleast one etag property specified"); Debug.Assert(!String.IsNullOrEmpty(ifMatchHeaderValue), "IfMatch header cannot be null"); if (ifMatchHeaderValue == XmlConstants.HttpAnyETag) { // if the value is '*', then we return an empty IEnumerable. return(new KeyValuePair <string, object> [0]); } Debug.Assert(ifMatchHeaderValue.StartsWith(XmlConstants.HttpWeakETagPrefix, StringComparison.Ordinal), "If-Match header must be properly formatted - this check is done in DataService.CheckETagValues method"); Debug.Assert(ifMatchHeaderValue.Length >= XmlConstants.HttpWeakETagPrefix.Length + 1, "If-Match header must be properly formatted - this check is done in DataService.CheckETagValues method"); // Just get the etag value - we need to ignore the 'W/"' and the last '"' character from the etag string strippedETag = ifMatchHeaderValue.Substring(XmlConstants.HttpWeakETagPrefix.Length, ifMatchHeaderValue.Length - XmlConstants.HttpWeakETagPrefix.Length - 1); KeyInstance keyInstance = null; bool success; Exception innerException = null; // In V1, when we didn't have IConcurrencyProvider interface, we always used to compute the // latest etag from the entity instance we got from IUpdatable.GetResource and then comparing // it with the If-Match request header. Hence all invalid cases always used to throw // DataServiceException with 412, since the etags didn't match. // In V1.5, we have added the support for IConcurrencyProvider, which means we need to parse // the etag values and parse it to the provider, if it has implement this interface. To avoid // breaking changes, we need to catch all parsing errors and report them as 412 instead of 400 // to avoid it from becoming a breaking change. try { success = KeyInstance.TryParseNullableTokens(Uri.UnescapeDataString(strippedETag), out keyInstance); } catch (DataServiceException e) { success = false; innerException = e; } if (!success) { // We could have throwed BadRequest here since the etag value is not properly formattted. But since // we used to do throw 412 in V1, keeping it that way to avoid breaking change. throw DataServiceException.CreatePreConditionFailedError(Strings.Serializer_ETagValueDoesNotMatch, innerException); } if (keyInstance.PositionalValues.Count != etagProperties.Count) { // We could have throwed BadRequest here since the etag value is not properly formattted. But since // we used to do throw 412 in V1, keeping it that way to avoid breaking change. throw DataServiceException.CreatePreConditionFailedError(Strings.Serializer_ETagValueDoesNotMatch); } KeyValuePair <string, object>[] etagPropertyInfo = new KeyValuePair <string, object> [etagProperties.Count]; for (int i = 0; i < etagPropertyInfo.Length; i++) { ResourceProperty etagProperty = etagProperties[i]; object propertyValue = null; string value = (string)keyInstance.PositionalValues[i]; if (value != XmlConstants.NullLiteralInETag) { // The reason we need to catch the Overflow Exception here is because of the Bug #679728. // In V1, when we didn't have IConcurrencyProvider interface, we always used to compute the // latest etag from the entity instance we got from IUpdatable.GetResource and then comparing // it with the If-Match request header. Hence all invalid cases always used to throw // DataServiceException with 412, since the etags didn't match. // In V1.5, we have added the support for IConcurrencyProvider, which means we need to parse // the etag values and parse it to the provider, if it has implement this interface. To avoid // breaking changes, we need to catch all parsing errors and report them as 412 instead of 400 // to avoid it from becoming a breaking change. try { success = System.Data.Services.Parsing.WebConvert.TryKeyStringToPrimitive(value, etagProperty.Type, out propertyValue); } catch (OverflowException e) { success = false; innerException = e; } if (!success) { // We could have throwed BadRequest here since the etag value is not properly formattted. But since // we used to do throw 412 in V1, keeping it that way to avoid breaking change. throw DataServiceException.CreatePreConditionFailedError(Strings.Serializer_ETagValueDoesNotMatch, innerException); } } etagPropertyInfo[i] = new KeyValuePair <string, object>(etagProperties[i].Name, propertyValue); } return(etagPropertyInfo); }
/// <summary> /// If the provider implements IConcurrencyProvider, then this method passes the etag values /// to the provider, otherwise compares the etag itself. /// </summary> /// <param name="resourceCookie">etag values for the given resource.</param> /// <param name="container">container for the given resource.</param> internal void SetETagValues(object resourceCookie, ResourceSetWrapper container) { Debug.Assert(resourceCookie != null, "resourceCookie != null"); Debug.Assert(container != null, "container != null"); DataServiceHostWrapper host = this.service.OperationContext.Host; Debug.Assert(String.IsNullOrEmpty(host.RequestIfNoneMatch), "IfNoneMatch header cannot be specified for Update/Delete operations"); // Resolve the cookie first to the actual resource type object actualEntity = this.ResolveResource(resourceCookie); Debug.Assert(actualEntity != null, "actualEntity != null"); ResourceType resourceType = WebUtil.GetNonPrimitiveResourceType(this.service.Provider, actualEntity); Debug.Assert(resourceType != null, "resourceType != null"); IList <ResourceProperty> etagProperties = this.service.Provider.GetETagProperties(container.Name, resourceType); if (etagProperties.Count == 0) { if (!String.IsNullOrEmpty(host.RequestIfMatch)) { throw DataServiceException.CreateBadRequestError(Strings.Serializer_NoETagPropertiesForType); } // If the type has no etag properties, then we do not need to do any etag checks return; } // If the provider implements IConcurrencyProvider, then we need to call the provider // and pass the etag values. Else, we need to compare the etag values ourselves. IDataServiceUpdateProvider concurrencyProvider = this.updateProvider as IDataServiceUpdateProvider; if (concurrencyProvider != null) { bool?checkForEquality = null; IEnumerable <KeyValuePair <string, object> > etagValues = null; if (!String.IsNullOrEmpty(host.RequestIfMatch)) { checkForEquality = true; etagValues = ParseETagValue(etagProperties, host.RequestIfMatch); } else { etagValues = new KeyValuePair <string, object> [0]; } concurrencyProvider.SetConcurrencyValues(resourceCookie, checkForEquality, etagValues); } else if (String.IsNullOrEmpty(host.RequestIfMatch)) { throw DataServiceException.CreateBadRequestError(Strings.DataService_CannotPerformOperationWithoutETag(resourceType.FullName)); } else if (host.RequestIfMatch != XmlConstants.HttpAnyETag) { // Compare If-Match header value with the current etag value, if the If-Match header value is not equal to '*' string etagValue = WebUtil.GetETagValue(resourceCookie, resourceType, etagProperties, this.service, false /*getMethod*/); Debug.Assert(!String.IsNullOrEmpty(etagValue), "etag value can never be null"); if (etagValue != host.RequestIfMatch) { throw DataServiceException.CreatePreConditionFailedError(Strings.Serializer_ETagValueDoesNotMatch); } } }