diff --git a/src/Microsoft.OData.Core/MediaTypeUtils.cs b/src/Microsoft.OData.Core/MediaTypeUtils.cs
index a41524e842..bac04e885f 100644
--- a/src/Microsoft.OData.Core/MediaTypeUtils.cs
+++ b/src/Microsoft.OData.Core/MediaTypeUtils.cs
@@ -56,12 +56,12 @@ internal static class MediaTypeUtils
///
/// Max size to cache match info.
///
- private const int MatchInfoCacheMaxSize = 256;
+ private const int MatchInfoCacheInitialSize = 256;
///
/// Concurrent cache to cache match info.
///
- private static MatchInfoConcurrentCache MatchInfoCache = new MatchInfoConcurrentCache(MatchInfoCacheMaxSize);
+ private static MatchInfoConcurrentCache MatchInfoCache = new MatchInfoConcurrentCache(MatchInfoCacheInitialSize);
/// UTF-8 encoding, without the BOM preamble.
///
@@ -413,6 +413,15 @@ internal static ODataFormat GetFormatFromContentType(string contentTypeName, ODa
throw new ODataContentTypeException(Strings.MediaTypeUtils_CannotDetermineFormatFromContentType(str, contentTypeName));
}
+ ///
+ /// Internal method for testing membership of the cache
+ ///
+ /// The ContentTypes in the cache in the MediaInfoCache
+ internal static IEnumerable GetCacheKeys()
+ {
+ return MatchInfoCache.GetKeys();
+ }
+
///
/// Parses the specified content type header into a media type instance.
///
@@ -908,7 +917,7 @@ public MatchInfoCacheKey(ODataMediaTypeResolver resolver, ODataPayloadKind paylo
{
this.MediaTypeResolver = resolver;
this.PayloadKind = payloadKind;
- this.ContentTypeName = contentTypeName;
+ this.ContentTypeName = RemoveBoundary(contentTypeName);
}
///
@@ -924,7 +933,7 @@ public MatchInfoCacheKey(ODataMediaTypeResolver resolver, ODataPayloadKind paylo
///
/// Name of content type.
///
- private string ContentTypeName { get; set; }
+ internal string ContentTypeName { get; set; }
///
/// Returns a value indicating whether this instance is equal to a specified object.
@@ -967,6 +976,27 @@ public override int GetHashCode()
int result = this.MediaTypeResolver.GetHashCode() ^ this.PayloadKind.GetHashCode();
return this.ContentTypeName != null ? result ^ this.ContentTypeName.GetHashCode() : result;
}
+
+ ///
+ /// Multipart/mixed content types have a boundary that varies by request.
+ /// It should not be used as part of the format key.
+ ///
+ ///
+ ///
+ private static string RemoveBoundary(string contentTypeName)
+ {
+ if (contentTypeName.StartsWith("multipart", StringComparison.OrdinalIgnoreCase))
+ {
+ // our formatters don't care about parameters for multipart, so just strip them all out for simplicity
+ int parameter = contentTypeName.IndexOf(';');
+ if (parameter > 0)
+ {
+ return contentTypeName.Substring(0, parameter);
+ }
+ }
+
+ return contentTypeName;
+ }
}
///
@@ -982,10 +1012,10 @@ private sealed class MatchInfoConcurrentCache
///
/// Constructor.
///
- /// Max size of the elements that the cache can contain.
- public MatchInfoConcurrentCache(int maxSize)
+ /// Initial size of the cache.
+ public MatchInfoConcurrentCache(int initialSize)
{
- this.dict = new ConcurrentDictionary(4, maxSize);
+ this.dict = new ConcurrentDictionary(4, initialSize);
}
///
@@ -1018,6 +1048,15 @@ public void Add(MatchInfoCacheKey key, MediaTypeMatchInfo value)
this.dict.TryAdd(key, value);
}
}
+
+ ///
+ /// Internal method for validating expected elements in the cache
+ ///
+ /// ContentType of items in the cache
+ internal IEnumerable GetKeys()
+ {
+ return this.dict.Keys.Select(k => k.ContentTypeName);
+ }
}
}
}
diff --git a/test/FunctionalTests/Microsoft.OData.Core.Tests/MediaTypeUtilsTests.cs b/test/FunctionalTests/Microsoft.OData.Core.Tests/MediaTypeUtilsTests.cs
index 519f411b64..726eaf7170 100644
--- a/test/FunctionalTests/Microsoft.OData.Core.Tests/MediaTypeUtilsTests.cs
+++ b/test/FunctionalTests/Microsoft.OData.Core.Tests/MediaTypeUtilsTests.cs
@@ -434,6 +434,20 @@ public void AlterContentTypeForJsonPaddingIfNeededShouldFailIfAppJsonIsNotAtStar
Action target = () => MediaTypeUtils.AlterContentTypeForJsonPadding(original);
target.Throws(Strings.ODataMessageWriter_JsonPaddingOnInvalidContentType("tricky/application/json"));
}
+
+ [Fact]
+ public void MultipartBoundariesShouldNotGrowCache()
+ {
+ for (int i = 1; i < 100; i++)
+ {
+ ODataMediaType mediaType;
+ Encoding encoding;
+ ODataPayloadKind payloadKind;
+ MediaTypeUtils.GetFormatFromContentType(string.Format("multipart/mixed;boundary={0}", Guid.NewGuid()), new ODataPayloadKind[] { ODataPayloadKind.Batch }, ODataMediaTypeResolver.GetMediaTypeResolver(null), out mediaType, out encoding, out payloadKind);
+ }
+
+ Assert.True(MediaTypeUtils.GetCacheKeys().Count(k => k.StartsWith("multipart/mixed")) == 1, "Multiple multipart/mixed keys in cache");
+ }
}
internal class TestMediaTypeWithFormat